|
|
@@ -0,0 +1,95 @@
|
|
|
+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);
|
|
|
+});
|