26 lines
4.5 KiB
PHP
26 lines
4.5 KiB
PHP
<?php
|
|
use Illuminate\Support\Facades\DB;
|
|
$s=json_decode(file_get_contents(getenv('RECEIPT_COMPOSITION_STATE')),true);$out=$s['root'].'/composition-stock-demo.json';
|
|
if(!($s['stockComposition']??false)||is_file($out))throw new LogicException('Fresh stock demonstration required');
|
|
require $s['app'].'/vendor/autoload.php';$app=require $s['app'].'/bootstrap/app.php';$app->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.stock_demo'=>$s['nativeConnection']]);$db=DB::connection('stock_demo');
|
|
$checks=[];$ok=function($v,$name)use(&$checks){if(!$v)throw new RuntimeException($name);$checks[]=$name;};
|
|
$fingerprint=fn()=>hash('sha256',json_encode([$db->table('stock_items')->orderBy('id')->get(),$db->table('warehouses')->orderBy('id')->get(),$db->table('stock_movements')->orderBy('id')->get(),$db->table('audit_logs')->orderBy('id')->get(),DB::table('permission_grants')->orderBy('id')->get()]));$before=$fingerprint();
|
|
$ok($db->table('requisitions')->where('tenant_id',$n['tenant'])->count()===0&&$db->table('goods_receipts')->count()===0&&$db->table('stock_movements')->count()===0,'new demonstration begins without main operations or physical movements');
|
|
$client=new GuzzleHttp\Client(['base_uri'=>'http://127.0.0.1:8083','http_errors'=>false,'timeout'=>25]);$jar=new GuzzleHttp\Cookie\CookieJar;
|
|
$r=$client->get('/qa/enter/validator',['cookies'=>$jar,'query'=>['module'=>'stock']]);$v=json_decode((string)$r->getBody(),true);$options=['cookies'=>$jar,'headers'=>['Accept'=>'application/json','X-SGF-Context'=>$v['context']]];
|
|
$r=$client->get('/',$options);$ok($r->getStatusCode()===200&&str_contains((string)$r->getBody(),'id="app"'),'existing SGF React shell is served in explicit Stock context');
|
|
$manifest=json_decode(file_get_contents($s['app'].'/public/build/manifest.json'),true);$entry=$manifest['resources/js/app.jsx']['file'];$r=$client->get('/build/'.$entry);
|
|
$ok($r->getStatusCode()===200&&hash('sha256',(string)$r->getBody())===hash_file('sha256',$s['app'].'/public/build/'.$entry),'served existing React bundle matches the local manifest');
|
|
$r=$client->get('/api/stock-read',$options);$d=json_decode((string)$r->getBody(),true);
|
|
$ok($r->getStatusCode()===200&&$d['rows']===[]&&$d['status']==='available'&&!$d['capabilities']['write'],'same existing Stock reader observes the new empty source without inventing balances');
|
|
$r=$client->get('/api/session',$options);$d=json_decode((string)$r->getBody(),true);$ok($r->getStatusCode()===200&&str_contains(json_encode($d),'stock.view'),'existing session API exposes only the explicit QA stock grant');
|
|
// Hold a real host authority lock in a separate connection, then release and recover.
|
|
$c=DB::connection()->getConfig();config(['database.connections.stock_lock'=>$c]);$hold=DB::connection('stock_lock');$hold->beginTransaction();
|
|
try{$hold->table('permission_grants')->where($f['scope']+['user_id'=>$f['validator'],'permission'=>'stock.view'])->lockForUpdate()->first();$r=$client->get('/api/stock-read',$options);$ok($r->getStatusCode()===503,'host authority lock contention fails boundedly with unavailable');}finally{$hold->rollBack();}
|
|
$r=$client->get('/api/stock-read',$options);$ok($r->getStatusCode()===200,'read recovers after host authority lock release');
|
|
$controller=new App\Modules\Inventory\CompositionStockRead;$request=Illuminate\Http\Request::create('/api/stock-read');$ctx=app(App\Shared\Context\CompanyContext::class);$permissions=app(App\Modules\Identity\Services\Permissions::class);
|
|
foreach(['disabled','production']as$mode){$old=app()->environment();$flag=config('receipt_qa.stock_read_enabled');if($mode==='disabled')config(['receipt_qa.stock_read_enabled'=>false]);else app()->instance('env','production');$status=0;try{$controller->index($request,$ctx,$permissions);}catch(Symfony\Component\HttpKernel\Exception\HttpException $e){$status=$e->getStatusCode();}finally{config(['receipt_qa.stock_read_enabled'=>$flag]);app()->instance('env',$old);}$ok($status===($mode==='disabled'?404:403),'direct adapter refuses '.$mode.' before accessing a source');}
|
|
$ok($fingerprint()===$before,'demo reads preserve source metadata movements audit and grants');
|
|
file_put_contents($out,json_encode(['passed'=>true,'checks'=>$checks,'before'=>$before,'after'=>$fingerprint(),'direct_guard_probes'=>2,'visual_review'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." composed stock demo checks passed.\n";
|