93 lines
10 KiB
PHP
93 lines
10 KiB
PHP
<?php
|
|
$checks=0;$cases=[];
|
|
$assert=function(bool $ok,string $message)use(&$checks):void{$checks++;if(!$ok)throw new RuntimeException($message);};
|
|
$case=function(string $name,callable $test)use(&$checks,&$cases,$enter):void{
|
|
$n=$checks;DB::beginTransaction();try{$enter('read');$test();$cases[]=['name'=>$name,'assertions'=>$checks-$n,'passed'=>true];}
|
|
finally{while(DB::transactionLevel()>0)DB::rollBack();$enter('read');}echo 'Passed settlement page case: '.$name."\n";
|
|
};
|
|
$book=fn()=>$service->engine($enter('read'))->settlements();
|
|
$case('independent-disjoint-windows-and-full-history-amounts',function()use($book,$g,$assert):void{
|
|
$a=$book()->listingPage();$b=$book()->listingPage(['payments_page'=>2,'allocation_proposals_page'=>2,'allocations_page'=>2,'payment_events_page'=>2]);
|
|
foreach(['payments','proposals','allocations','events'] as $key){
|
|
$assert(count($a[$key])===25&&count($b[$key])===25,'Bounded window '.$key);
|
|
$assert(!array_intersect(array_column($a[$key],'id'),array_column($b[$key],'id')),'Disjoint window '.$key);
|
|
$assert(array_column($a[$key],'id')[24]>array_column($b[$key],'id')[0],'Descending stable window '.$key);
|
|
}
|
|
$focus=$book()->listingPage(['payment'=>$g['payment_id']]);
|
|
$assert(count($focus['payments'])===1&&$focus['payments'][0]['unallocated_minor']===41,'Full 60 allocations and old reversal included in amount');
|
|
$assert(count($focus['allocations'])===25&&$focus['pagination']['allocations']['total']===60&&!$focus['reversals'],'Old reversal is outside first allocation page');
|
|
$last=$book()->listingPage(['payment'=>$g['payment_id'],'allocations_page'=>3]);
|
|
$assert(count($last['allocations'])===10&&count($last['reversals'])===1&&$last['payments'][0]['unallocated_minor']===41,'Reversal is attached on its own allocation page without changing balance');
|
|
$assert($last['allocations'][0]->local_source_id!==null,'Allocation traces to scoped source');
|
|
$empty=$book()->listingPage(['payments_page'=>999,'allocation_proposals_page'=>999,'allocations_page'=>999,'payment_events_page'=>999]);
|
|
$assert(!$empty['payments']&&!$empty['proposals']&&!$empty['allocations']&&!$empty['events']&&!$empty['reversals'],'Out-of-range never falls back to full books');
|
|
});
|
|
$case('state-reference-and-focus-predicates-before-counts',function()use($book,$g,$assert):void{
|
|
foreach(['confirmed'=>334,'proposed'=>333,'rejected'=>333] as $state=>$count){
|
|
$data=$book()->listingPage(['payment_state'=>$state]);$assert($data['pagination']['payments']['total']===$count,'State count '.$state);
|
|
$assert(count(array_filter($data['payments'],fn($p)=>$p['state']!==$state))===0,'State rows '.$state);
|
|
if($state!=='confirmed')$assert(!$data['allocations']&&!$data['proposals'],'Children constrained by same parent predicate');
|
|
}
|
|
$one=$book()->listingPage(['payment_q'=>'SIM-PAGE-CLONE-0001']);
|
|
$assert($one['pagination']['payments']['total']===1&&count($one['allocations'])===1,'Reference filter applies to all child windows');
|
|
foreach(['%','_'] as $q)$assert($book()->listingPage(['payment_q'=>$q])['pagination']['payments']['total']===0,'Literal wildcard');
|
|
$no=$book()->listingPage(['payment'=>$g['payment_id'],'payment_state'=>'proposed']);
|
|
$assert(!$no['payments']&&!$no['allocations']&&!$no['proposals']&&!$no['events'],'Combined focus and state intersect');
|
|
foreach([['payments_page'=>0],['payment_per_page'=>26],['payment_events_page'=>[]],['payment'=>true],['payment'=>null],['payment_state'=>'paid'],['company_id'=>1]] as $bad){
|
|
$refused=false;try{$book()->listingPage($bad);}catch(InvalidArgumentException){$refused=true;}$assert($refused,'Invalid core filter refused');
|
|
}
|
|
});
|
|
$case('isolation-covers-roots-children-and-aggregate-sums',function()use($book,$g,$scope,$assert):void{
|
|
$foreign=$scope;$foreign['unit_id']+=100000;
|
|
$p=(array)DB::table('ps_payments')->where('id',$g['payment_id'])->first();unset($p['id']);
|
|
$id=DB::table('ps_payments')->insertGetId(array_replace($p,$foreign,['fact_key'=>hash('sha256','foreign-fact'),'evidence_key'=>hash('sha256','foreign-evidence')]));
|
|
$refused=false;try{$book()->listingPage(['payment'=>$id]);}catch(DomainException){$refused=true;}$assert($refused,'Foreign parent focus refused');
|
|
$a=(array)DB::table('ps_allocations')->where('payment_id',$g['payment_id'])->first();unset($a['id']);
|
|
DB::table('ps_allocations')->insert(array_replace($a,$foreign,['obligation_id'=>end($g['obligation_ids']),'amount_minor'=>9999]));
|
|
DB::table('ps_events')->insert($foreign+['payment_id'=>$g['payment_id'],'actor_id'=>1,'person'=>'foreign','kind'=>'qa.foreign','payload'=>'{}','created_at'=>now()]);
|
|
$focus=$book()->listingPage(['payment'=>$g['payment_id']]);
|
|
$assert($focus['payments'][0]['unallocated_minor']===41&&$focus['pagination']['allocations']['total']===60,'Foreign allocation not summed or counted');
|
|
$assert(!array_filter($focus['events'],fn($e)=>$e->kind==='qa.foreign'),'Foreign child event excluded');
|
|
$assert($book()->listingPage()['pagination']['payments']['total']===1000,'Foreign root excluded');
|
|
});
|
|
$case('read-authority-revoked-and-current-cycle-rechecked',function()use($book,$g,$f,$scope,$enter,$command,$assert):void{
|
|
$book()->listingPage(['payment'=>$g['payment_id']]);
|
|
DB::table('pa_access')->where($scope)->where(['user_id'=>$f['actors']['read'],'action'=>'payments_view'])->delete();
|
|
$refused=false;try{$book()->listingPage();}catch(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$refused=$e->getStatusCode()===403;}$assert($refused,'General document view grants no payment read authority');
|
|
DB::table('pa_access')->insert($scope+['user_id'=>$f['actors']['read'],'action'=>'payments_view','person'=>'sgf-history-person-'.$f['actors']['read']]);
|
|
$a=DB::table('ps_allocations')->where('payment_id',$g['payment_id'])->orderBy('id')->skip(1)->first();
|
|
$r=$command('prepare','reversal-prepare',['allocation_id'=>$a->id,'source_revision'=>$a->source_revision,'reason'=>'Synthetic current-state change after paged reading.']);
|
|
$command('validate','reversal-confirm',['proposal_id'=>$r['proposal_id'],'reason'=>'Independent current-state correction.']);
|
|
$assert($book()->listingPage(['payment'=>$g['payment_id']])['payments'][0]['unallocated_minor']===42,'Next read sees full current reversal history');
|
|
$stale=false;try{$command('validate','allocation-decide',['proposal_id'=>$g['pending_proposal_id'],'decision'=>'confirmed','reason'=>'Old page is not authority to confirm.']);}catch(DomainException $e){$stale=str_contains($e->getMessage(),'desactualizado');}
|
|
$assert($stale,'Old allocation cycle requires new conference');
|
|
$assert(!DB::table('ps_allocation_decisions')->where('proposal_id',$g['pending_proposal_id'])->exists(),'Stale confirmation created no decision');
|
|
});
|
|
$case('read-failure-rolls-back-and-next-read-recovers',function()use($book,$assert):void{
|
|
$armed=true;DB::listen(function($event)use(&$armed){if($armed&&str_contains($event->sql,'"ps_proposals"')){$armed=false;throw new RuntimeException('QA_READ_FAILURE');}});
|
|
$level=DB::transactionLevel();$failed=false;try{$book()->listingPage();}catch(RuntimeException $e){$failed=$e->getMessage()==='QA_READ_FAILURE';}
|
|
$assert($failed&&DB::transactionLevel()===$level,'Failed read unwinds its transaction');
|
|
$assert(count($book()->listingPage()['payments'])===25,'Next read recovers without repair or duplicated facts');
|
|
});
|
|
$case('controller-links-keep-independent-filters-and-source-focus',function()use($g,$f,$assert):void{
|
|
$u=App\Models\User::findOrFail($f['actors']['read']);$controller=app(App\Modules\Purchasing\SupplierPaymentsController::class);
|
|
$request=function(array $query)use($u){$r=Illuminate\Http\Request::create('/supplier-payments?'.http_build_query($query),'GET');$r->headers->set('Accept','application/json');$r->setUserResolver(fn()=>$u);$r->setLaravelSession(app('session')->driver());return $r;};
|
|
$query=['source'=>$g['source_ids'][0],'payment_state'=>'confirmed','payment_per_page'=>25,'payments_page'=>2];
|
|
$data=$controller->index($request($query))->getData(true);
|
|
$assert(count($data['obligations'])===1&&count($data['payments'])===25,'Independent document focus and payment window');
|
|
$assert($data['settlement_pagination']['payments']['total']===334&&$data['obligation_pagination']['total']===1,'Independent server totals');
|
|
$html=$controller->page($request($query))->getContent();
|
|
$assert(str_contains($html,'Paginação de pagamentos')&&str_contains($html,'Paginação de afectações confirmadas')&&str_contains($html,'name="source" value="'.$g['source_ids'][0].'"'),'Rendered bounded navigation and focus');
|
|
$links=[];preg_match_all('/href="([^"]+)"/',$html,$links);$kept=false;
|
|
foreach($links[1] as $link){parse_str(parse_url(html_entity_decode($link),PHP_URL_QUERY)??'',$q);if(($q['payments_page']??'')==='3'&&($q['source']??'')===(string)$g['source_ids'][0]&&($q['payment_state']??'')==='confirmed')$kept=true;}
|
|
$assert($kept,'Payment pager preserves source and state');
|
|
$focused=$controller->page($request(['payment'=>$g['payment_id'],'allocations_page'=>3]))->getContent();
|
|
$assert(str_contains($focused,'Conferir origem e reconhecimento')&&str_contains($focused,'Consultar outros pagamentos'),'Child evidence and exit available outside first page');
|
|
$bad=$controller->page($request(['payment_q'=>'<script>ledger_xss</script>']))->getContent();
|
|
$assert(!str_contains($bad,'<script>ledger_xss</script>')&&str_contains($bad,'<script>ledger_xss</script>'),'Search remains escaped');
|
|
});
|
|
$baseline=json_decode(file_get_contents($s['root'].'/settlement-volume.json'),true,64,JSON_THROW_ON_ERROR);
|
|
$assert($fingerprint()===$baseline['baseline_fingerprint'],'Cases changed retained financial rows');
|
|
file_put_contents($s['root'].'/settlement-cases.json',json_encode(['passed'=>true,'cases'=>$cases,'assertions'=>$checks,'baseline_fingerprint_equal'=>true,'test_writes_rolled_back'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
|
|
echo json_encode(['cases'=>count($cases),'assertions'=>$checks,'passed'=>true])."\n";
|