import test from 'node:test'; import assert from 'node:assert/strict'; import vm from 'node:vm'; import { readFileSync } from 'node:fs'; const gatewaySource = readFileSync(new URL('../deploy-admin-functions.mjs', import.meta.url), 'utf8'); const source = gatewaySource.match(/const LEGACY_LEARNING_PULLS=new Map\(\);[\s\S]*?\nconst APP_LIVE_SCOPES=/)?.[0] .replace(/\nconst APP_LIVE_SCOPES=[\s\S]*$/, ''); assert.ok(source, '未找到旧系统学习记录拉取实现'); function contextFor(rows) { let sequence = 0; let stored = []; const writes = []; const context = { console, URLSearchParams, AbortSignal, Map, Date, JSON, String, Math, DEFAULT_COMPANY_ID: 'company', number(value, fallback = 0) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : fallback; }, firstValue(...values) { return values.find((value) => value !== undefined && value !== null && value !== ''); }, parseArray(value) { try { const parsed = typeof value === 'string' ? JSON.parse(value) : value; return Array.isArray(parsed) ? parsed : []; } catch { return []; } }, randomObjectId() { sequence += 1; return `object${String(sequence).padStart(14, '0')}`; }, async appLegacyConfig() { return { url: 'https://legacy.example/api', apiId: 'id', apiKey: 'key' }; }, async fetch(url) { assert.match(String(url), /myfield2=UserId%3D42/); return { ok: true, async text() { return JSON.stringify({ retcode: 0, result: JSON.stringify(rows), page: { itemCount: rows.length, pageCount: 1 } }); } }; }, Psql: { async query(sql) { if (sql.includes('SELECT c."generalId"')) return stored; if (sql.includes('SELECT CAST("id" AS text)')) return stored.map((row) => ({ id: row.itemId })); throw new Error(`unexpected sql: ${sql}`); }, }, async mobileAtomicWrite(_input, _current, operations) { writes.push(operations); for (let index = 0; index < operations.length; index += 2) { const addon = operations[index].data; const common = operations[index + 1].data; stored = stored.filter((item) => Number(item.generalId) !== Number(common.generalId)); stored.push({ ...common, ...addon, itemId: addon.id }); } return operations.map((operation) => operation.data); }, }; vm.runInNewContext(`${source};this.normalize=legacyLearningRecord;this.signature=legacyLearningSignature;this.sync=performLegacyLearningSync`, context); return { context, writes }; } test('旧系统学习记录按学员过滤,并规范化北京时间与单词明细', () => { const { context } = contextFor([]); assert.equal(context.normalize({ GeneralID: 1, UserID: 7 }, 42), null); const row = context.normalize({ GeneralID: 9001, UserID: 42, CreateTime: '2026-09-25 08:10:00', Title: '灵灵', dqrq: '2026-09-25', xxqs: '[ { "GeneralID": 1 } ]', learned: 1, }, 42); assert.equal(row.generalId, 9001); assert.equal(row.createdAt, '2026-09-25T00:10:00.000Z'); assert.equal(row.addon.dqrq, '20260925'); assert.equal(row.addon.xxqs, '[{"GeneralID":1}]'); }); test('旧系统学习记录只在变化时原子写入,重复轮询不制造版本变化', async () => { const rows = [{ GeneralID: 9001, UserID: 42, CreateTime: '2026-09-25 08:10:00', Title: '灵灵', Inputer: '老师', dqrq: '20260925', xxqs: '[{"GeneralID":1}]', learned: 1, }]; const { context, writes } = contextFor(rows); const first = await context.sync(42, { id: 'actor' }); const second = await context.sync(42, { id: 'actor' }); assert.equal(first.pulled, 1); assert.equal(first.written, 1); assert.equal(second.written, 0); assert.equal(writes.length, 1); assert.equal(writes[0].length, 2); assert.equal(writes[0][0].match.id, 9001); assert.equal(writes[0][1].match.generalId, 9001); }); test('移动网关实时版本逻辑不引用运营网关的局部工具函数', () => { const liveSource = gatewaySource.match(/const APP_LIVE_SCOPES=[\s\S]*?async function appLearningOverview/)?.[0]; assert.ok(liveSource, '未找到移动网关实时版本实现'); assert.doesNotMatch(liveSource, /\btext\(/); assert.doesNotMatch(liveSource, /\bdateIso\(/); assert.doesNotMatch(liveSource, /\bsyncConfig\(/); });