legacy-review-sync-runner.test.mjs 5.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import test from 'node:test';import assert from 'node:assert/strict';
  2. import {mkdtemp,rm} from 'node:fs/promises';import {tmpdir} from 'node:os';import {join} from 'node:path';
  3. import {readFileSync} from 'node:fs';
  4. import vm from 'node:vm';
  5. import {runDirectSync} from '../run-nightly-direct-sync.mjs';
  6. test('旧费用页完整且逐字段匹配的正金额记录才允许补进队列',async()=>{
  7. const control=readFileSync(new URL('../cloud/legacy-direct-control.js',import.meta.url),'utf8');
  8. const body=control.slice(control.indexOf('async function directReviewFeeProof('),control.indexOf('async function directControl('));
  9. const rows=Array.from({length:5},(_,index)=>({
  10. GeneralID:index+1,plid:index===4?31:30,yhid:20,xxjlid:90,orderId:80,kcid:70,fxzt:'1',wcsj:'2026-10-13 10:00',
  11. kywrq:'2026-10-13',kywsj:'2026-10-13 09:00',reviewFeeVerified:true,
  12. }));
  13. const warnings=[];
  14. const run=vm.runInNewContext(body+';directReviewFeeProof',{
  15. Psql:{query:async()=>rows.map(row=>({generalId:row.GeneralID,coachId:row.plid}))},
  16. console:{warn:message=>warnings.push(message)},
  17. directOld:async(action,{uid,cpage})=>{
  18. if(action==='content_list')return {items:[],page:{itemCount:0,pageCount:0}};
  19. assert.equal(action,'e_get_21list');
  20. if(uid===31)return {items:[{...rows[4],money:1}],page:{pageCount:1,itemCount:2}};
  21. return cpage===1?{items:[{...rows[0],money:1},{...rows[1],money:0},{...rows[2],money:1}],page:{pageCount:2,itemCount:5}}:
  22. {items:[{...rows[2],money:1},{...rows[3],money:1,wcsj:'2026-10-13 10:01'}],page:{pageCount:2,itemCount:5}};
  23. },
  24. });
  25. const result=await run('c',rows);
  26. assert.deepEqual(Array.from(result,row=>row.reviewFeeVerified),[true,false,false,false,false]);
  27. assert.ok(rows.every(row=>row.reviewFeeVerified));
  28. assert.equal(warnings.length,1);
  29. });
  30. test('所有课次同步后分页核定工资,待核定原因进入回执,写入重试复用同一批次',async()=>{
  31. const root=await mkdtemp(join(tmpdir(),'review-sync-runner-')),original=globalThis.fetch,order=[],receipts=new Map();let simulatedLostResponse=false,applied=0,final;
  32. try{
  33. globalThis.fetch=async(url,options)=>{const p=JSON.parse(options.body).params;order.push(p.action==='source'?p.modelId:p.action);let data;
  34. if(p.action==='status')data={source:'old-api/WXAPP',bridgeUsed:false,reviewSyncVersion:'source-owned-v1'};
  35. else if(p.action==='claim')data={runId:'review-sync-test-run',businessDate:'2026-11-02'};
  36. else if(p.action==='supplement')data={accounts:{missing:0},nodes:{missing:0}};
  37. else if(p.action==='source')data={items:[],page:{itemCount:0,pageCount:1}};
  38. else if(p.action==='evidence')data={sourceChecksum:'d41d8cd98f00b204e9800998ecf8427e',targetChecksum:'d41d8cd98f00b204e9800998ecf8427e',reconciled:true};
  39. else if(p.action==='review-income'){
  40. if(!receipts.has(p.batchKey)){applied++;receipts.set(p.batchKey,p.afterTaskId?{processed:0,earned:0,excluded:0,amount:0,pending:1,errors:[{taskId:'z',reason:'来源课次时长仍不明确'}],hasMore:false,afterTaskId:'z'}:{processed:1,earned:1,excluded:0,amount:.5,pending:1,errors:[],hasMore:true,afterTaskId:'a'});}
  41. data=receipts.get(p.batchKey);if(!simulatedLostResponse){simulatedLostResponse=true;throw Error('response lost after commit');}
  42. }
  43. else if(p.action==='finance-members'||p.action==='read')data=[];
  44. else if(p.action==='finish'){final=p.summary;data=p.summary;}
  45. else throw Error('Unexpected action '+p.action);
  46. return {ok:true,json:async()=>({success:true,data})};};
  47. const result=await runDirectSync({XIAOSHU_SYNC_REPORT_DIR:root,XIAOSHU_SYNC_OPERATOR_TOKEN:'test'});
  48. assert.equal(result.status,'completed-with-preserved-conflicts',result.reason);assert.equal(applied,2);assert.equal(receipts.size,2);
  49. assert.equal(final.reviewIncome.earned,1);assert.equal(final.reviewIncome.amount,.5);assert.equal(final.reviewIncome.pending,1);assert.equal(final.conflicts,1);assert.match(final.reviewIncome.errors[0].reason,/时长/);
  50. assert(order.lastIndexOf(59)<order.indexOf('review-income'));assert(order.indexOf('review-income')<order.indexOf('finance-members'));
  51. }finally{globalThis.fetch=original;await rm(root,{recursive:true,force:true});}
  52. });
  53. test('体验课零元需要精确费用、原预约和学习记录,正式课正金额沿用原验证',async()=>{
  54. const control=readFileSync(new URL('../cloud/legacy-direct-control.js',import.meta.url),'utf8');
  55. const body=control.slice(control.indexOf('async function directReviewFeeProof('),control.indexOf('async function directControl('));
  56. const rows=[80,81,82,83,84].map((orderId,i)=>({GeneralID:i+1,plid:30,yhid:20,xxjlid:90+i,orderId,kcid:70,fxzt:'1',wcsj:'2026-10-13 10:00',kywrq:'2026-10-13',kywsj:'2026-10-13 09:00'}));
  57. const page=items=>({items,page:{itemCount:items.length,pageCount:1}});
  58. const run=vm.runInNewContext(body+';directReviewFeeProof',{
  59. Psql:{query:async()=>rows.map(row=>({generalId:row.GeneralID,coachId:30}))},
  60. directOld:async(action,args)=>{
  61. if(action==='e_get_21list')return page(rows.map((row,i)=>({...row,money:['0','1','0','',null][i]})));
  62. const ids=args.ids.split(',').map(Number);
  63. if(args.modelId===54)return page(rows.map((row,i)=>({GeneralID:row.orderId,szyh:20,dslx:i===2?'2':'3',Title:'学员(体验课)'})).filter(row=>ids.includes(row.GeneralID)));
  64. if(args.modelId===56)return page(rows.map(row=>({GeneralID:row.xxjlid,dsid:row.orderId})).filter(row=>ids.includes(row.GeneralID)));
  65. throw Error('unexpected source');
  66. },
  67. });
  68. const result=await run('c',rows);
  69. assert.equal(result[0].reviewTrialProof?.fee.money,'0');
  70. assert.equal(result[0].reviewTrialProof?.appointment.GeneralID,80);
  71. assert.equal(result[0].reviewTrialProof?.study.GeneralID,90);
  72. assert.deepEqual(Array.from(result,row=>row.reviewFeeVerified),[false,true,false,false,false]);
  73. assert.deepEqual(Array.from(result,row=>Boolean(row.reviewTrialProof)),[true,false,false,false,false]);
  74. });