lesson-reading-source.test.mjs 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. import {test} from 'node:test';import assert from 'node:assert/strict';import {readFileSync} from 'node:fs';import vm from 'node:vm';
  2. const source=readFileSync(new URL('../deploy-admin-functions.mjs',import.meta.url),'utf8').match(/const appGatewayCode = String.raw`([\s\S]*?)\n`;/)[1];
  3. test('real AI adapter appends all server-resolved words and rejects stale source before provider call',async()=>{
  4. const sent=[],wordList=Array.from({length:17},(_,i)=>'word'+i);
  5. const ctx=vm.createContext({Parse:{Config:{get:async()=>({get:()=> 'test-provider-token'})}},fetch:async(url,init)=>{sent.push(JSON.parse(init.body));return{ok:true,text:async()=>JSON.stringify({choices:[{message:{content:'article'}}]})}}});
  6. vm.runInContext(source,ctx);ctx.appReadingSource=async()=>({sourceVersion:'4',wordList});
  7. await ctx.readingAi({sourceStudyId:'fixture',sourceVersion:'4',prompt:'Write a story'},{});
  8. assert.equal(sent.length,1);for(const word of wordList)assert(sent[0].messages[0].content.includes(word));
  9. await assert.rejects(ctx.readingAi({sourceStudyId:'fixture',sourceVersion:'3',prompt:'Write a story'},{}),/已更新/);assert.equal(sent.length,1);
  10. });
  11. const lessonStudy=readFileSync(new URL('../cloud/lesson-study.js',import.meta.url),'utf8');
  12. function readingSourceContext(record,order,authorizeRecord=true,studentId=86){
  13. const calls=[];
  14. const ctx=vm.createContext({number:Number,recordDetailData:async()=>{if(!authorizeRecord)throw Object.assign(Error('无权查看该学员的学习记录'),{status:403});return{record,words:[{GeneralID:1,Title:'apple'},{GeneralID:2,Title:'school'}]};},orderDetailRow:async(id,current,authorize=true)=>{calls.push({id,authorize});if(authorize)throw Object.assign(Error('无权查看该业务记录'),{status:403});return order;},fail:(status,message)=>{throw Object.assign(Error(message),{status});}});
  15. vm.runInContext(lessonStudy,ctx);return{run:()=>ctx.appReadingSource({sourceStudyId:record.GeneralID,studentId},{id:'linked-teacher'}),calls};
  16. }
  17. test('关联老师可以用已授权的学员课堂词表生成阅读,无需读取其他老师的课程详情',async()=>{
  18. const {run,calls}=readingSourceContext({GeneralID:'study',userId:86,pl:104,dsid:'lesson',recordScope:'lesson',studyVersion:9},{szyh:86,pl:104,dszt:30});
  19. const result=await run();assert.equal(result.studentId,86);assert.equal(result.sourceVersion,'9');assert.deepEqual(Array.from(result.wordList),['apple','school']);assert.equal(calls.length,1);assert.equal(calls[0].authorize,false);
  20. });
  21. test('未通过学习记录授权的老师仍被拒绝,不继续读取课程',async()=>{
  22. const {run,calls}=readingSourceContext({GeneralID:'study',userId:86,dsid:'lesson',recordScope:'lesson'},{szyh:86,dszt:30},false);
  23. await assert.rejects(run(),e=>e.status===403);assert.equal(calls.length,0);
  24. });
  25. test('阅读词表不能使用其他学员的课程或尚未结束的课程',async()=>{
  26. const record={GeneralID:'study',userId:86,dsid:'lesson',recordScope:'lesson'};
  27. await assert.rejects(readingSourceContext(record,{szyh:85,dszt:30}).run(),/课程学员不一致/);
  28. await assert.rejects(readingSourceContext(record,null).run(),/课程学员不一致/);
  29. await assert.rejects(readingSourceContext(record,{szyh:86,dszt:10}).run(),/请先完成课程/);
  30. });
  31. test('历史非课堂记录沿用学习记录权限,不误触发预约详情权限',async()=>{
  32. const {run,calls}=readingSourceContext({GeneralID:'historical',userId:86,dsid:'old-lesson',updatedAt:'old-version'},null);
  33. assert.equal((await run()).studentId,86);assert.equal(calls.length,0);
  34. });
  35. test('阅读入口指定的学员必须与学习记录一致,不能串用关联学员词表',async()=>{
  36. const {run,calls}=readingSourceContext({GeneralID:'study',userId:86,dsid:'lesson',recordScope:'lesson'},{szyh:86,dszt:30},true,85);
  37. await assert.rejects(run(),e=>e.status===403&&e.message.includes('阅读学员与学习记录不一致'));assert.equal(calls.length,0);
  38. });