exam-history.test.mjs 4.0 KB

12345678910111213141516171819202122232425262728
  1. import {test} from 'node:test';
  2. import assert from 'node:assert/strict';
  3. import {readFileSync} from 'node:fs';
  4. import vm from 'node:vm';
  5. import {PGlite} from '@electric-sql/pglite';
  6. const code=readFileSync(new URL('../cloud/exam-history.js',import.meta.url),'utf8');
  7. test('考试历史恢复原答卷、摘要去重、分页并按当前账号及帐套隔离',async()=>{
  8. const db=new PGlite();try{
  9. await db.exec(`CREATE TABLE "DesignAnswer" ("objectId" text,id numeric,"askId" numeric,"userId" numeric,company text,cdate timestamptz,"createdAt" timestamptz,answer text);
  10. CREATE TABLE "AssessmentProfile" ("objectId" text,answerid numeric,askid numeric,"userId" numeric,company text,"createdAt" timestamptz,"updatedAt" timestamptz,df numeric,"totalScore" numeric,wrong numeric,"dontKnow" numeric);`);
  11. const answers=JSON.stringify([{result:1},{result:0},{result:2}]);
  12. for(let i=1;i<=22;i++)await db.query('INSERT INTO "DesignAnswer" VALUES($1,$2,1,100,$3,$4,$4,$5)',['a'+i,i,i%2?'co':'Company$co',new Date(Date.UTC(2026,9,i)).toISOString(),answers]);
  13. await db.query(`INSERT INTO "DesignAnswer" VALUES('other',100,1,200,'co',now(),now(),$1),('foreign',101,1,100,'foreign',now(),now(),$1),('unrelated',102,3,100,'co',now(),now(),$1)`,[answers]);
  14. await db.exec(`INSERT INTO "AssessmentProfile" VALUES('old-summary',22,1,100,'co',now(),now(),1,3,1,1),('summary-only',999,2,100,'co','2026-01-01','2026-01-01',2,3,0,1)`);
  15. const ctx={DEFAULT_COMPANY_ID:'co',assertLessonTenant:()=>{},ownLegacyId:()=>100,number:(n,d=0)=>Number.isFinite(Number(n))?Number(n):d,fail:(_c,m)=>{throw Error(m)},Psql:{query:async(s,p)=>(await db.query(s,p)).rows}};vm.createContext(ctx);vm.runInContext(code,ctx);
  16. const first=await ctx.appExamHistory({page:1,uid:200},{}),second=await ctx.appExamHistory({page:2},{}),last=await ctx.appExamHistory({page:3},{});
  17. assert.equal(first.items.length,10);assert.equal(first.items[0].GeneralID,'answer:22');assert.equal(first.hasMore,true);assert.equal(second.items.length,10);assert.equal(last.items.length,3);assert.equal(last.hasMore,false);
  18. const all=[...first.items,...second.items,...last.items];assert.equal(new Set(all.map(r=>r.GeneralID)).size,23);assert.ok(all.every(r=>r.resultAvailable));assert.equal(first.items[0].df,1);assert.equal(last.items[2].df,2);
  19. assert.equal(first.items[0].wrong,1);assert.equal(first.items[0].dontKnow,1);assert.ok(all.every(r=>!Object.hasOwn(r,'answer')));
  20. assert.equal(ctx.examHistoryItem({key:'bad',data:{answer:'broken'}}).resultAvailable,false);
  21. }finally{await db.close();}
  22. });
  23. test('旧客户端更新考试成绩改为新增,保留原归属校验和请求幂等编号',async()=>{
  24. const src=readFileSync(new URL('../deploy-admin-functions.mjs',import.meta.url),'utf8');const fragment=src.slice(src.indexOf('async function updateContentPair('),src.indexOf('async function createContentPair('));let appended,authorized=false;
  25. const ctx={DEFAULT_COMPANY_ID:'co',parseObject:s=>JSON.parse(s),number:Number,resolveContent:async()=>({object:{get:k=>({modelId:61,itemId:10}[k])}}),CONTENT_ADDONS:{61:{className:'AssessmentProfile',fields:['df','askid','userId','answerid','wrong','dontKnow','totalScore']}},CONTENT_WRITE_AUTH:{61:{subjects:['userId'],actors:[]}},Psql:{query:async()=>[{objectId:'old'}]},Parse:{Query:class{async get(){return {id:'old'};}}},safe:()=>({userId:100,df:8,askid:1,answerid:5}),authorizeBusinessRow:async()=>{if(!authorized)throw Error('Forbidden')},createContentPair:async input=>{appended=input;return 'new-record'},fail:(_c,m)=>{throw Error(m)}};
  26. vm.createContext(ctx);vm.runInContext(fragment,ctx);const input={requestKey:'same-attempt-key',content:JSON.stringify({GeneralID:1}),addon:JSON.stringify({UserID:100,df:12,answerid:6})};
  27. await assert.rejects(ctx.updateContentPair(input,{}),/Forbidden/);assert.equal(appended,undefined);authorized=true;assert.equal(await ctx.updateContentPair(input,{}),'new-record');assert.equal(appended.requestKey,input.requestKey);assert.equal(JSON.parse(appended.content).ModelID,61);assert.equal(JSON.parse(appended.addon).df,12);assert.equal(JSON.parse(appended.addon).userId,100);
  28. });