Files
zyt/server/tests/PrivateTcmR26ClockDomainIndependentTest.php
2026-10-27 14:03:35 +08:00

58 lines
3.7 KiB
PHP

<?php
declare(strict_types=1);
// Independent real-clock composition; synthetic control only, no app/bootstrap/network.
$root = dirname(__DIR__, 2);
require $root . '/server/app/adminapi/service/AssistantRequestControl.php';
require $root . '/app/artifacts/ai_concurrency/private_tcm_r26_wire_audit.php';
require $root . '/app/artifacts/ai_concurrency/private_tcm_r26_run_input_inspector.php';
$directory = sys_get_temp_dir() . '/zyt-r26-real-clock-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$checks = 0; $failures = [];
$expect = static function (bool $value, string $label) use (&$checks, &$failures): void {
++$checks; if (!$value) $failures[] = $label;
};
try {
$control = new \app\adminapi\service\AssistantRequestControl(90001, 11, '33333333-3333-4333-8333-333333333333', $directory);
$expect($control->begin(), 'synthetic control starts');
$submission = $control->beginSubmission('qwen', 'synthetic-clock-user');
$control->bindTask('qwen', 'synthetic-clock-user', 'synthetic-clock-task', 'synthetic-clock-run', $submission);
$control->markTerminal('synthetic-clock-task', 'stopped', 'synthetic-clock-run');
$lookups = 0;
$result = $control->closeSubmittedGeneration($submission, 'synthetic-clock-task', 'unconfirmed',
static function (array $identity, int $milliseconds) use (&$lookups): array {
++$lookups;
return ['ok' => true, 'workflow_run_id' => $identity['workflow_run_id'], 'status' => 'stopped'];
});
$expect(($result['confirmed'] ?? false) === true && $lookups === 1, 'real control independently confirms one synthetic exact lookup');
$files = glob($directory . '/*.json');
$state = json_decode(file_get_contents($files[0]), true, 64, JSON_THROW_ON_ERROR);
$record = array_values($state['completed_generations'])[0];
$helper = (new ReflectionClass(PrivateTcmR26WireAudit::class))->newInstanceWithoutConstructor();
$snapshot = (new ReflectionMethod(PrivateTcmR26WireAudit::class, 'snapshot'))->invoke($helper, $record, true);
$expect(is_array($snapshot) && $snapshot['independently_terminal'] === true, 'actual helper captures real control terminal record');
$validate = new ReflectionMethod(\PrivateTcmR26\RunInputInspector::class, 'lifecycle');
$bound = ['closed' => true, 'snapshot' => null];
$expect($validate->invoke(null, $snapshot, $bound) === true,
'real monotonic verification clock and wall observation clock must not be directly compared');
// Positive witness isolates the cross-domain comparisons, not a production mutation.
$sameClock = $snapshot;
$sameClock['verification_started_at'] = $sameClock['transfer_closed_at'];
$expect($validate->invoke(null, $sameClock, $bound) === true, 'otherwise identical same-clock synthetic witness accepted');
echo json_encode(['checks' => $checks, 'failed' => count($failures), 'failures' => $failures,
'real_control_snapshot_accepted' => $validate->invoke(null, $snapshot, $bound),
'wall_and_monotonic_origins_differ' => abs($snapshot['transfer_closed_at'] - $snapshot['verification_started_at']) > 1000,
'actual_network_or_model_calls' => 0], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";
} finally {
$base = realpath($directory);
if ($base === false || !str_starts_with(basename($base), 'zyt-r26-real-clock-')) throw new RuntimeException('Synthetic cleanup boundary unavailable');
foreach (glob($directory . '/*') as $file) {
if (dirname(realpath($file)) !== $base || is_dir($file)) throw new RuntimeException('Synthetic cleanup boundary mismatch');
unlink($file);
}
$lock = $directory . '/.gc.lock';
if (is_file($lock)) unlink($lock);
rmdir($directory);
}
exit($failures === [] ? 0 : 1);