mobile-review-performance.test.mjs 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637
  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 mobile=readFileSync(new URL('../cloud/mobile-teaching.js',import.meta.url),'utf8');
  7. const baseline=readFileSync(new URL('./fixtures/mobile-review-performance-baseline.js',import.meta.url),'utf8');
  8. const plain=v=>JSON.parse(JSON.stringify(v));
  9. test('indexed correlated review reads preserve every page, summary, reward, date and identity-format result',async()=>{
  10. const db=new PGlite();try{
  11. await db.exec(readFileSync(new URL('./fixtures/teacher-review-schema.sql',import.meta.url),'utf8'));
  12. await db.exec(`INSERT INTO "MemoryPracticeRecord" VALUES
  13. ('scaled','co',101,30.0,20.0,101,1,'1','2026-10-14','2026-10-14'),
  14. ('foreign','other',102,30,20,101,1,'1','2026-10-14','2026-10-14'),
  15. ('missing-model','co',103,30,20,102,1,'0','2026-10-14','2026-10-14'),
  16. ('empty-source','co',104,30,20,103,1,'0','2026-10-14','2026-10-14');
  17. INSERT INTO "CommonModel"(company,"modelId","generalId","itemId") VALUES('co',60,1101,101),('other',60,1102,102),('co',60,1104,104),('co',56,103,203);
  18. INSERT INTO "DailyStudyRecord"(company,id,learned,xxqs,"userId") VALUES('co',203,0,'[]',20);
  19. INSERT INTO "ReviewRewardIncome"(company,"taskId",amount,status,"originalAmount","correctionAmount","correctionReason") VALUES('co','r1',0,'ineligible',1,-1,'逾期'),('co','r2',1,'earned',1,NULL,NULL);`);
  20. const query=async(sql,args)=>(await db.query(sql,args)).rows;
  21. function context(old,coach){const ctx=vm.createContext({Date,Promise,DEFAULT_COMPANY_ID:'co',number:(v,f=0)=>Number(v)||f,authorizeRequestedUser:async()=>coach?30:20,appReviewTarget:async()=>20,appOverviewToday:()=> '2026-10-14',legacyAliases:r=>r,appLiveVersionSnapshot:async()=>({}),syncLegacyReviews:async()=>({state:'current'}),Psql:{query,one:async(s,a)=>(await query(s,a))[0]}});vm.runInContext(mobile+(old?'\n'+baseline:''),ctx);ctx.mobileSource=()=>({source:'database',refreshedAt:'fixed',complete:true});return ctx;}
  22. const cases=[{}, {scope:'today'}, {scope:'today',page:2,pageSize:1}, {scope:'overdue'}, {status:'completed'}, {status:'pending',month:'2026-10'}, {date:'2026-10-20',pageSize:100}, {studentId:20,includeMetadata:false,includeIncome:false}];
  23. for(const coach of [true,false])for(const input of cases){const current={get:()=>coach?3:1},a=context(true,coach),b=context(false,coach);assert.deepEqual(plain(await b.appReviewPage(input,current)),plain(await a.appReviewPage(input,current)),JSON.stringify({coach,input}));}
  24. }finally{await db.close();}
  25. });
  26. test('review version uses ownership indexes without changing numeric text identity or version data',async()=>{
  27. const db=new PGlite();try{
  28. await db.exec(`CREATE TABLE "MemoryPracticeRecord"(company text,yhid numeric,plid numeric,"updatedAt" timestamptz,"createdAt" timestamptz);
  29. INSERT INTO "MemoryPracticeRecord" VALUES('co',20,30,'2026-11-10','2026-11-01'),('co',20.0,30.0,'2026-11-11','2026-11-01'),('other',20,30,'2026-11-12','2026-11-01'),('co',NULL,20,NULL,'2026-11-09');`);
  30. const gateway=readFileSync(new URL('../deploy-admin-functions.mjs',import.meta.url),'utf8');
  31. const current=gateway.slice(gateway.indexOf('async function appLiveScopeVersion('),gateway.indexOf('async function appLiveSyncStatus('));
  32. const context=code=>{const c=vm.createContext({DEFAULT_COMPANY_ID:'co',appLiveVersion:r=>r,Psql:{one:async(s,a)=>(await db.query(s,a)).rows[0]}});vm.runInContext(code,c);return c;};
  33. const before=context(baseline),after=context(current);
  34. for(const uid of [20,30,0,999])assert.deepEqual(plain(await after.appLiveScopeVersion('review',uid,{})),plain(await before.appLiveScopeVersion('review',uid,{})));
  35. }finally{await db.close();}
  36. });