43 lines
3.0 KiB
PHP
43 lines
3.0 KiB
PHP
<?php
|
|
|
|
// Read-only verification in the restored QA database; never connect to the active target.
|
|
$directory = dirname(__DIR__).'/storage/app/private/investmoz-v2';
|
|
$proof = json_decode(file_get_contents($directory.'/territory-backup-proof.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
$plan = json_decode(file_get_contents($directory.'/opportunity-association-plan.json'), true, 512, JSON_THROW_ON_ERROR);
|
|
$target = $proof['restored_target'];
|
|
if (! $proof['restore_verified'] || ! preg_match('/^desk_restore_\d{8}_\d{6}_test$/', $target['database']) || (int) $target['port'] !== 55441) {
|
|
throw new RuntimeException('Isolated restored QA required.');
|
|
}
|
|
$pdo = new PDO('pgsql:host=127.0.0.1;port=55441;dbname='.$target['database'], 'postgres', '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
|
|
$pdo->exec('BEGIN TRANSACTION READ ONLY');
|
|
$identity = $pdo->query("SELECT current_database() AS database,inet_server_port() AS port,current_setting('transaction_read_only') AS read_only")->fetch(PDO::FETCH_ASSOC);
|
|
if ($identity['database'] !== $target['database'] || (int) $identity['port'] !== 55441 || $identity['read_only'] !== 'on' || $plan['associations'] !== [] || $plan['deletions'] !== []) {
|
|
throw new RuntimeException('QA identity or review-only plan mismatch.');
|
|
}
|
|
$checks = [];
|
|
foreach ($plan['records'] as $record) {
|
|
$statement = $pdo->prepare('SELECT reference,title,description FROM opportunities WHERE id=?');
|
|
$statement->execute([$record['id']]);
|
|
$current = $statement->fetch(PDO::FETCH_ASSOC);
|
|
if (! $current || $current['reference'] !== $record['reference'] || $current['title'] !== $record['title'] || $current['description'] !== $record['description']) {
|
|
throw new RuntimeException('QA record identity differs.');
|
|
}
|
|
foreach (['opportunity_projects' => 'project_id', 'opportunity_clusters' => 'cluster_id'] as $table => $column) {
|
|
$statement = $pdo->prepare('SELECT '.$column.' FROM '.$table.' WHERE opportunity_id=? ORDER BY '.$column);
|
|
$statement->execute([$record['id']]);
|
|
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);
|
|
$expected = $table === 'opportunity_projects' ? $record['existing_projects'] : $record['existing_clusters'];
|
|
if ($rows !== $expected) {
|
|
throw new RuntimeException('Existing relationships differ.');
|
|
}
|
|
}
|
|
if ($record['unequivocal_links'] !== [] || array_filter($record['options'], fn ($option) => $option['relation_proven'])) {
|
|
throw new RuntimeException('An option was promoted without a decision.');
|
|
}
|
|
$checks[] = ['id' => $record['id'], 'identity_matches' => true, 'existing_links_preserved' => true, 'no_option_applied' => true];
|
|
}
|
|
$pdo->exec('ROLLBACK');
|
|
$evidence = ['recorded_at' => gmdate('c'), 'identity' => $identity, 'checks' => $checks, 'plan_has_no_writes' => true, 'active_writes' => 0];
|
|
file_put_contents($directory.'/opportunity-association-qa.json', json_encode($evidence, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
|
echo json_encode($evidence, JSON_THROW_ON_ERROR).PHP_EOL;
|