Files
SGF/scripts/workshop-corrections-concurrency.php
2026-10-29 18:43:29 +02:00

21 lines
4.3 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\WorkshopCorrections;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Symfony\Component\Process\Process;
WorkshopCorrections::guard();if(DB::table('wo_orders')->exists())throw new RuntimeException('Empty synthetic workshop required; nothing deleted.');
$f=\Tests\Support\WorkshopCorrectionFixture::create();$base=['tenant'=>$f['tenant'],'company'=>$f['company'],'unit'=>$f['unit'],'generation'=>$f['generation']];$checks=[];$key=fn()=>(string)Str::uuid();
$pair=function($one,$two)use($base){$a=new Process([PHP_BINARY,'scripts/workshop-corrections-worker.php','--env=testing'],base_path(),null,json_encode($one+$base+['pause'=>true]));$b=new Process([PHP_BINARY,'scripts/workshop-corrections-worker.php','--env=testing'],base_path(),null,json_encode($two+$base));$a->start();if(!$a->waitUntil(fn($type,$v)=>str_contains($v,'LOCK_HELD')))throw new RuntimeException('Company 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,bool $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 outcome.');return $out[0]['result'];};
$lines=array_map(fn($id,$price)=>['entry_id'=>$id,'service_id'=>$f['service'],'service_version'=>1,'price'=>$price,'price_source'=>'WORKSHOP-SALES-SYNTHETIC-v2','description'=>'Descrição corrigida sintética.'],[$f['time'],$f['material']],['200','80']);
$p=['actor'=>$f['manager']->id,'method'=>'request','key'=>$key(),'input'=>['version'=>1,'previous_id'=>null,'kind'=>'replace','billing_id'=>$f['billing_draft'],'billing_version'=>1,'service_id'=>$f['service_draft'],'service_version'=>1,'lines'=>$lines,'reason'=>'Correcção sintética 500 para 510.']];$id=$check($pair($p,$p),true)['id'];$checks['request_replay_one_version']=true;
$p['key']=$key();$p['input']['version']=2;$p['input']['previous_id']=$id;$cancel=$p;$cancel['key']=$key();$cancel['input']['kind']='cancel';$cancel['input']['lines']=[];$id=$check($pair($p,$cancel),false)['id'];$checks['replacement_vs_cancellation_one_request_successor']=true;
$r=['actor'=>$f['reader']->id,'method'=>'review','key'=>$key(),'input'=>['id'=>$id,'version'=>2,'decision'=>'approved','reason'=>'Revisão distinta.']];$check($pair($r,$r),true);$checks['review_replay_one_decision']=true;
$a=['actor'=>$f['manager']->id,'method'=>'apply','key'=>$key(),'input'=>['id'=>$id,'version'=>2]];$applied=$check($pair($a,$a),true);$checks['apply_replay_one_successor_pair']=true;
$p['key']=$key();$p['input']['version']=3;$p['input']['previous_id']=$id;$p['input']['kind']='cancel';$p['input']['lines']=[];$p['input']['billing_id']=$applied['billing_draft_id'];$p['input']['service_id']=$applied['service_draft_id'];$id=$check($pair($p,$p),true)['id'];
$r['key']=$key();$r['input']['id']=$id;$r['input']['version']=3;$other=$r;$other['key']=$key();$other['input']['decision']='refused';$check($pair($r,$other),false);$checks['competing_reviews_one_decision']=true;
$a['key']=$key();$a['input']=['id'=>$id,'version'=>3];$b1=['actor'=>$f['manager']->id,'method'=>'b1','key'=>$key(),'input'=>['billing_id'=>$applied['billing_draft_id']]];$check($pair($a,$b1),false);$checks['cancellation_blocks_overlapping_b1']=true;
if(DB::table('wb_claims')->count()!==2||DB::table('wc_applications')->count()!==2||DB::table('b_internal_confirmations')->exists())throw new RuntimeException('History/claims or confirmation differs.');
file_put_contents(base_path('docs/proofs/workshop-corrections-concurrency.json'),json_encode(['policy'=>WorkshopCorrections::POLICY,'database'=>'sgf_tempo_test','processes_per_case'=>2,'checks'=>$checks,'source_claims_unchanged'=>2,'applications'=>2,'b1_created'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo "Six overlapping correction cases passed: one replacement, one cancellation, two original source claims, no B1.\n";