review-income-query.test.mjs 1.8 KB

123456789101112131415161718192021
  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. test('income lookup stays correlated to visible teacher tasks and preserves all/month/student totals',async()=>{
  7. const db=new PGlite();try{
  8. await db.exec(readFileSync(new URL('./fixtures/teacher-review-schema.sql',import.meta.url),'utf8'));
  9. await db.exec(`INSERT INTO "ReviewRewardIncome"(company,"taskId",amount,status) VALUES('co','r1',1,'earned'),('co','r2',0.5,'earned'),('other','r1',99,'earned');`);
  10. const query=async(s,a)=>(await db.query(s,a)).rows;const incomeQueries=[];
  11. const ctx=vm.createContext({Date,Promise,DEFAULT_COMPANY_ID:'co',number:(v,f=0)=>Number(v)||f,authorizeRequestedUser:async()=>30,appReviewTarget:async()=>20,appOverviewToday:()=> '2026-10-14',legacyAliases:r=>r,appLiveVersionSnapshot:async()=>({}),syncLegacyReviews:async()=>({}),Psql:{query,one:async(s,a)=>{if(s.includes(' AS income'))incomeQueries.push(s);return (await query(s,a))[0];}}});
  12. vm.runInContext(readFileSync(new URL('../cloud/mobile-teaching.js',import.meta.url),'utf8'),ctx);
  13. const coach={get:()=>3};const first=await ctx.appReviewPage({status:'completed'},coach);
  14. assert.equal(first.summary.income,0.5);assert.equal(first.summary.incomeScope,'all');
  15. await ctx.appReviewPage({status:'completed',month:'2026-10',studentId:20},coach);
  16. assert.equal((await ctx.appReviewPage({status:'completed',month:'2020-01'},coach)).summary.income,0);
  17. assert.equal(incomeQueries.length,3);
  18. for(const sql of incomeQueries)assert.match(sql,/JOIN LATERAL \(SELECT r\."amount" FROM "ReviewRewardIncome"[\s\S]*r\."taskId"=m\."objectId" OFFSET 0\) r ON TRUE/,'fee view must be looked up after teacher task selection, not expanded across all tenant rewards');
  19. }finally{await db.close();}
  20. });