read-pressure.test.mjs 4.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. import {test} from 'node:test';import assert from 'node:assert/strict';import {PGlite} from '@electric-sql/pglite';import {readFileSync,existsSync} from 'node:fs';
  2. const file=new URL('../lib/read-pressure-runtime.mjs',import.meta.url);
  3. test('runtime protects named heavy reads, preserves unknown writes and isolates permission scopes',async()=>{
  4. assert.ok(existsSync(file),'read pressure runtime exists');const {readPressurePolicy}=await import(file);
  5. assert.equal(readPressurePolicy('ops',{operation:'ops/payroll/overview'}).heavy,true);
  6. assert.equal(readPressurePolicy('app',{action:'app_review_page',operation:'ops/lessons/list'}).heavy,true);
  7. assert.equal(readPressurePolicy('app',{action:'content_add',operation:'ops/lessons/list'}).heavy,false);
  8. assert.equal(readPressurePolicy('ops',{operation:'ops/payroll/create'}).heavy,false);
  9. });
  10. test('shared row cache coalesces separate requests, validates each caller and refreshes after revision changes',async()=>{
  11. assert.ok(existsSync(file));const {dispatchReadPressure}=await import(file);const pg=new PGlite();
  12. const query=async(s,p=[])=>(await pg.query(s,p)).rows;const db={query,any:query,one:async(s,p=[])=>(await query(s,p))[0],none:async(s,p=[])=>{await query(s,p);},tx:async work=>work(db)};
  13. try{await pg.exec('CREATE TABLE "LessonRecord"(company text,id int);');await pg.exec(readFileSync(new URL('../sql/read-revisions.sql',import.meta.url),'utf8'));await pg.exec(readFileSync(new URL('../sql/read-pressure.sql',import.meta.url),'utf8'));
  14. let executions=0,auths=0;const response=()=>({body:null,json(value){this.body=value;return this;},status(){return this;}});
  15. const req={body:{params:{operation:'ops/lessons/list'}}};const authorize=async()=>{auths++;return{company:'co',scope:'manager:0'};};
  16. const execute=async(sql,r)=>{executions++;await new Promise(r=>setTimeout(r,15));return r.json({success:true,data:{items:[{id:executions}]}});};
  17. const a=response(),b=response();await Promise.all([dispatchReadPressure(db,req,a,'ops',sql=>execute(sql,a),authorize),dispatchReadPressure(db,req,b,'ops',sql=>execute(sql,b),authorize)]);
  18. assert.equal(executions,1);assert.equal(auths,2);assert.deepEqual(a.body.data,b.body.data);assert.ok(b.body.readCache.remainingMs<=30000);
  19. await pg.exec("INSERT INTO \"LessonRecord\" VALUES('co',1)");const c=response();await dispatchReadPressure(db,req,c,'ops',sql=>execute(sql,c),authorize);assert.equal(executions,2);
  20. const other=response();await dispatchReadPressure(db,req,other,'ops',sql=>execute(sql,other),async()=>({company:'co',scope:'manager:10'}));assert.equal(executions,3);
  21. await new Promise(r=>setTimeout(r,5));const count=executions;await Promise.all(Array.from({length:3},()=>{const r=response();return dispatchReadPressure(db,{body:{params:{operation:'ops/lessons/list',refresh:true}}},r,'ops',sql=>execute(sql,r),authorize);}));assert.equal(executions-count,1,'forced refresh calls must coalesce');
  22. }finally{await pg.close();}
  23. });
  24. test('budget adapter supports pg-promise frozen method properties',async()=>{
  25. const {readPressureDb}=await import(file);const tx=Object.freeze({none:async()=>{},one:async()=>({ok:true})});const db=Object.freeze({tx:async work=>work(tx),one:async()=>({ok:true})});
  26. assert.deepEqual(await readPressureDb(db,Date.now()+1000).one('SELECT 1'),{ok:true});
  27. });
  28. test('budget batches transaction control in one exchange and rolls back on query failure',async()=>{
  29. const {readPressureDb}=await import(file);const calls=[];let fail=false;
  30. const connection={multiResult:async sql=>{calls.push(sql);if(fail)throw Error('sql failed');return[{rows:[]},{rows:[]},{rows:[{ok:1}]},{rows:[]}];},none:async sql=>calls.push(sql)};
  31. const db={$config:{pgp:{as:{format:sql=>sql}}},task:async work=>work(connection),one:async()=>{throw Error('Must batch');}};
  32. assert.deepEqual(await readPressureDb(db,Date.now()+5000).one('SELECT 1'),{ok:1});assert.match(calls[0],/^BEGIN;SET LOCAL statement_timeout='\d+ms';SELECT 1\n;COMMIT;$/);
  33. fail=true;await assert.rejects(readPressureDb(db,Date.now()+5000).one('SELECT 1'),/sql failed/);assert.equal(calls.at(-1),'ROLLBACK');
  34. });
  35. test('budget preserves callers that select transaction isolation before their first query',async()=>{
  36. const {readPressureDb}=await import(file);let isolation=false;const calls=[];
  37. const tx={none:async sql=>{calls.push(sql);if(sql.startsWith('SET TRANSACTION'))isolation=true;},one:async sql=>{if(!isolation)throw Error('SET TRANSACTION must precede queries');return{ok:true};}};
  38. const db={tx:async work=>work(tx)};
  39. await readPressureDb(db,Date.now()+1000).tx(async bounded=>{await bounded.none('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ');assert.deepEqual(await bounded.one('SELECT 1'),{ok:true});});
  40. assert.ok(calls[0].startsWith('SET LOCAL statement_timeout='));
  41. });