11 lines
4.7 KiB
PHP
11 lines
4.7 KiB
PHP
<?php
|
|
$fingerprint=function($connection){$result=[];foreach($connection->select("SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename")as$table){if(!preg_match('/^[a-z0-9_]+$/D',$table->tablename))throw new LogicException('Unexpected fixture table');$name=$table->tablename;$row=$connection->selectOne('SELECT count(*) n,md5(coalesce(string_agg(row_to_json(t)::text,\'\' ORDER BY row_to_json(t)::text),\'\')) h FROM "'.$name.'" t');$result[$name]=[(int)$row->n,$row->h];}$result['sequences']=$connection->select("SELECT sequencename,last_value FROM pg_sequences WHERE schemaname='public' ORDER BY sequencename");return $result;};
|
|
$before=['host'=>$fingerprint(DB::connection()),'native'=>$fingerprint($db)];$saved=json_decode(file_get_contents($s['original_root'].'/integrated-result.json'),true);$id=$saved['source_ids']['request'];$order=$saved['source_ids']['order'];$unfiltered=$db->table('audit_logs')->where('requisition_id',$id)->orderByDesc('id')->limit(25)->get();$ok($unfiltered->contains(fn($v)=>str_starts_with($v->action,'invoice.')||$v->action==='sgf.invoice.command'||$v->action==='sgf.obligation.command'),'completed read fixture contains downstream payloads that old unfiltered histories would disclose');
|
|
$isRequest=fn($action)=>str_starts_with($action,'requisition.')||$action==='sgf.request.command';$isSourcing=fn($action)=>$isRequest($action)||str_starts_with($action,'quotation.')||str_starts_with($action,'award.')||$action==='sgf.sourcing.command';
|
|
$expectedRequest=$db->table('audit_logs')->where('requisition_id',$id)->get()->filter(fn($v)=>$isRequest($v->action))->sortByDesc('id')->take(25)->pluck('id')->values()->all();
|
|
foreach(['requester','actor','issuer']as$role){$r=$call($role,'/api/purchasing-requests/'.$id);$ok($r['status']===200&&array_column($r['data']['history'],'id')===$expectedRequest&&!array_filter($r['data']['history'],fn($v)=>!$isRequest($v['action'])),'request history is scoped before limit and retains exact own-stage evidence for '.$role);}
|
|
$r=$call('actor','/api/purchasing-sourcing/requests/'.$id);$ok($r['status']===200&&!array_filter($r['data']['history'],fn($v)=>!$isSourcing($v['action'])),'sourcing summary contains only upstream and sourcing-stage events');$total=$db->table('audit_logs')->where('requisition_id',$id)->get()->filter(fn($v)=>$isSourcing($v->action))->count();$pages=[];$r=$call('actor','/api/purchasing-sourcing/requests/'.$id.'/history?page=1');$ok($r['status']===200&&$r['data']['total']===$total&&$r['data']['per_page']===25,'sourcing pagination total counts only authorised stage history');$last=$r['data']['last_page'];$pages=$r['data']['data'];for($page=2;$page<=$last;$page++){$r=$call('actor','/api/purchasing-sourcing/requests/'.$id.'/history?page='.$page);if($r['status']!==200)throw new RuntimeException('Sourcing history page refused');array_push($pages,...$r['data']['data']);}$ok(count($pages)===$total&&count(array_unique(array_column($pages,'id')))===$total&&!array_filter($pages,fn($v)=>!$isSourcing($v['action'])),'all sourcing pages preserve unique own-stage evidence without financial payload events');
|
|
$r=$call('issuer','/api/purchasing-invoices/orders/'.$order.'/history');$ok($r['status']===200&&array_filter($r['data']['data'],fn($v)=>str_starts_with($v['action'],'invoice.')||$v['action']==='sgf.invoice.command'),'finance retains authorised invoice history in its own endpoint');$ok($call('actor','/api/purchasing-invoices/orders/'.$order.'/history')['status']===403&&$call('requester','/api/purchasing-sourcing/requests/'.$id.'/history')['status']===403,'stage history filtering does not grant access to another stage');
|
|
$r=$call('requester','/api/purchasing-dossiers/'.$id);$ok($r['status']===200&&!array_filter($r['data']['events']['data'],fn($v)=>!$isRequest($v['action'])),'request-only dossier remains scoped independently of inline histories');$ok($call('actor','/api/purchasing-sourcing/requests/'.$n['foreign_requisition'].'/history')['status']===403,'foreign request cannot expose sourcing history');
|
|
$after=['host'=>$fingerprint(DB::connection()),'native'=>$fingerprint($db)];$ok(json_encode($before)===json_encode($after),'142 original regression tables and all sequences remain unchanged by stage-history verification');file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'reused_proof'=>'purchasing-integrated-composition-20261103.json','financial_commands_repeated'=>0,'before_sha256'=>hash('sha256',json_encode($before)),'after_sha256'=>hash('sha256',json_encode($after)),'tables_checked'=>count($before['host'])+count($before['native'])-2,'new_right_names'=>[],'visual_review'=>false,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." purchasing continuity read checks passed.\n";
|