7 lines
5.8 KiB
JavaScript
7 lines
5.8 KiB
JavaScript
import fs from 'node:fs';import assert from 'node:assert/strict';import crypto from 'node:crypto';import {session} from './sgf-single-http.mjs';
|
|
const root=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-current.json')).root+'/private/receipt-composition-cc50a276c10c',file=root+'/stock-catalogue-http-20261110.json';let p=fs.existsSync(file)?JSON.parse(fs.readFileSync(file)):{record_key:crypto.randomUUID(),operations:{},checks:[]};if(p.passed){console.log(JSON.stringify({passed:true,reused:true,proof:file}));process.exit();}const save=()=>fs.writeFileSync(file,JSON.stringify(p,null,2)),ok=(v,n)=>{assert(v,n);if(!p.checks.includes(n))p.checks.push(n);save();};const prepare=await session('purchasing:validator','stock'),review=await session('sgf:workshop_verify','stock');ok(prepare.identity.context.stock.catalogue===true,'approved catalogue available in existing context');let r=await prepare.get('/api/stock-catalogue?view=items&search=QA-CATALOGO-INTEGRADO');assert.equal(r.status,200);const initial=await r.json();if(!Object.keys(p.operations).length)assert.equal(initial.records.total,0,'no previous article auto-associated');ok(initial.capabilities.prepare&&!initial.capabilities.review,'warehouse prepares but cannot review');const input=(action,version,data)=>({action,version,record_key:p.record_key,data,reason:'Decisão individual QA do catálogo integrado, sem quantidade, movimento, preço ou lançamento.'});const data={code:'QA-CATALOGO-INTEGRADO',name:'Artigo fictício do catálogo integrado',unit:'UN'};
|
|
const command=async(name,s,body)=>{if(p.operations[name]?.result)return p.operations[name].result;let op=p.operations[name];if(!op){op=p.operations[name]={key:crypto.randomUUID(),input:body};save();}if(!op.cut){const out=await s.command('/api/stock-catalogue/preview',op.input,op.key);assert.equal(out.status,200,JSON.stringify(out.value));op.cut=out.value.preview;op.impact=out.value.impact;ok(op.impact.stock_movements===0&&op.impact.journal_entries===0,'preview '+name+' zero financial or physical movements');save();}const out=await s.command('/api/stock-catalogue/confirm',{preview:op.cut});assert([200,201].includes(out.status),JSON.stringify(out.value));op.result=out.value;save();const repeated=await s.command('/api/stock-catalogue/confirm',{preview:op.cut});ok(repeated.status===200&&repeated.value.replayed&&repeated.value.id===out.value.id,'repeat '+name+' same result without duplicate');return out.value;};
|
|
await command('prepare',prepare,input('prepare',0,data));r=await prepare.get('/api/stock-catalogue?view=items&search='+data.code);ok((await r.json()).records.total===0||Boolean(p.operations.review?.result),'preparation alone does not create canonical item');let denied=await prepare.command('/api/stock-catalogue/preview',input('review',1,{decision:'approved'}));ok(denied.status===403,'preparer self-review refused');const approved=await command('review',review,input('review',1,{decision:'approved'}));ok(approved.stock_item_id>0&&approved.version===2&&approved.state==='approved','distinct review approves exactly one article');r=await prepare.get('/api/stock-catalogue?view=items&search='+data.code+'&per_page=100');const list=await r.json();ok(list.records.total===1&&list.records.data[0].id===approved.stock_item_id&&list.records.data[0].unit==='UN','canonical article searchable with owned provenance');r=await review.get('/api/stock-catalogue?view=history&record_key='+p.record_key+'&per_page=25');const history=await r.json();ok(history.records.total===2&&history.records.data[0].host_actor_id===12&&history.records.data[1].host_actor_id===2,'two retained decisions with distinct actors reasons and dates');ok(history.records.data.every(e=>e.reason.length>=10&&e.created_at),'history evidence retained');
|
|
r=await prepare.get('/api/stock-read?view=items&search='+data.code+'&per_page=25');const shared=await r.json();ok(r.status===200&&shared.rows.length===1&&shared.rows[0].id===approved.stock_item_id,'same item visible to SELECT-only physical reader');r=await prepare.get('/api/stock-operations?view=items&search='+encodeURIComponent(data.name)+'&per_page=25');const selector=await r.json();ok(r.status===200&&selector.data.some(row=>row.id===approved.stock_item_id&&row.code===data.code),'existing operation selector sees approved item without movement');
|
|
const fresh=await session('purchasing:validator','stock');r=await fresh.get('/api/stock-catalogue/receipts/'+p.operations.prepare.key);const receipt=await r.json();ok(r.status===200&&receipt.result.id===p.operations.prepare.result.id&&receipt.operation_executed===false,'own receipt recovered in fresh context');r=await review.get('/api/stock-catalogue/receipts/'+p.operations.prepare.key);ok(r.status===404,'other actor receipt hidden');denied=await review.command('/api/stock-catalogue/preview',input('prepare',2,data));ok(denied.status===403,'reviewer cannot prepare');const existing=await prepare.command('/api/stock-catalogue/preview',{...input('prepare',0,data),record_key:crypto.randomUUID()});ok(existing.status===409,'duplicate code cannot attach pre-existing item');denied=await prepare.command('/api/stock-catalogue/preview',input('prepare',2,{...data,unit:'KG'}));ok(denied.status===422,'unit modification refused');r=await prepare.get('/api/stock-catalogue?tenant_id=2');ok(r.status===422,'scope override refused');const wrong=await session('purchasing:validator','purchasing');r=await wrong.get('/api/stock-catalogue');ok([403,409].includes(r.status),'other module refused');r=await prepare.get('/stock/catalogue',{headers:{Accept:'text/html'}});ok(r.status===200&&(await r.text()).includes('Artigos de Stock'),'actual catalogue screen served');p.passed=true;p.business_confirmations=2;p.new_items=1;p.native_movements=0;p.journal_entries=0;p.browser_visual_review=false;save();console.log(JSON.stringify({passed:true,checks:p.checks.length,proof:file}));
|