| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import { test } from 'node:test';
- import assert from 'node:assert/strict';
- import { readFileSync } from 'node:fs';
- import { PGlite } from '@electric-sql/pglite';
- import vm from 'node:vm';
- const migration = new URL('../sql/legacy-enrollment-reconciliation.sql', import.meta.url);
- const sql = readFileSync(migration, 'utf8');
- const gateway = readFileSync(new URL('../cloud/member-enrollment.js', import.meta.url), 'utf8');
- async function fixture(run) {
- const db = new PGlite();
- try {
- await db.exec(`
- CREATE TABLE "_User" (company text,"legacyUserId" numeric,"identityType" text,"isDisabled" boolean,"isDeleted" boolean,"legacyUserData" jsonb);
- CREATE TABLE "Node" (company text,"nodeId" numeric,zstatus numeric,"nodeName" text);
- CREATE TABLE "CourseTeachingProfile" (company text,"courseId" numeric,"categoryKey" text,confirmed boolean);
- CREATE TABLE "CommonModel" (company text,"modelId" numeric,"itemId" numeric,status numeric,"objectId" text,"sourceKey" text);
- CREATE TABLE "CourseBinding" (company text,"objectId" text PRIMARY KEY,id numeric,yhid text,kcid text,"categoryKey" text,"categoryName" text,"enrollmentStatus" text,"sourceKey" text,yxx numeric,cksl numeric,syjd text,"updatedAt" timestamptz);
- CREATE UNIQUE INDEX enrollment_active_identity ON "CourseBinding" (company,yhid,"categoryKey",kcid) WHERE "enrollmentStatus"='active';
- INSERT INTO "_User" VALUES ('co',20,'member',false,false,'{"Purse":10}');
- INSERT INTO "Node" VALUES ('co',100,99,'词表');
- INSERT INTO "CourseTeachingProfile" VALUES ('co',100,'word',true);
- `);
- const binding = async (id, status = 'pending', category = 'word', source = `legacy:model:58:general:${id}`) => db.query(
- `INSERT INTO "CourseBinding" VALUES ('co',$1,$2,'20','100',$3,'单词课',$4,$5,123,500,'{"101":"101@第一本@1"}',now())`,
- [String(id),id,category,status,source]);
- const common = async (id, status = 99) => db.query(`INSERT INTO "CommonModel" VALUES ('co',58,$1,$2,$3,$4)`, [id,status,'c'+id,`legacy:model:58:general:${id}`]);
- const get = async id => (await db.query('SELECT * FROM "CourseBinding" WHERE "objectId"=$1',[String(id)])).rows[0];
- const install = () => db.exec(sql);
- await run({db,binding,common,get,install});
- } finally { await db.close(); }
- }
- test('历史待确认开课恢复后进入真实排课查询,保留编号、进度和余额,重复迁移无变化', () => fixture(async ({db,binding,common,get,install}) => {
- await binding(1); await common(1); const before = await get(1);
- await install();
- const after = await get(1);
- assert.equal(after.enrollmentStatus, 'active');
- for (const key of ['objectId','id','sourceKey','yxx','cksl','syjd']) assert.equal(after[key],before[key]);
- const c = { Psql: { query: async (s,p) => (await db.query(s,p)).rows }, companyIdOf: () => 'co', text: x => String(x ?? ''), number: Number, TEACHING_CATEGORY_LABELS: new Map([['word','单词课']]) };
- vm.createContext(c); vm.runInContext(gateway,c);
- const courses = await c.enrollmentBindings({}, {},20);
- assert.equal(courses.length,1); assert.equal(courses[0].bindingId,'1');
- await install(); assert.deepEqual(await get(1),after);
- assert.deepEqual((await db.query('SELECT "legacyUserData" FROM "_User"')).rows[0].legacyUserData,{Purse:10});
- }));
- for (const order of ['binding-first','common-first']) test(`未来同步两张表按${order}顺序写入时自动激活`, () => fixture(async ({binding,common,get,install}) => {
- await install();
- if(order==='binding-first'){await binding(1,null,null);assert.equal((await get(1)).enrollmentStatus,null);await common(1);}
- else {await common(1);await binding(1,null,null);}
- assert.equal((await get(1)).enrollmentStatus,'active'); assert.equal((await get(1)).categoryKey,'word');
- }));
- test('停用学生保留原状,重新启用后无需删除重开;恢复删除的学生同样生效', () => fixture(async ({db,binding,common,get,install}) => {
- await db.exec('UPDATE "_User" SET "isDisabled"=true,"isDeleted"=true');
- await binding(1);await common(1);await install();
- assert.equal((await get(1)).enrollmentStatus,'pending');
- await db.exec('UPDATE "_User" SET "isDisabled"=false');assert.equal((await get(1)).enrollmentStatus,'pending');
- await db.exec('UPDATE "_User" SET "isDeleted"=false');assert.equal((await get(1)).enrollmentStatus,'active');
- }));
- test('后续补入学生资料、确认映射和恢复内容时补齐开课', () => fixture(async ({db,binding,common,get,install}) => {
- await db.exec('DELETE FROM "_User";UPDATE "CourseTeachingProfile" SET confirmed=false;UPDATE "Node" SET zstatus=0');
- await binding(1,null,null);await common(1);await install();
- await db.exec(`INSERT INTO "_User" VALUES ('co',20,'member',false,false,'{}');UPDATE "CourseTeachingProfile" SET confirmed=true`);
- assert.equal((await get(1)).enrollmentStatus,null);
- await db.exec('UPDATE "Node" SET zstatus=99');assert.equal((await get(1)).enrollmentStatus,'active');
- }));
- test('重复历史开课、已存在有效开课、回收记录及新系统记录保持原状', () => fixture(async ({db,binding,common,get,install}) => {
- await binding(1);await common(1);await binding(2);await common(2);await install();
- assert.equal((await get(1)).enrollmentStatus,'pending');assert.equal((await get(2)).enrollmentStatus,'pending');
- await db.exec(`UPDATE "CourseBinding" SET "enrollmentStatus"='active' WHERE "objectId"='2'`);
- assert.equal((await get(1)).enrollmentStatus,'pending');
- await binding(3,'recycled');await common(3,-2);assert.equal((await get(3)).enrollmentStatus,'recycled');
- await db.exec('DELETE FROM "CourseBinding";DELETE FROM "CommonModel"');
- await binding(4,'pending','word','enroll:4');await common(4);assert.equal((await get(4)).enrollmentStatus,'pending');
- }));
- for(const kind of ['unconfirmed','conflicting-profile','explicit-other-category','deleted-common','foreign-tenant']) test(`${kind}不自动授权`, () => fixture(async ({db,binding,common,get,install}) => {
- if(kind==='unconfirmed')await db.exec('UPDATE "CourseTeachingProfile" SET confirmed=false');
- if(kind==='conflicting-profile')await db.exec(`INSERT INTO "CourseTeachingProfile" VALUES ('co',100,'trial',true)`);
- if(kind==='foreign-tenant')await db.exec(`UPDATE "CourseTeachingProfile" SET company='other'`);
- await binding(1,'pending',kind==='explicit-other-category'?'trial':null);await common(1,kind==='deleted-common'?-2:99);await install();
- assert.equal((await get(1)).enrollmentStatus,'pending');
- }));
|