Files
SGF/scripts/native-payable-page-http.mjs
2026-11-04 09:37:20 +02:00

15 lines
4.9 KiB
JavaScript

import fs from 'node:fs';import path from 'node:path';import net from 'node:net';import crypto from 'node:crypto';import assert from 'node:assert/strict';import {spawn} from 'node:child_process';
const state=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json','utf8')),tag=process.argv[2];if(state.port!==55439||!/^[a-f0-9]{12}$/.test(tag||''))throw Error('Owned native QA tag required');const root=path.join(state.root,'private/payable-history-'+tag),settings=path.join(root,'settings.json');const owned=JSON.parse(fs.readFileSync(settings,'utf8'));if(owned.tag!==tag||owned.cluster!=='sgf_modular_regression_'+state.id)throw Error('Wrong owned native database');
await new Promise((resolve,reject)=>{const p=net.createServer();p.once('error',()=>reject(Error('QA loopback port occupied')));p.listen(8047,'127.0.0.1',()=>p.close(resolve));});const log=fs.openSync(path.join(root,'native-page-http-server.log'),'a');const child=spawn('C:/laragon/tools/sgf-tempo/php-8.4.27/php.exe',['-S','127.0.0.1:8047',path.resolve('scripts/native-payable-page-http-router.php')],{cwd:process.cwd(),windowsHide:true,env:{...process.env,NATIVE_PAYABLE_PAGE_QA_SETTINGS:settings},stdio:['ignore',log,log]});
const jar=new Map();let csrf='';let checks=0;const check=(v,why)=>{checks++;assert(v,why);};const request=async(url,body,overrides={})=>{const headers={Accept:'application/json',Cookie:[...jar].map(([k,v])=>k+'='+v).join('; ')};if(body)Object.assign(headers,{'Content-Type':'application/json','X-CSRF-TOKEN':csrf,'Idempotency-Key':crypto.randomUUID()});Object.assign(headers,overrides);const r=await fetch('http://127.0.0.1:8047'+url,{method:body?'POST':'GET',headers,body:body?JSON.stringify(body):undefined,redirect:'manual',signal:AbortSignal.timeout(10000)});for(const c of r.headers.getSetCookie()){const pair=c.split(';')[0],n=pair.indexOf('=');jar.set(pair.slice(0,n),pair.slice(n+1));}const text=await r.text();let data;try{data=JSON.parse(text);}catch{}return {status:r.status,text,data,headers:r.headers};};
try{
let entry;for(let n=0;n<50;n++){try{entry=await request('/qa/native-enter');if(entry.status===200)break;}catch{}await new Promise(r=>setTimeout(r,100));}check(entry?.status===200,'Native QA role entry');csrf=entry.data.csrf;const invoice=entry.data.invoice;
const a=await request('/api/supplier-payables');check(a.status===200&&a.data.documents.length===25&&a.data.pagination.documents.total===10000,'Default native page bounded');const b=await request('/api/supplier-payables?page=2');check(b.status===200&&!b.data.documents.some(d=>a.data.documents.some(old=>old.local_id===d.local_id)),'Native pages disjoint');const large=await request('/api/supplier-payables?per_page=100');check(large.status===200&&large.data.documents.length===100,'Native larger page');
const original=await request('/api/supplier-payables?invoice='+invoice);check(original.status===200&&original.data.documents.length===1&&original.data.obligations.length===1&&original.data.obligations[0].balance_minor===3000,'Direct invoice uses same live financial position');check(original.data.obligations[0].pending_payments_minor===1000&&original.data.obligations[0].rejected_payments_minor===500,'Pending/rejected original states kept');
const recognised=await request('/api/supplier-payables?recognition=recognised');check(recognised.status===200&&recognised.data.pagination.documents.total===1,'Recognised filter before pagination');const search=await request('/api/supplier-payables?q=NATIVE-LOAD-00001');check(search.status===200&&search.data.pagination.documents.total===1,'Native number search');
for(const query of ['page=0','per_page=1000','tenant_id=999','recognition=paid','q%5Bx%5D=y'])check((await request('/api/supplier-payables?'+query)).status===422,'Invalid/foreign filter refused');
check((await request('/api/supplier-payables/prepare_history',{id:invoice,reason:'Read role cannot prepare.'})).status===403,'Reader has no write capability');check((await request('/api/supplier-payables/prepare_history',{id:invoice,reason:'Missing token must be refused.'},{'X-CSRF-TOKEN':''})).status===419,'Real HTTP CSRF enforced');
const html=await request('/obrigacoes-fornecedores?invoice='+invoice);check(html.status===200&&html.text.includes('name="invoice"')&&html.text.includes('action="/obrigacoes-fornecedores"'),'Native HTML keeps exact invoice scope');check(html.text.includes('Saldo por liquidar: 30.00 MZN'),'Native HTML uses common position');check(a.headers.get('cache-control').includes('no-store'),'Private native response');
const report={kind:'native_financial_page_actual_http',checks,passed:true,documents:10000,invoice_filter_preserved:true,live_native_history:true,read_role_write_refused:true,csrf_enforced:true,visual_review:false,browser_interactions_tested:false};fs.writeFileSync(path.join(root,'native-page-http.json'),JSON.stringify(report,null,2)+'\n');console.log(JSON.stringify(report));
}finally{if(child.exitCode===null){child.kill();await new Promise(r=>child.once('exit',r));}fs.closeSync(log);}