make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); $checks=[]; $check=function(bool $v,string $name) use (&$checks) {if(!$v)throw new RuntimeException($name);$checks[]=$name;}; $check(!DB::getSchemaBuilder()->hasTable('users'),'new database; provider mount performed no migration'); $routes=Illuminate\Support\Facades\Route::getRoutes(); $check(count(array_filter(iterator_to_array($routes),fn($r)=>$r->uri()==='api/internal-requisitions'))===1,'provider mounts one canonical list route'); config(['internal_requisitions.qa_enabled'=>false]);$check(InternalRequisitionQa::mount()===null,'default off does not mount');config(['internal_requisitions.qa_enabled'=>true]); require $s['app'].'/packages/budget-control/tests/sgf-schema.php'; require $s['app'].'/packages/budget-control/tests/host-support.php'; $f=DB::transaction(fn()=>budgetQaFixture('sgf'));budgetQaCreate('sgf',$f,1000000,true);Schema::install(DB::connection()); $grant=fn($id,$p)=>DB::table('permission_grants')->insertOrIgnore($f['scope']+['user_id'=>$id,'permission'=>$p,'created_at'=>now(),'updated_at'=>now()]); foreach([$f['actor'],$f['validator']]as$id)foreach(['view','prepare','approve.department_head']as$p)$grant($id,'purchasing_requisitions.'.$p); $grant($f['actor'],'purchasing_requisitions.configure');$grant($f['validator'],'purchasing_budget.record'); file_put_contents($s['root'].'/fixture.json',json_encode($f,JSON_THROW_ON_ERROR)); $socket=stream_socket_server('tcp://127.0.0.1:0');$address=stream_socket_get_name($socket,false);fclose($socket);$port=(int)substr(strrchr($address,':'),1); $server=new Symfony\Component\Process\Process([PHP_BINARY,'-S','127.0.0.1:'.$port,dirname(__DIR__).'/scripts/requisition-composition-http.php'],$s['app'],null,null,90);$server->start(); try{ for($i=0;$i<80;$i++){if($sock=@fsockopen('127.0.0.1',$port,$e,$m,.1)){fclose($sock);break;}usleep(50000);} $client=new GuzzleHttp\Client(['base_uri'=>'http://127.0.0.1:'.$port,'http_errors'=>false,'timeout'=>20]);$sessions=[]; $enter=function($role,$module='purchasing')use($client,&$sessions){$jar=new GuzzleHttp\Cookie\CookieJar();$r=$client->get('/qa/enter/'.$role,['cookies'=>$jar,'query'=>['module'=>$module]]);if($r->getStatusCode()!==200)throw new RuntimeException('QA entry failed');$sessions[$role]=['jar'=>$jar]+json_decode((string)$r->getBody(),true,32,JSON_THROW_ON_ERROR);}; $call=function($role,$url,$body=null,$context=null)use($client,&$sessions){$v=$sessions[$role];$options=['cookies'=>$v['jar'],'headers'=>['Accept'=>'application/json','X-CSRF-TOKEN'=>$v['csrf'],'X-SGF-Context'=>$context??$v['context'],'X-Purchasing-Context'=>'procurement.internal']];if($body!==null)$options['json']=$body;$r=$client->request($body===null?'GET':'POST',$url,$options);$text=(string)$r->getBody();return ['status'=>$r->getStatusCode(),'data'=>json_decode($text,true),'body'=>$text];}; $enter('actor');$enter('validator'); $policy=['version'=>0,'reason'=>'Explicit isolated shared requisition composition.','enabled'=>true,'budget_required'=>true,'first_limit_minor'=>1000000,'second_limit_minor'=>2000000,'approval_includes_tax'=>true,'centres'=>[['id'=>$f['centre'],'department_ref'=>'OPER']],'members'=>[['user_id'=>$f['actor'],'department_ref'=>'OPER'],['user_id'=>$f['validator'],'department_ref'=>'OPER']]]; $r=$call('actor','/api/internal-requisitions/policy',$policy);$check($r['status']===200,'configure through provider routes'); $r=$call('actor','/purchasing/internal-requisitions');$check($r['status']===200&&str_contains($r['body'],'Compras internas'),'canonical workflow screen available'); $input=['operation_key'=>(string)Str::uuid(),'cost_center_id'=>$f['centre'],'priority'=>'normal','needed_by'=>now()->addWeek()->toDateString(),'justification'=>'Isolated integration of existing shared requisitions.','items'=>[['description'=>'Material QA','category'=>'Consumíveis','purchase_type'=>'consumable','quantity'=>'2.000','unit'=>'UN','unit_price'=>'50.00','tax_rate'=>'0.00']]]; $r=$call('actor','/api/internal-requisitions/save',$input);$check($r['status']===200,'create through shared adapter');$record=$r['data'];$id=$record['id']; $check($call('actor','/api/internal-requisitions/save',$input)['data']['id']===$id&&DB::table('ir_requests')->count()===1,'create replay no duplicate'); $submit=['operation_key'=>(string)Str::uuid(),'version'=>$record['version']];$r=$call('actor','/api/internal-requisitions/'.$id.'/submit',$submit);$check($r['status']===200,'submit reserves common budget'); $check($call('actor','/api/internal-requisitions/'.$id.'/submit',$submit)['status']===200&&DB::table('cb_budget_entries')->where('kind','reserve')->count()===1,'submission replay reserves once'); $decision=['operation_key'=>(string)Str::uuid(),'version'=>$r['data']['version'],'decision'=>'approved']; $check($call('actor','/api/internal-requisitions/'.$id.'/decide',$decision)['status']===403,'distinct approval retained'); $r=$call('validator','/api/internal-requisitions/'.$id.'/decide',$decision);$check($r['status']===200&&$r['data']['status']==='approved','independent approval through mounted routes'); $check($call('validator','/api/internal-requisitions/'.$id.'/decide',$decision)['status']===200&&DB::table('ir_commands')->count()===3,'approval replay no duplicate'); $check($call('actor','/api/internal-requisitions/'.$id.'/save',[...$input,'operation_key'=>(string)Str::uuid(),'version'=>1])['status']===409,'stale version refused'); $check($call('actor','/api/internal-requisitions',null,'stale-context')['status']===409,'read context token revalidated'); $enter('actor','commercial');$check(in_array($call('actor','/api/internal-requisitions')['status'],[403,409],true),'foreign active module refused');$enter('actor'); DB::table('permission_grants')->where($f['scope'])->where('user_id',$f['actor'])->where('permission','purchasing_requisitions.view')->delete(); $check($call('actor','/api/internal-requisitions')['status']===403,'revoked read permission refused'); $check(DB::table('cb_budget_entries')->where('kind','reserve')->sum('reserved_minor')==10000&&DB::table('cb_budget_consumptions')->count()===0&&DB::table('ir_requests')->count()===1,'one request and single100MZN reserve; no automatic consumption'); file_put_contents($s['root'].'/result.json',json_encode(['passed'=>true,'checks'=>$checks,'requests'=>1,'reservations'=>1,'reservation_minor'=>10000,'actual_http'=>true,'new_qa_database'=>true,'existing_shared_core'=>true,'destination_activation'=>false,'visual_review'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." directed composition checks passed.\n"; }finally{$server->stop(2);file_put_contents($s['root'].'/http-output.txt',$server->getOutput()."\n".$server->getErrorOutput());}