34 lines
5.2 KiB
PHP
34 lines
5.2 KiB
PHP
<?php
|
|
use IlluminateSupportFacadesDB;
|
|
use AppSharedInstallationCompositionSchema;
|
|
require __DIR__.'/sgf-composition-bootstrap.php';
|
|
$checks=[];$ok=function($v,$name)use(&$checks,$proof){if(!$v)throw new RuntimeException($name);$checks[]=$name;file_put_contents($proof.'/progress.json',json_encode($checks));};
|
|
$snapshot=function($db){$tables=[];foreach($db->select("SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename") as $r){$table=$r->tablename;if(!preg_match('/^[a-z_][a-z0-9_]*$/D',$table))throw new LogicException('Table');$tables[$table]=(array)$db->selectOne('SELECT count(*) rows,md5(coalesce(string_agg(row_to_json(t)::text,chr(10) ORDER BY row_to_json(t)::text),\'\')) hash FROM "'.$table.'" t');}
|
|
return hash('sha256',json_encode([$tables,$db->select("SELECT sequencename,last_value FROM pg_sequences WHERE schemaname='public' ORDER BY sequencename"),$db->select("SELECT conname,pg_get_constraintdef(oid) def FROM pg_constraint WHERE connamespace='public'::regnamespace ORDER BY conrelid,conname"),$db->select("SELECT tgrelid,tgname,pg_get_triggerdef(oid) def FROM pg_trigger WHERE NOT tgisinternal AND tgrelid IN (SELECT oid FROM pg_class WHERE relnamespace='public'::regnamespace) ORDER BY tgrelid,tgname")],JSON_THROW_ON_ERROR));};
|
|
$before=$snapshot($host);$grants=$host->table('permission_grants')->orderBy('id')->get()->toJson();$clients=$host->table('a_clients')->orderBy('id')->get()->toJson();
|
|
$installer=new CompositionSchema;
|
|
$result=$installer->run(false,function($db)use($ok,$grants,$clients,$installer){
|
|
config(['sgf_single_installation.enabled'=>true,'sgf_single_installation.schema_sha256'=>$installer->fingerprint()]);
|
|
App\Shared\Installation\CompositionRuntime::assert();$ok(true,'composed runtime accepts only the physically verified host and schema pin');
|
|
App\Shared\Testing\TestingDatabaseGuard::assertSafe();$ok(true,'existing domain guard accepts the verified composition');
|
|
App\Modules\Services\ServiceInvoicing::guard();$ok(true,'synthetic invoice guard operates on the composed host');
|
|
App\Modules\Workshop\WorkshopWorkflow::guard();App\Modules\Workshop\DirectCosts::guard();$ok(true,'workshop and direct costs guards operate on the same host');
|
|
foreach(['wo','sr','dc','ic'] as $prefix)$db->select('SELECT sgf_'.$prefix.'_environment()');$ok(true,'four SQL environment guards point to the exact composed QA identity');
|
|
config(['sgf_single_installation.schema_sha256'=>str_repeat('0',64)]);$refused=false;try{App\Shared\Installation\CompositionRuntime::assert();}catch(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$refused=$e->getStatusCode()===503;}
|
|
$ok($refused,'a stale schema pin blocks the runtime');config(['sgf_single_installation.schema_sha256'=>$installer->fingerprint()]);
|
|
foreach(['a_clients','a_services','a_service_drafts','a_billing_drafts','b_internal_confirmations','doc_records','document_preview_models','service_calculation_profiles','ca_proposals','sf_proposals','si_invoices','sr_records','wo_orders','dc_calculations','ic_results','ib_budgets','wb_proposals','wc_requests'] as $table)$ok($db->getSchemaBuilder()->hasTable($table),'same host contains '.$table);
|
|
$ok($db->table('permission_grants')->orderBy('id')->get()->toJson()===$grants,'existing grants retained without assigning new access');
|
|
$ok($db->table('a_clients')->orderBy('id')->get()->toJson()===$clients,'existing clients retain identifiers versions payloads and associations');
|
|
$old=(array)$db->table('permission_grants')->first();$old['id']=(int)$db->table('permission_grants')->max('id')+1000;$old['permission']='workshop.view';$old['unit_id']=$db->table('units')->value('id');
|
|
// The expanded CHECK accepts the existing right name, but this probe is rolled back.
|
|
$level=$db->transactionLevel();$db->beginTransaction();try{$db->table('permission_grants')->insert($old);$ok(true,'new module whitelist accepts scoped workshop right');}finally{while($db->transactionLevel()>$level)$db->rollBack();}
|
|
$ok($db->selectOne("SELECT count(*) count FROM pg_trigger WHERE NOT tgisinternal AND tgname IN ('a_clients_guard','a_history_scope','b_confirmation_immutable','si_invoices_test_only','wo_orders_immutable','wc_requests_immutable')")->count>=4,'module constraints and append-only guards installed');
|
|
return ['business_commands'=>0,'new_associations'=>0,'new_grants'=>0];
|
|
});
|
|
$ok(count($result['reused_tables'])===4,'four existing compatible A tables reused without rebuilding');
|
|
$ok($result['committed']===false,'rehearsal rolls back instead of installing');
|
|
$ok($snapshot($host)===$before,'all previous rows sequences constraints and triggers unchanged after rehearsal');
|
|
$app->instance('env','production');$refused=false;try{CompositionSchema::identity();}catch(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$refused=$e->getStatusCode()===403;}finally{$app->instance('env','budget-testing');}
|
|
$ok($refused,'installer rejects production environment');
|
|
$result=['passed'=>true,'checks'=>$checks,'schema'=>$result,'target8085_touched'=>false,'new_database'=>false,'new_server'=>false];file_put_contents($proof.'/result.json',json_encode($result,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo json_encode(['passed'=>true,'checks'=>count($checks),'proof'=>$proof.'/result.json']).PHP_EOL;
|