course-word-progress.test.mjs 4.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  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 source=readFileSync(new URL('../deploy-admin-functions.mjs',import.meta.url),'utf8');
  7. const gateway=source.match(/const appGatewayCode = String.raw`([\s\S]*?)\n`;/)[1];
  8. async function setup(t){
  9. const db=new PGlite();t.after(()=>db.close());
  10. await db.exec(`
  11. CREATE TABLE "Node" (company text,"nodeId" int,"parentId" int);
  12. CREATE TABLE "CommonModel" (company text,"modelId" int,"generalId" int,"itemId" int,"nodeId" int,"orderId" int,title text);
  13. CREATE TABLE "VocabularyWord" (company text,id int,sy text);
  14. CREATE TABLE "PracticeRecord" (company text,yhid text,scid text,xxcs text,jrscb text);
  15. INSERT INTO "Node" VALUES ('7pIbDBJmKx',100,0),('7pIbDBJmKx',110,100),('7pIbDBJmKx',120,100),('other',130,100);
  16. INSERT INTO "CommonModel" VALUES
  17. ('7pIbDBJmKx',52,1,1,110,1,'one'),('7pIbDBJmKx',52,2,2,110,2,'two'),
  18. ('7pIbDBJmKx',52,3,3,110,3,'three'),('7pIbDBJmKx',52,4,4,110,4,'four'),
  19. ('7pIbDBJmKx',52,5,5,120,5,'other-unit'),('other',52,6,6,130,6,'foreign');
  20. INSERT INTO "VocabularyWord" VALUES ('7pIbDBJmKx',2,'第二个词');
  21. INSERT INTO "PracticeRecord" VALUES
  22. ('7pIbDBJmKx','42','2','1','0'),('7pIbDBJmKx','42','2','0','0'),
  23. ('7pIbDBJmKx','42','3','2.0','0'),('7pIbDBJmKx','42','4','7','0'),
  24. ('7pIbDBJmKx','99','1','3','0'),('other','42','1','3','0');
  25. `);
  26. const context=vm.createContext({Psql:{one:async(s,a)=>(await db.query(s,a)).rows[0],query:async(s,a)=>(await db.query(s,a)).rows}});
  27. vm.runInContext(gateway,context);
  28. context.authorizeRequestedUser=async(_,uid)=>{if(uid!==42)throw Error('无权访问');return uid;};
  29. return {db,read:input=>context.courseWordPage({uid:42,nids:'110',pnid:100,...input},{})};
  30. }
  31. test('四栏按最大有效学习次数分类,旧记录超过三遍归入第三遍,不重复计算',async t=>{
  32. const {read}=await setup(t);
  33. for(let pass=0;pass<4;pass++){
  34. const result=await read({learningPass:pass,psize:1});
  35. assert.equal(result.rows.length,1);assert.equal(result.rows[0].w_learned,pass);
  36. assert.equal(result.rows[0].GeneralID,pass+1);
  37. assert.deepEqual(Array.from(result.page.passCounts),[1,1,1,1]);
  38. assert.deepEqual(Array.from(result.page.bookPassCounts),[2,1,1,1]);
  39. assert.equal(result.page.firstPassProgress,60);
  40. }
  41. const result=await read({learningPass:1});assert.equal(result.rows[0].detail.sy,'第二个词');
  42. });
  43. test('分页后仍保留整本计数,其他单元未学完不会被误判为首遍完成',async t=>{
  44. const {read,db}=await setup(t);
  45. await db.exec(`UPDATE "PracticeRecord" SET xxcs='1' WHERE company='7pIbDBJmKx' AND yhid='42';INSERT INTO "PracticeRecord" VALUES('7pIbDBJmKx','42','1','1','0');`);
  46. const selected=await read({learningPass:1,psize:2,cpage:2});
  47. assert.equal(selected.rows.length,2);assert.equal(selected.page.itemCount,4);
  48. assert.deepEqual(Array.from(selected.page.bookPassCounts),[1,4,0,0]);assert.equal(selected.page.firstPassProgress,80);
  49. const empty=await read({learningPass:1,psize:2,cpage:3});assert.equal(empty.rows.length,0);assert.equal(empty.page.itemCount,4);
  50. });
  51. test('首遍全完成后进度始终100%,第二第三遍不重置;兼容旧客户端混合列表',async t=>{
  52. const {read,db}=await setup(t);
  53. await db.exec(`INSERT INTO "PracticeRecord" VALUES ('7pIbDBJmKx','42','1','1','0'),('7pIbDBJmKx','42','5','1','0');`);
  54. assert.equal((await read({})).page.firstPassProgress,100);
  55. await db.exec(`UPDATE "PracticeRecord" SET xxcs='2' WHERE company='7pIbDBJmKx' AND yhid='42';`);
  56. assert.equal((await read({})).page.firstPassProgress,100);
  57. await db.exec(`UPDATE "PracticeRecord" SET xxcs='3' WHERE company='7pIbDBJmKx' AND yhid='42';`);
  58. const done=await read({});assert.equal(done.page.firstPassProgress,100);assert.equal(done.rows.length,4);assert.equal(done.page.passCounts[3],4);
  59. });
  60. test('拒绝无效遍数和越权访问',async t=>{
  61. const {read}=await setup(t);
  62. for(const learningPass of [-1,4,1.5,'bad'])await assert.rejects(read({learningPass}),/学习遍数无效/);
  63. await assert.rejects(read({uid:99}),/无权访问/);
  64. });