import fs from 'node:fs/promises'; import assert from 'node:assert/strict'; import {randomUUID,createHash} from 'node:crypto'; import {execFile} from 'node:child_process'; import {promisify} from 'node:util'; const run=promisify(execFile), reports=[]; const historyOnly=process.argv.includes('--history-only'); async function probe(mode,job,input={}){ // Use spawned stdin for payloads; credentials stay in protected settings read by PHP. const {spawn}=await import('node:child_process'); return await new Promise((resolve,reject)=>{const p=spawn(mode==='compras'?'C:/Users/macbook/.config/investmoz/php-8.5.11/php.exe':'C:/laragon/tools/sgf-tempo/php-8.4.27/php.exe',['scripts/payables-probe.php',mode,job],{cwd:process.cwd(),windowsHide:true});let out='',err='';p.stdout.on('data',d=>out+=d);p.stderr.on('data',d=>err+=d);p.on('error',reject);p.on('close',code=>{try{if(code)throw new Error('QA probe failed: '+out+err);resolve(JSON.parse(out));}catch(e){reject(e);}});p.stdin.end(JSON.stringify(input));}); } for(const mode of ['compras','sgf']){ const fixture=JSON.parse(await fs.readFile(`C:/laragon/data/sgf-tempo/secrets/payables-proof-${mode}-access.json`,'utf8')); const base=mode==='compras'?'http://127.0.0.1:8007':'http://127.0.0.1:8008'; function client(){let csrf='',context='';const cookies=new Map();return { async request(path,method='GET',body,opts={}){ const r=await fetch(base+path,{method,headers:{Accept:'application/json','Content-Type':'application/json',Cookie:[...cookies].map(([k,v])=>`${k}=${v}`).join('; '),'X-CSRF-TOKEN':opts.noCsrf?'':csrf,'X-XSRF-TOKEN':opts.noCsrf?'':decodeURIComponent(cookies.get('XSRF-TOKEN')??''),'X-SGF-Context':opts.stale?'stale':context,...(opts.key?{'Idempotency-Key':opts.key}:{})},...(body===undefined?{}:{body:JSON.stringify(body)})}); for(const c of r.headers.getSetCookie()){const pair=c.split(';',1)[0],i=pair.indexOf('=');cookies.set(pair.slice(0,i),pair.slice(i+1));} const text=await r.text();let data={};try{data=JSON.parse(text);}catch{}if(data.csrf)csrf=data.csrf;if(data.context?.token)context=data.context.token;return {status:r.status,data,text}; }, async login(role){await this.request(mode==='compras'?'/login':'/api/session');const a=fixture.accounts[role];const r=await this.request(mode==='compras'?'/login':'/api/login','POST',{email:a.email,password:a.password});assert.equal(r.status,200,mode+' login');if(mode==='sgf')assert.equal((await this.request('/api/context','POST',{tenant_id:fixture.scope.tenant_id,company_id:fixture.scope.company_id,unit_id:fixture.scope.unit_id})).status,200);}, async list(){const r=await this.request('/api/supplier-payables');assert.equal(r.status,200,mode+' listing '+r.text.slice(0,180));return r.data;}, async send(action,id,key=randomUUID(),expected=200){const r=await this.request('/api/supplier-payables/'+action,'POST',{id,reason:'Prova sintética de '+action},{key});assert.equal(r.status,expected,mode+' '+action+' '+r.text.slice(0,180));return r.data;} };} const anon=client();assert.equal((await anon.request('/api/supplier-payables')).status,401); const p=client(),v=client(),q=client();await p.login('preparacao');await v.login('validacao');await q.login('consulta'); let list=await q.list();assert.equal(list.obligations.length,0);const doc=list.documents[0];assert.equal(doc.subtotal_minor,1000000);assert.equal(doc.tax_minor,160000);assert.equal(doc.total_minor,1160000);assert.equal(doc.status,'validated');assert(doc.validation_audit_id); const before=await probe(mode,'inspect'); if(historyOnly){ await probe(mode,'history');const existing=await probe(mode,'inspect'); const pending=await p.send('prepare',fixture.source_id);assert.equal(pending.state,'pending_source');assert(pending.missing.some(x=>x.includes('pagamentos'))); assert.equal((await v.send('validate',pending.proposal_id)).state,'conflict'); const refused=await probe(mode,'inspect');assert.equal(refused.counts.obligations,0);assert.deepEqual(refused.domain_hashes,existing.domain_hashes);if(mode==='compras')assert.equal(refused.legacy_balance,1130000); reports.push({host:mode,passed:true,core_sha256:refused.core_sha256,source_revision:doc.revision,recognised_obligations:0,checks:['previous_settlement_blocks_recognition','history_unchanged','no_full_unpaid_import'],visual_review:false});continue; } const preparerPage=await p.request(mode==='compras'?'/obrigacoes-fornecedores':'/supplier-payables');assert.equal(preparerPage.status,200);assert(preparerPage.text.includes('data-action="prepare"')); const readerPage=await q.request(mode==='compras'?'/obrigacoes-fornecedores':'/supplier-payables');assert(!readerPage.text.includes('