Files
SGF/scripts/workshop-billing-concurrency.php
T

17 lines
3.2 KiB
PHP

<?php
require __DIR__.'/../vendor/autoload.php';$app=require __DIR__.'/../bootstrap/app.php';$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
use App\Modules\Workshop\WorkshopBilling;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;
WorkshopBilling::guard();$base=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);$f=$base+$base['workshop_billing'];$scope=['tenant'=>$f['tenant'],'company'=>$f['company'],'unit'=>$f['unit'],'order'=>$f['order']];$key=fn()=>(string)Str::uuid();$checks=[];
$pair=function($one,$two)use($scope){$a=new Process([PHP_BINARY,'scripts/workshop-billing-worker.php','--env=testing'],base_path(),null,json_encode($one+$scope+['pause'=>true]));$b=new Process([PHP_BINARY,'scripts/workshop-billing-worker.php','--env=testing'],base_path(),null,json_encode($two+$scope));$a->start();if(!$a->waitUntil(fn($type,$v)=>str_contains($v,'LOCK_HELD')))throw new RuntimeException('Concurrent lock not observed.');$b->start();$a->wait();$b->wait();$out=[];foreach([$a,$b] as $p){if(!$p->isSuccessful())throw new RuntimeException('Worker failed.');$lines=array_values(array_filter(explode("\n",trim($p->getOutput())),fn($s)=>str_starts_with($s,'{')));$out[]=json_decode(end($lines),true,32,JSON_THROW_ON_ERROR);}return $out;};
$check=function($out,$replay){if($out[0]['status']!==200||$out[1]['status']!==($replay?200:409)||($replay&&(!$out[1]['result']['replayed']||$out[0]['result']['id']!==$out[1]['result']['id'])))throw new RuntimeException('Unexpected concurrent result.');return $out[0]['result']['id'];};
$p=['actor'=>$f['manager_id'],'method'=>'prepare','key'=>$key(),'input'=>\Tests\Support\WorkshopBillingFixture::proposal($f)];$id=$check($pair($p,$p),true);$checks['proposal_replay_one_version']=true;
$p['key']=$key();$p['input']['version']=2;$p['input']['previous_id']=$id;$other=$p;$other['key']=$key();$other['input']['lines'][0]['price']='210';$id=$check($pair($p,$other),false);$checks['competing_proposals_one_successor']=true;
$r=['actor'=>$f['reader_id'],'method'=>'review','key'=>$key(),'input'=>['id'=>$id,'version'=>2,'decision'=>'approved','reason'=>'Revisão comercial concorrente distinta.']];$check($pair($r,$r),true);$checks['review_replay_one_decision']=true;
$g=['actor'=>$f['manager_id'],'method'=>'generate','key'=>$key(),'input'=>['id'=>$id,'version'=>2]];$gid=$check($pair($g,$g),true);$checks['generation_replay_one_draft']=true;
$g['key']=$key();$out=$pair($g,$g);if($out[0]['status']!==409||$out[1]['status']!==409)throw new RuntimeException('Duplicate generation not blocked.');$checks['new_keys_cannot_duplicate_generation']=true;
if(DB::table('wb_generations')->where('order_id',$f['order'])->count()!==1||DB::table('wb_claims')->where('order_id',$f['order'])->count()!==2)throw new RuntimeException('Generation or origin claims duplicated.');
file_put_contents(base_path('docs/proofs/workshop-billing-concurrency.json'),json_encode(['policy'=>WorkshopBilling::POLICY,'database'=>'sgf_tempo_test','overlapping_processes_per_case'=>2,'checks'=>$checks,'one_generation_two_origin_claims'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Five overlapping commercial conversion cases passed; one generation and two origin claims.\n";