|
|
@@ -0,0 +1,61 @@
|
|
|
+import test from 'node:test';
|
|
|
+import assert from 'node:assert/strict';
|
|
|
+import vm from 'node:vm';
|
|
|
+import { readFileSync } from 'node:fs';
|
|
|
+import { PGlite } from '@electric-sql/pglite';
|
|
|
+
|
|
|
+const extension = readFileSync(new URL('../cloud/mobile-teaching.js', import.meta.url), 'utf8');
|
|
|
+const gateway = readFileSync(new URL('../deploy-admin-functions.mjs', import.meta.url), 'utf8');
|
|
|
+
|
|
|
+test('学生已分配词库保留待分类和缺少通用记录的历史开通,排除回收、停用及其他学员', async () => {
|
|
|
+ const db = new PGlite();
|
|
|
+ try {
|
|
|
+ await db.exec(`CREATE TABLE "CourseBinding"("objectId" text,company text,id bigint,yhid numeric,kcid text,"enrollmentStatus" text,syjd text,yxx numeric);
|
|
|
+ CREATE TABLE "CommonModel"(company text,"modelId" int,"itemId" bigint,"generalId" bigint,title text,"createTime" timestamptz,status int);
|
|
|
+ CREATE TABLE "Node"(company text,"nodeId" numeric,"nodeName" text,zstatus int);
|
|
|
+ INSERT INTO "Node" VALUES ('co',81,'历史词库',99),('co',82,'停用词库',0),('other',81,'其他帐套',99);`);
|
|
|
+ const states = ['active', 'pending', null, '', 'recycled', 'disabled', 'active', 'pending', 'pending', 'pending', 'pending'];
|
|
|
+ for (const [i,state] of states.entries()) {
|
|
|
+ const id = i + 1;
|
|
|
+ await db.query('INSERT INTO "CourseBinding" VALUES($1,$2,$3,$4,$5,$6,$7,$8)', [`b${id}`,id===11?'other':'co',id,id===10?43:42,id===9?'82':id===2?'81.0':'81',state,'{"91":"91@已分配单元"}',12]);
|
|
|
+ if (id !== 8) await db.query('INSERT INTO "CommonModel" VALUES($1,58,$2,$2,$3,now(),$4)', [id===11?'other':'co',id,'已开通词库',id===7?-2:99]);
|
|
|
+ }
|
|
|
+ // An explicitly unpublished common record must remain unavailable.
|
|
|
+ await db.exec(`INSERT INTO "CourseBinding" VALUES('draft','co',12,42,'81','pending','',0); INSERT INTO "CommonModel" VALUES('co',58,12,12,'未发布',now(),0);`);
|
|
|
+ const context = {
|
|
|
+ DEFAULT_COMPANY_ID: 'co',
|
|
|
+ Psql: { query: async (sql,args) => (await db.query(sql,args)).rows },
|
|
|
+ number: value => Number(value) || 0,
|
|
|
+ ownLegacyId: current => Number(current.get('legacyUserId')),
|
|
|
+ };
|
|
|
+ vm.runInNewContext(extension,context);
|
|
|
+ const courses = await context.mobileReadableCourseBindings(42);
|
|
|
+ assert.deepEqual(courses.map(row=>row.bindingObjectId),['b8','b4','b3','b2','b1']);
|
|
|
+ assert.ok(courses.every(row=>Number(row.yxx)===12 && row.syjd.includes('已分配单元')));
|
|
|
+ assert.equal(courses.find(row=>row.bindingObjectId==='b2').courseTitle,'历史词库');
|
|
|
+
|
|
|
+ const student = {get:key=>({legacyGroupId:1,legacyUserId:42})[key]};
|
|
|
+ const coach = {get:key=>({legacyGroupId:3,legacyUserId:99})[key]};
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b2',81,student)).length,1);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'8',81,student)).length,1);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b7',81,student)).length,0);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b5',81,student)).length,0);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b9',82,student)).length,0);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b10',81,student)).length,0);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b2',81,coach)).length,0);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(42,'b1',81,coach)).length,1);
|
|
|
+ assert.equal((await context.mobileUnitBindingRows(43,'b2',81,student)).length,0);
|
|
|
+ const retained = (await db.query('SELECT "enrollmentStatus",yxx FROM "CourseBinding" WHERE "objectId"=\'b2\'' )).rows[0];
|
|
|
+ assert.equal(retained.enrollmentStatus,'pending');
|
|
|
+ assert.equal(Number(retained.yxx),12);
|
|
|
+ } finally { await db.close(); }
|
|
|
+});
|
|
|
+
|
|
|
+test('伴学中心、学习记录和单元入口采用统一的词库读取规则', () => {
|
|
|
+ const studentOverview = gateway.slice(gateway.indexOf('async function appStudentCompanionOverview'),gateway.indexOf('async function appCompanionOverview'));
|
|
|
+ const learningOverview = gateway.slice(gateway.indexOf('async function appLearningOverview'),gateway.indexOf('async function appStudentCompanionOverview'));
|
|
|
+ const unitPage = gateway.slice(gateway.indexOf('async function nodeUnitPage'),gateway.indexOf('function legacyAppRow'));
|
|
|
+ assert.match(studentOverview,/courses=await mobileReadableCourseBindings\(uid\)/);
|
|
|
+ assert.match(learningOverview,/mobileReadableCourseBindings\(targetId\)/);
|
|
|
+ assert.match(unitPage,/mobileUnitBindingRows\(uid,bindingId,parentId,current\)/);
|
|
|
+});
|