74 lines
3.1 KiB
PHP
74 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
use app\adminapi\service\AssistantSseTiming;
|
|
|
|
$checks = 0;
|
|
function r39TimingExpect(bool $condition, string $message): void
|
|
{
|
|
global $checks;
|
|
++$checks;
|
|
if (!$condition) {
|
|
throw new RuntimeException($message);
|
|
}
|
|
}
|
|
|
|
$timing = new AssistantSseTiming();
|
|
$codes = [
|
|
'TCM_FACT_STATE_CONFLICT', 'TCM_REPORT_INCOMPLETE', 'TASK_ANALYSIS_MISSING',
|
|
'STREAM_CONTENT_MISMATCH', 'UPSTREAM_CLEANUP_PENDING',
|
|
];
|
|
foreach (['upstream_done', 'sse_done'] as $phase) {
|
|
foreach ($codes as $code) {
|
|
$event = $timing->event($phase, [
|
|
'status' => 'error', 'error_code' => $code,
|
|
'patient_id' => 123456, 'patient_name' => 'PRIVATE_PATIENT',
|
|
'content' => 'PRIVATE_MODEL_BODY',
|
|
'clinical_fact_state_issues' => ['UNKNOWN_TO_POSITIVE:PRIVATE_SOURCE'],
|
|
'token' => 'PRIVATE_TOKEN',
|
|
]);
|
|
r39TimingExpect(($event['error_code'] ?? null) === $code,
|
|
'Existing failure code must survive at ' . $phase . ': ' . $code);
|
|
r39TimingExpect(($event['status'] ?? null) === 'error' && $event['trace_id'] === $timing->id(),
|
|
'Failure status and request trace must remain paired');
|
|
r39TimingExpect(array_keys($event) === ['trace_id', 'phase', 'request_elapsed_ms', 'status', 'error_code'],
|
|
'Diagnostic addition must not retain clinical text, identities or credentials');
|
|
}
|
|
}
|
|
foreach (['TCM_FACT_STATE_CONFLICT PRIVATE_MODEL_BODY', 'UNKNOWN_TO_POSITIVE:PRIVATE_SOURCE',
|
|
'UNKNOWN_ERROR', 'UPSTREAM_CLEANUP_PENDING\nPRIVATE_TOKEN', [], 123, null] as $bad) {
|
|
r39TimingExpect(!array_key_exists('error_code', $timing->event('sse_done', ['error_code' => $bad])),
|
|
'Unlisted or untyped diagnostic values must still be discarded');
|
|
}
|
|
|
|
// Exercise the actual buffered log channel used by the SSE timing writer.
|
|
$app = new \think\App(dirname(__DIR__) . '/');
|
|
$directory = sys_get_temp_dir() . '/zyt-r39-timing-' . bin2hex(random_bytes(8));
|
|
$app->config->set(['default' => 'r39', 'channels' => ['r39' => [
|
|
'type' => 'File', 'path' => $directory, 'single' => 'timing', 'realtime_write' => false,
|
|
]]], 'log');
|
|
$app->make('log');
|
|
$file = $directory . '/timing.log';
|
|
try {
|
|
foreach ($codes as $code) {
|
|
$timing->log('sse_done', ['status' => 'error', 'error_code' => $code,
|
|
'content' => 'PRIVATE_MODEL_BODY', 'token' => 'PRIVATE_TOKEN']);
|
|
}
|
|
$logged = is_file($file) ? (string) file_get_contents($file) : '';
|
|
foreach ($codes as $code) {
|
|
r39TimingExpect(str_contains($logged, '"error_code":"' . $code . '"'),
|
|
'SSE exit path must persist the concrete safe code: ' . $code);
|
|
}
|
|
r39TimingExpect(str_contains($logged, $timing->id())
|
|
&& !str_contains($logged, 'PRIVATE_MODEL_BODY') && !str_contains($logged, 'PRIVATE_TOKEN'),
|
|
'Persisted failure remains correlatable and contains no raw payload');
|
|
} finally {
|
|
if (is_file($file)) unlink($file);
|
|
if (is_dir($directory)) rmdir($directory);
|
|
}
|
|
|
|
echo 'R39 timing error code checks: ' . $checks . " passed\n";
|