79 lines
3.4 KiB
PHP
79 lines
3.4 KiB
PHP
<?php
|
|
|
|
require __DIR__.'/../vendor/autoload.php';
|
|
$app = require __DIR__.'/../bootstrap/app.php';
|
|
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
|
$db = Illuminate\Support\Facades\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 (Illuminate\Database\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');
|
|
}
|