make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();$f=json_decode(file_get_contents($s['root'].'/fixture.json'),true);$n=json_decode(file_get_contents($s['root'].'/native-fixture.json'),true);config(['database.connections.sourcing_verify'=>$s['nativeConnection']]);$db=DB::connection('sourcing_verify');$checks=[];$ok=function($v,$name)use(&$checks,$s){if(!$v)throw new RuntimeException($name);$checks[]=$name;file_put_contents($s['root'].'/supplier-boundary-progress.json',json_encode($checks,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));};$servers=[];$clients=[];$sessions=[]; try{ foreach([0,1]as$i){$socket=stream_socket_server('tcp://127.0.0.1:0');$port=(int)substr(strrchr(stream_socket_get_name($socket,false),':'),1);fclose($socket);$server=new Symfony\Component\Process\Process([PHP_BINARY,'-S','127.0.0.1:'.$port,'-t',$s['app'].'/public',dirname(__DIR__).'/scripts/receipt-composition-http.php'],$s['app'],null,null,180);$server->start();$servers[]=$server;for($j=0;$j<80;$j++){if($socket=@fsockopen('127.0.0.1',$port,$errno,$error,.1)){fclose($socket);break;}usleep(50000);}$clients[]=new GuzzleHttp\Client(['base_uri'=>'http://127.0.0.1:'.$port,'http_errors'=>false,'timeout'=>25]);} $enter=function($role,$i=0,$query=[])use(&$sessions,$clients){$jar=new GuzzleHttp\Cookie\CookieJar;$r=$clients[$i]->get('/qa/enter/'.$role,['cookies'=>$jar,'query'=>$query]);$sessions[$role.$i]=['jar'=>$jar]+json_decode((string)$r->getBody(),true,32,JSON_THROW_ON_ERROR);};$options=function($role,$i=0,$data=null,$headers=[])use(&$sessions){$v=$sessions[$role.$i];$o=['cookies'=>$v['jar'],'headers'=>$headers+['Accept'=>'application/json','X-SGF-Context'=>$v['context'],'X-CSRF-TOKEN'=>$v['csrf']]];if($data!==null)$o['json']=$data;return $o;};$call=function($role,$path,$data=null,$i=0,$headers=[])use($clients,$options,$s){$r=$clients[$i]->request($data===null?'GET':'POST',$path,$options($role,$i,$data,$headers));$text=(string)$r->getBody();if($r->getStatusCode()>=500)file_put_contents($s['root'].'/supplier-last-500.txt',$text);return ['status'=>$r->getStatusCode(),'data'=>json_decode($text,true),'body'=>$text];};foreach(['actor','issuer','validator']as$role)$enter($role);$enter('actor',1);$base='/api/purchasing-suppliers'; $id=$db->table('suppliers')->where('nuit','444555666')->value('id');$original=(array)$db->table('suppliers')->where('id',$id)->first();$review=function()use($call,$base,$id){$state=$call('issuer',$base.'/'.$id);return $call('issuer',$base.'/'.$id.'/preview',['operation_key'=>(string)Str::uuid(),'revision'=>$state['data']['revision'],'action'=>'review','data'=>['status'=>'active','comment'=>'Conferência sintética da fronteira de revisão.','version'=>$state['data']['supplier']['version']]]);}; try{$db->table('suppliers')->where('id',$id)->update(['last_changed_by'=>$n['finance'],'blocked'=>false]);$r=$call('issuer',$base.'/'.$id);$ok($r['data']['capabilities']['review']===false&&$review()['status']===403,'native finance role cannot review a record last changed by the same identity');$db->table('suppliers')->where('id',$id)->update(['last_changed_by'=>$n['buyer'],'next_review'=>date('Y-m-d',strtotime('-1 day'))]);$ok($review()['status']===422,'expired diligence cannot be activated');}finally{$db->table('suppliers')->where('id',$id)->update($original);} $bindingFile=$s['root'].'/binding.json';$bindingBytes=file_get_contents($bindingFile);$profile=json_decode($bindingBytes,true);$key=implode(':',array_values($f['scope'])).':'.$f['actor'];$url='/api/purchasing-sourcing/requests/'.$n['requisition'];$quote=['supplier_id'=>$n['supplier_ids'][0],'reference'=>'QA-BOUNDARY','delivery_days'=>5,'valid_until'=>date('Y-m-d',strtotime('+30 days')),'payment_terms'=>'QA30dias','no_conflict'=>true,'items'=>[['requisition_item_id'=>$n['line'],'unit_price'=>'100','discount_type'=>'none','discount_value'=>'0']]];$qp=function($data)use($call,$url){$state=$call('actor',$url);return $call('actor',$url.'/preview',['operation_key'=>(string)Str::uuid(),'revision'=>$state['data']['revision'],'action'=>'quote','data'=>$data]);};$before=$db->table('quotations')->count();$p=$qp($quote);$ok($p['status']===200,'bound supplier still reaches the original preview after access guard extension'); try{$profile['bindings'][$key]['warehouse_ids'][]=99999;file_put_contents($bindingFile,json_encode($profile));$ok($call('actor',$url.'/confirm',['preview'=>$p['data']['preview']])['status']===409,'changed explicit binding invalidates prepared quotation');$profile=json_decode($bindingBytes,true);$profile['bindings'][$key]['supplier_ids']=array_values(array_diff($profile['bindings'][$key]['supplier_ids'],[$n['supplier_ids'][2]]));file_put_contents($bindingFile,json_encode($profile));$quote['supplier_id']=$n['supplier_ids'][2];$ok($qp($quote)['status']===404,'same-tenant supplier outside the company-unit binding cannot bypass selector through API');}finally{file_put_contents($bindingFile,$bindingBytes);} $ok($db->table('quotations')->count()===$before&&(array)$db->table('suppliers')->where('id',$id)->first()===$original,'boundary probes restore all injected fixture facts and commit no proposal');file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'new_boundary_only'=>true,'valid_34_checks_not_repeated'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." boundary checks passed.\n"; }finally{foreach($servers as$i=>$server){$server->stop(2);file_put_contents($s['root'].'/supplier-boundary-http-'.$i.'.log',$server->getOutput()."\n".$server->getErrorOutput());}}