make(Kernel::class)->bootstrap(); $db = DB::connection(); if (! $app->environment('testing') || $db->getDatabaseName() !== 'portal_services_test' || (string) $db->getConfig('port') !== '55441') { throw new RuntimeException('Isolated QA required'); } if ($db->selectOne("SELECT count(*) AS n FROM pg_namespace WHERE nspname='mne'")->n) { throw new RuntimeException('QA schema mne must be absent'); } $run = function (string $file) use ($db): void { $sql = file_get_contents(base_path('migrations/'.$file)); $sql = preg_replace('/^BEGIN;\s*$/m', '', $sql); $db->unprepared(preg_replace('/COMMIT;\s*$/', '', $sql)); }; $indexes = fn () => (int) $db->selectOne("SELECT count(*) AS n FROM pg_indexes WHERE schemaname='mne'")->n; $db->beginTransaction(); try { $run('001_mne_schema.sql'); $run('002_mne_existing_territories.sql'); $before = $indexes(); $run('003_mne_integrity.sql'); $after = $indexes(); $missing = $db->selectOne(<<<'SQL' SELECT count(*) AS n FROM pg_constraint c JOIN pg_namespace n ON n.oid=c.connamespace WHERE n.nspname='mne' AND c.contype='f' AND NOT EXISTS ( SELECT 1 FROM pg_index i JOIN pg_class ix ON ix.oid=i.indexrelid JOIN pg_am am ON am.oid=ix.relam WHERE i.indrelid=c.conrelid AND i.indisvalid AND i.indisready AND i.indpred IS NULL AND am.amname='btree' AND i.indnkeyatts >= cardinality(c.conkey) AND ARRAY(SELECT x FROM unnest(i.indkey::smallint[]) WITH ORDINALITY u(x,p) WHERE p <= cardinality(c.conkey) ORDER BY p)=c.conkey ) SQL); if ((int) $missing->n !== 0) { throw new RuntimeException('Foreign keys without index coverage'); } $run('003_mne_integrity.sql'); if ($indexes() !== $after) { throw new RuntimeException('Duplicate indexes'); } $db->insert("INSERT INTO mne.audit_events(action,subject_type,subject_id,correlation_id) VALUES ('qa','synthetic',gen_random_uuid(),gen_random_uuid())"); $rejections = 0; foreach (['audit_events', 'business_status_history', 'document_versions', 'negotiation_events'] as $table) { foreach (["UPDATE mne.$table SET id=id", "DELETE FROM mne.$table", "TRUNCATE mne.$table"] as $sql) { $db->statement('SAVEPOINT mutation'); $rejected = false; try { $db->unprepared($sql); } catch (QueryException $e) { $rejected = $e->getCode() === '23514'; } $db->statement('ROLLBACK TO SAVEPOINT mutation'); if (! $rejected) { throw new RuntimeException('Mutation was not rejected by evidence guard'); } $rejections++; } } $run('003_mne_integrity_down.sql'); if ($indexes() !== $before) { throw new RuntimeException('DOWN changed original indexes'); } $db->delete('DELETE FROM mne.audit_events'); $run('003_mne_integrity.sql'); if ($indexes() !== $after) { throw new RuntimeException('Reapply failed'); } echo json_encode(['passed' => true, 'original_indexes' => $before, 'added_indexes' => $after - $before, 'mutation_rejections' => $rejections, 'idempotency_and_down' => true]).PHP_EOL; } finally { $db->rollBack(); } if ($db->selectOne("SELECT count(*) AS n FROM pg_namespace WHERE nspname='mne'")->n) { throw new RuntimeException('QA cleanup failed'); }