import fs from 'node:fs'; import assert from 'node:assert/strict'; import crypto from 'node:crypto'; const root='C:/laragon/data/sgf-tempo/run/modular-regression-2576427d1939'; const privateData=JSON.parse(fs.readFileSync(root+'/private/development.json','utf8')); assert.deepEqual([privateData.tenant_id,privateData.company_id,privateData.unit_id],[1,1,1]); const stateFile=root+'/private/requisitions-recovery/http-state.json'; const state=fs.existsSync(stateFile)?JSON.parse(fs.readFileSync(stateFile,'utf8')):{}; const persist=()=>fs.writeFileSync(stateFile,JSON.stringify(state,null,2)); const key=name=>{state.keys??={};state.keys[name]??=crypto.randomUUID();persist();return state.keys[name];}; const checks=[]; const clients={}; for(const p of privateData.profiles){ const jar=new Map();let csrf='',token=''; const api=async(path,method='GET',body,expected=200,commandKey)=>{ const r=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':commandKey??crypto.randomUUID()},...(body===undefined?{}:{body:JSON.stringify(body)})}); for(const cookie of r.headers.getSetCookie()){const pair=cookie.split(';')[0],at=pair.indexOf('=');jar.set(pair.slice(0,at),pair.slice(at+1));} const text=await r.text();let d;try{d=JSON.parse(text);}catch{d={message:text.slice(0,120)};} assert((Array.isArray(expected)?expected:[expected]).includes(r.status),`${p.role} ${path}: expected ${expected}, got ${r.status}: ${d.message??text.slice(0,180)}`); 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:p.email,password:privateData.password}); await api('/api/context','POST',{tenant_id:1,company_id:1,unit_id:1});clients[p.role]=api; } checks.push('Existing three synthetic identities authenticate without password changes'); const prep=clients.preparador,review=clients.revisor,read=clients.consulta; const rights={1:['purchasing_requisitions.view','purchasing_requisitions.prepare','purchasing_requisitions.configure','purchasing_requisitions.approve.department_head','purchasing_budget.view','purchasing_budget.create','purchasing_budget.approve','purchasing_budget.record'],2:['purchasing_requisitions.view','purchasing_requisitions.approve.department_head','purchasing_budget.view','purchasing_budget.approve'],3:['purchasing_requisitions.view','purchasing_budget.view']}; await prep('/api/internal-requisitions/access-initialisation','POST',{operation_key:key('initial'),reason:'Owner-confirmed fourteen rights in synthetic demonstration 8039 only.',administrators:[{user_id:1,rights:rights[1]}]}); for(const [role,id] of [['revisor',2],['consulta',3]]){ const profile=privateData.profiles.find(p=>p.role===role); for(const permission of rights[id])await prep('/api/accesses','POST',{email:profile.email,unit_id:1,permission,reason:'Owner-confirmed synthetic demonstration access 8039.'}); } checks.push('Eight plus four plus two explicit authorised grants'); for(const api of Object.values(clients))await api('/api/context','POST',{tenant_id:1,company_id:1,unit_id:1,module_id:'purchasing'}); const centre=await prep('/api/internal-requisitions/centres','POST',{operation_key:key('centre'),code:'SINTETICO-REPRO',reason:'Owner-authorised synthetic centre for demonstration only.'}); state.centre_id=centre.centre_id;persist(); await read('/api/internal-requisitions/centres','POST',{operation_key:key('read-centre'),code:'REFUSED',reason:'Synthetic read-only refusal verification.'},403); if(!state.budget_id){ await prep('/api/purchasing-budget/create','POST',{cost_center_id:state.centre_id,year:2026,kind:'operating',includes_tax:true,amount:'10000.00',reason:'Owner-authorised synthetic 10000 MZN allocation for 2026.'},200,key('budget')); const list=await prep('/api/purchasing-budget'); fs.writeFileSync(root+'/private/requisitions-recovery/budget-response.json',JSON.stringify(list,null,2)); state.budget_pending=true;persist(); } // Obtain exact budget identifier from the native response; no guessing IDs. const workspace=await prep('/api/purchasing-budget'); const candidates=workspace.budgets??workspace.allocations??workspace.records; if(!state.budget_id){ assert(Array.isArray(candidates),'Inspect native budget response shape before proceeding'); const found=candidates.filter(b=>Number(b.cost_center_id??b.centre_id)===Number(state.centre_id)&&Number(b.year)===2026); assert.equal(found.length,1,'Exactly one synthetic allocation');state.budget_id=found[0].id;persist(); } if(!state.budget_approved){ await prep('/api/purchasing-budget/approve','POST',{budget_id:state.budget_id},403,key('self-budget')); await read('/api/purchasing-budget/approve','POST',{budget_id:state.budget_id},403,key('read-budget')); await review('/api/purchasing-budget/approve','POST',{budget_id:state.budget_id},200,key('approve-budget')); state.budget_approved=true;persist(); } checks.push('Synthetic allocation 10000 MZN approved by second identity; self and read-only approvals refused'); const policy={version:0,reason:'Owner-confirmed synthetic OPER policy, demonstration 8039 only.',enabled:true,budget_required:true,first_limit_minor:1000000,second_limit_minor:2000000,approval_includes_tax:true,centres:[{id:state.centre_id,department_ref:'OPER'}],members:[{user_id:1,department_ref:'OPER'},{user_id:2,department_ref:'OPER'}]}; await read('/api/internal-requisitions/policy','POST',policy,403); if(!state.policy_configured){await prep('/api/internal-requisitions/policy','POST',policy);state.policy_configured=true;persist();} const draft={operation_key:key('draft'),cost_center_id:state.centre_id,priority:'normal',needed_by:'2026-11-30',justification:'Synthetic demonstration requisition, no Tempo business operation.',items:[{description:'Synthetic demonstration material',category:'Consumiveis',purchase_type:'consumable',quantity:'1.000',unit:'UN',unit_price:'100.00',tax_rate:'0.00'}]}; await read('/api/internal-requisitions/save','POST',{...draft,operation_key:key('read-save')},403); if(!state.request_id){const saved=await prep('/api/internal-requisitions/save','POST',draft);state.request_id=saved.id;persist();} const path='/api/internal-requisitions/'+state.request_id; if(!state.submitted){ const submitted=await prep(path+'/submit','POST',{operation_key:key('submit'),version:1}); state.submitted=submitted;persist(); const replay=await prep(path+'/submit','POST',{operation_key:key('submit'),version:1}); assert.equal(replay.id,state.request_id);checks.push('Submission retry reuses same requisition'); } if(!state.approved){ const decision={operation_key:key('decision'),version:state.submitted.version,decision:'approved'}; await prep(path+'/decide','POST',decision,403); await read(path+'/decide','POST',decision,[403,404]); await read(path+'/submit','POST',{operation_key:key('read-submit'),version:state.submitted.version},[403,404]); const approved=await review(path+'/decide','POST',decision);assert.equal(approved.status,'approved');state.approved=true;persist(); } for(const api of [prep,review]){const d=await api(path);assert.equal(d.record.status,'approved');assert.equal(d.history.length,3);} await read(path,'GET',undefined,404); await read('/api/internal-requisitions'); await read('/api/accesses','GET',undefined,403); checks.push('Full request lifecycle, independent approval and three-event history consulted by preparer and reviewer'); checks.push('Existing record visibility rule returns 404 to read-only third identity; listing remains accessible; no extra rights granted'); checks.push('Read-only profile refused centre, budget approval, policy, preparation, submission, decision and access administration'); fs.writeFileSync('C:/laragon/www/compras/docs/proofs/sgf-demo-8039-http.json',JSON.stringify({passed:true,url:'http://127.0.0.1:8039',scope:'1:1:1',rights,centre_id:state.centre_id,budget_id:state.budget_id,request_id:state.request_id,checks,checked_at:new Date().toISOString(),visual_review:false,real_installation_changed:false},null,2)); console.log(JSON.stringify({passed:true,checks,request_id:state.request_id}));