import fs from 'node:fs'; import assert from 'node:assert/strict'; import crypto from 'node:crypto'; const legacy=process.argv.includes('--previous'); const privateData=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-2576427d1939/private/development.json','utf8')); assert.deepEqual([privateData.tenant_id,privateData.company_id,privateData.unit_id],[1,1,1]); const checks=[]; for(const profile of privateData.profiles){ const jar=new Map();let csrf='',token=''; const api=async(path,method='GET',body,status=200,overrides={})=>{ const response=await fetch('http://127.0.0.1:8039'+path,{method,headers:{Accept:'application/json','Content-Type':'application/json',Cookie:[...jar].map(([k,v])=>k+'='+v).join('; '),'X-CSRF-TOKEN':csrf,'X-SGF-Context':token,'X-Purchasing-Context':'procurement.internal','Idempotency-Key':crypto.randomUUID(),...overrides},...(body===undefined?{}:{body:JSON.stringify(body)})}); for(const cookie of response.headers.getSetCookie()){const pair=cookie.split(';')[0],at=pair.indexOf('=');jar.set(pair.slice(0,at),pair.slice(at+1));} const text=await response.text();let d;try{d=JSON.parse(text);}catch{d={};} assert((Array.isArray(status)?status:[status]).includes(response.status),`${profile.role} ${path}: expected ${status}, got ${response.status}: ${d.message??''}`); if(d.csrf)csrf=d.csrf;if(d.context?.token)token=d.context.token; return d; }; await api('/api/session');await api('/api/login','POST',{email:profile.email,password:privateData.password}); await api('/api/context','POST',{tenant_id:1,company_id:1,unit_id:1,module_id:'purchasing'}); const list=await api('/api/internal-requisitions'); if(legacy&&profile.role==='consulta'){ assert(!list.records.some(r=>r.id===1));await api('/api/internal-requisitions/1','GET',undefined,404); checks.push('Previous version reproduces missing read-only listing/detail');continue; } assert(list.records.some(r=>r.id===1),'Existing request must be listed'); const detail=await api('/api/internal-requisitions/1'); assert.equal(detail.record.id,1);assert.equal(detail.record.version,3);assert.equal(detail.record.status,'approved');assert.equal(detail.record.total_minor,10000); assert.equal(detail.history.length,3); checks.push(`${profile.role}: existing request 1 listed, detail and three-event history readable`); if(profile.role==='consulta'){ assert.equal(list.permissions.prepare,false);assert.equal(list.permissions.configure,false);assert.deepEqual(detail.record.allowed_actions,[]); await api('/api/internal-requisitions/save','POST',{operation_key:crypto.randomUUID()},403); for(const action of ['save','submit','decide'])await api('/api/internal-requisitions/1/'+action,'POST',{operation_key:crypto.randomUUID(),version:3,...(action==='decide'?{decision:'approved'}:{})},[403,404]); await api('/api/internal-requisitions/policy','POST',{version:1,reason:'Denied read-only configuration verification.',enabled:true,budget_required:true,first_limit_minor:1000000,second_limit_minor:2000000,approval_includes_tax:true,centres:[{id:1,department_ref:'OPER'}],members:[{user_id:1,department_ref:'OPER'},{user_id:2,department_ref:'OPER'}]},403); await api('/api/internal-requisitions/centres','POST',{operation_key:crypto.randomUUID(),code:'REFUSED',reason:'Read-only denial check.'},403); await api('/api/purchasing-budget/approve','POST',{budget_id:1},403); await api('/api/accesses','GET',undefined,403); checks.push('Consulta: no allowed actions; creation, save, submission, decision, policy, centre, budget approval and administration refused'); await api('/api/internal-requisitions/1','GET',undefined,409,{'X-SGF-Context':'invalid'}); await api('/api/internal-requisitions/1','GET',undefined,403,{'X-Purchasing-Context':'procurement.third_party'}); await api('/api/internal-requisitions/999999999','GET',undefined,404); await api('/api/context','POST',{tenant_id:1,company_id:1,unit_id:2,module_id:'purchasing'},403); await api('/api/context','POST',{tenant_id:1,company_id:2,unit_id:3,module_id:'purchasing'},403); checks.push('Consulta: forged context, wrong option, missing record and unauthorised company/unit refused'); } } const report={passed:true,mode:legacy?'previous-version-recovery':'corrected-version',url:'http://127.0.0.1:8039',scope:'1:1:1',request_id:1,request_recreated:false,reservation_commands_executed:false,rights_changed:false,checks,checked_at:new Date().toISOString(),visual_review:false}; fs.writeFileSync('C:/laragon/www/compras/docs/proofs/sgf-read-8039-'+(legacy?'rollback':'http')+'.json',JSON.stringify(report,null,2)); console.log(JSON.stringify(report));