| 123456789101112131415161718192021 |
- import {test} from 'node:test';
- import assert from 'node:assert/strict';
- import {readFileSync} from 'node:fs';
- import vm from 'node:vm';
- import {PGlite} from '@electric-sql/pglite';
- test('income lookup stays correlated to visible teacher tasks and preserves all/month/student totals',async()=>{
- const db=new PGlite();try{
- await db.exec(readFileSync(new URL('./fixtures/teacher-review-schema.sql',import.meta.url),'utf8'));
- await db.exec(`INSERT INTO "ReviewRewardIncome"(company,"taskId",amount,status) VALUES('co','r1',1,'earned'),('co','r2',0.5,'earned'),('other','r1',99,'earned');`);
- const query=async(s,a)=>(await db.query(s,a)).rows;const incomeQueries=[];
- 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];}}});
- vm.runInContext(readFileSync(new URL('../cloud/mobile-teaching.js',import.meta.url),'utf8'),ctx);
- const coach={get:()=>3};const first=await ctx.appReviewPage({status:'completed'},coach);
- assert.equal(first.summary.income,0.5);assert.equal(first.summary.incomeScope,'all');
- await ctx.appReviewPage({status:'completed',month:'2026-10',studentId:20},coach);
- assert.equal((await ctx.appReviewPage({status:'completed',month:'2020-01'},coach)).summary.income,0);
- assert.equal(incomeQueries.length,3);
- 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');
- }finally{await db.close();}
- });
|