This commit is contained in:
Your Name
2026-10-27 14:03:35 +08:00
parent 7986e4baf5
commit f9cbd42113
417 changed files with 71876 additions and 5344 deletions
+324
View File
@@ -0,0 +1,324 @@
<?php
declare(strict_types=1);
// Standalone synthetic test: no framework bootstrap, runtime config, socket,
// credential, model, database, request-control file or external service is used.
namespace app\common\service {
function hrtime(bool $asNumber = false) { return $GLOBALS['runStatusStub']['now_ns']; }
function config(string $name) {
$stub =& $GLOBALS['runStatusStub'];
++$stub['config_calls'];
if ($name !== 'prescription_ai') throw new \RuntimeException('Unexpected config read');
$stub['now_ns'] += $stub['config_delay_ms'] * 1000000;
if ($stub['config_throw']) throw new \RuntimeException($stub['sensitive']);
return $stub['config'];
}
function curl_init() {
$stub =& $GLOBALS['runStatusStub'];
++$stub['init_calls'];
$stub['now_ns'] += $stub['init_delay_ms'] * 1000000;
if ($stub['init_throw']) throw new \RuntimeException($stub['sensitive']);
if ($stub['init_false']) return false;
return (object) ['errno' => $stub['errno']];
}
function curl_setopt_array(\stdClass $handle, array $options): bool {
$stub =& $GLOBALS['runStatusStub'];
++$stub['setopt_calls'];
$stub['options'] = $handle->options = $options;
$stub['now_ns'] += $stub['setopt_delay_ms'] * 1000000;
if ($stub['setopt_throw']) throw new \RuntimeException($stub['sensitive']);
return !$stub['setopt_false'];
}
function curl_exec(\stdClass $handle): bool {
$stub =& $GLOBALS['runStatusStub'];
++$stub['exec_calls'];
$stub['now_ns'] += $stub['exec_delay_ms'] * 1000000;
if ($stub['exec_throw']) throw new \RuntimeException($stub['sensitive']);
if ($stub['progress']) {
$option = defined('CURLOPT_XFERINFOFUNCTION') ? CURLOPT_XFERINFOFUNCTION : CURLOPT_PROGRESSFUNCTION;
$stub['progress_result'] = ($handle->options[$option])($handle, 0, 0, 0, 0);
if ($stub['progress_result'] !== 0) { $handle->errno = CURLE_ABORTED_BY_CALLBACK; return false; }
}
$chunks = $stub['chunks'] ?? [$stub['body']];
foreach ($chunks as $index => $chunk) {
$stub['now_ns'] += ($stub['chunk_delay_ms'][$index] ?? 0) * 1000000;
$written = ($handle->options[CURLOPT_WRITEFUNCTION])($handle, $chunk);
$stub['write_results'][] = $written;
if ($written !== strlen($chunk)) { $handle->errno = CURLE_WRITE_ERROR; return false; }
}
$stub['now_ns'] += $stub['after_body_ms'] * 1000000;
return !$stub['exec_false'];
}
function curl_errno(\stdClass $handle): int { return $handle->errno; }
function curl_getinfo(\stdClass $handle, int $option): int {
if ($option !== CURLINFO_RESPONSE_CODE) throw new \RuntimeException('Unexpected cURL info read');
$GLOBALS['runStatusStub']['now_ns'] += $GLOBALS['runStatusStub']['info_delay_ms'] * 1000000;
return $GLOBALS['runStatusStub']['http'];
}
function curl_error(\stdClass $handle): string {
++$GLOBALS['runStatusStub']['error_reads'];
throw new \RuntimeException('Raw cURL error must never be read');
}
function curl_close(\stdClass $handle): void {
++$GLOBALS['runStatusStub']['close_calls'];
$GLOBALS['runStatusStub']['now_ns'] += $GLOBALS['runStatusStub']['close_delay_ms'] * 1000000;
if ($GLOBALS['runStatusStub']['close_throw']) throw new \RuntimeException($GLOBALS['runStatusStub']['sensitive']);
}
}
namespace {
require dirname(__DIR__) . '/app/common/service/DifyWorkflowRunStatus.php';
use app\common\service\DifyWorkflowRunStatus;
$checks = 0;
$cases = 0;
$identity = ['profile' => 'qwen', 'user' => 'synthetic-private-user', 'task_id' => 'synthetic-task-001',
'workflow_run_id' => 'synthetic-run-001', 'generation' => 1];
function runStatusExpect(bool $condition, string $message): void {
++$GLOBALS['checks'];
if (!$condition) throw new RuntimeException($message);
}
function runStatusReset(array $changes = []): void {
$GLOBALS['runStatusStub'] = array_replace([
'now_ns' => 10000000000,
'config' => ['enable' => true, 'base_url' => 'https://status-synthetic.invalid/v1/chat-messages',
'models' => ['qwen' => ['api_key' => 'qwen-fixture-only-key'], 'openai' => ['api_key' => 'openai-fixture-only-key']]],
'sensitive' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL',
'body' => json_encode(['id' => 'synthetic-run-001', 'status' => 'succeeded',
'inputs' => ['patient' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL'],
'outputs' => ['answer' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL'],
'error' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL'], JSON_THROW_ON_ERROR),
'http' => 200, 'errno' => 0, 'chunks' => null, 'write_results' => [],
'config_calls' => 0, 'init_calls' => 0, 'setopt_calls' => 0, 'exec_calls' => 0,
'close_calls' => 0, 'error_reads' => 0, 'options' => [],
'config_delay_ms' => 0, 'init_delay_ms' => 0, 'setopt_delay_ms' => 0, 'exec_delay_ms' => 0,
'chunk_delay_ms' => [], 'after_body_ms' => 0, 'info_delay_ms' => 0, 'close_delay_ms' => 0,
'config_throw' => false, 'init_false' => false, 'init_throw' => false,
'setopt_false' => false, 'setopt_throw' => false, 'exec_false' => false,
'exec_throw' => false, 'close_throw' => false, 'progress' => false,
], $changes);
}
function runStatusLookup(?array $identity = null, int $timeoutMs = 1000): array {
++$GLOBALS['cases'];
ob_start();
try { $result = DifyWorkflowRunStatus::lookup($identity ?? $GLOBALS['identity'], $timeoutMs); }
finally { $output = ob_get_clean(); }
runStatusExpect($output === '', 'adapter emits no body, error or private data');
$stub = $GLOBALS['runStatusStub'];
runStatusExpect($stub['error_reads'] === 0, 'raw provider transport error is never read');
runStatusExpect($stub['exec_calls'] <= 1 && $stub['init_calls'] <= 1, 'no retry or endpoint/protocol fallback');
if ($stub['init_calls'] === 1 && !$stub['init_false'] && !$stub['init_throw']) {
runStatusExpect($stub['close_calls'] === 1, 'every initialized handle closes exactly once');
}
$encoded = json_encode($result, JSON_THROW_ON_ERROR);
foreach ([$stub['sensitive'], 'status-synthetic.invalid', 'fixture-only-key', 'synthetic-private-user',
'synthetic-task-001', 'inputs', 'outputs', 'Authorization', 'api_key'] as $private) {
runStatusExpect(!str_contains($encoded, $private), 'result excludes sensitive or unrelated metadata');
}
if (isset($stub['options'][CURLOPT_WRITEFUNCTION])) {
$variables = (new ReflectionFunction($stub['options'][CURLOPT_WRITEFUNCTION]))->getStaticVariables();
runStatusExpect($variables['body'] === '', 'bounded sensitive response buffer is discarded on every result');
}
return $result;
}
function runStatusError(array $result, string $code): void {
runStatusExpect($result === ['ok' => false, 'error_code' => $code], 'failure returns only expected static code');
}
function runStatusSuccess(array $result, string $status = 'succeeded', string $run = 'synthetic-run-001'): void {
runStatusExpect($result === ['ok' => true, 'workflow_run_id' => $run, 'status' => $status], 'success returns only exact private run/status');
}
function runStatusTransportOptions(int $timeoutMs = 1000, int $protocol = CURLPROTO_HTTPS): void {
$stub = $GLOBALS['runStatusStub']; $options = $stub['options'];
runStatusExpect($stub['config_calls'] === 1 && $stub['exec_calls'] === 1, 'one server profile read and one synthetic request');
foreach ([CURLOPT_HTTPGET => true, CURLOPT_RETURNTRANSFER => false, CURLOPT_HEADER => false, CURLOPT_VERBOSE => false,
CURLOPT_FOLLOWLOCATION => false, CURLOPT_MAXREDIRS => 0, CURLOPT_PROXY => '', CURLOPT_NOPROXY => '*',
CURLOPT_PROTOCOLS => $protocol, CURLOPT_REDIR_PROTOCOLS => $protocol, CURLOPT_NETRC => CURL_NETRC_IGNORED,
CURLOPT_UNRESTRICTED_AUTH => false, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_TIMEOUT_MS => $timeoutMs, CURLOPT_CONNECTTIMEOUT_MS => $timeoutMs, CURLOPT_NOSIGNAL => true,
CURLOPT_NOPROGRESS => false] as $name => $expected) {
runStatusExpect(($options[$name] ?? null) === $expected, 'GET/direct/TLS/protocol/deadline option retained');
}
foreach ([CURLOPT_POST, CURLOPT_POSTFIELDS, CURLOPT_CUSTOMREQUEST, CURLOPT_USERPWD] as $forbidden) {
runStatusExpect(!array_key_exists($forbidden, $options), 'no inference/stop/body/alternate credential option');
}
}
foreach (['qwen', 'openai'] as $profile) {
runStatusReset(); $target = array_replace($identity, ['profile' => $profile]);
runStatusSuccess(runStatusLookup($target)); runStatusTransportOptions();
runStatusExpect($runStatusStub['options'][CURLOPT_URL] === 'https://status-synthetic.invalid/v1/workflows/run/synthetic-run-001', 'configured chat endpoint becomes exact run GET');
runStatusExpect($runStatusStub['options'][CURLOPT_HTTPHEADER] === ['Authorization: Bearer ' . $profile . '-fixture-only-key', 'Accept: application/json'], 'only original configured profile key is used');
}
foreach (['https://status-synthetic.invalid', 'https://status-synthetic.invalid/',
'https://status-synthetic.invalid/v1', 'https://status-synthetic.invalid/v1/',
'https://status-synthetic.invalid/v1/chat-messages/', 'https://status-synthetic.invalid/prefix/v1/chat-messages',
'https://status-synthetic.invalid:8443/v1/chat-messages', 'http://status-synthetic.invalid/v1/chat-messages'] as $base) {
runStatusReset(); $runStatusStub['config']['base_url'] = $base;
runStatusSuccess(runStatusLookup());
$root = rtrim($base, '/');
if (str_ends_with($root, '/chat-messages')) $root = substr($root, 0, -strlen('/chat-messages'));
runStatusExpect($runStatusStub['options'][CURLOPT_URL] === $root . '/workflows/run/synthetic-run-001', 'one configured root is preserved');
runStatusTransportOptions(1000, str_starts_with($base, 'https:') ? CURLPROTO_HTTPS : CURLPROTO_HTTP);
}
runStatusReset();
runStatusSuccess(runStatusLookup($identity + ['url' => 'https://untrusted.invalid/', 'base_url' => 'https://untrusted.invalid/',
'api_key' => 'untrusted-key', 'ca_file' => 'untrusted-file']));
runStatusExpect($runStatusStub['options'][CURLOPT_URL] === 'https://status-synthetic.invalid/v1/workflows/run/synthetic-run-001'
&& !str_contains(implode(',', $runStatusStub['options'][CURLOPT_HTTPHEADER]), 'untrusted')
&& !array_key_exists(CURLOPT_CAINFO, $runStatusStub['options']), 'identity cannot select a destination, credential or CA file');
runStatusReset(); runStatusSuccess(runStatusLookup(array_replace($identity, ['user' => ''])));
foreach (['task_id', 'workflow_run_id'] as $field) {
foreach (['a', str_repeat('x', 128), 'AZaz09_-'] as $valid) {
runStatusReset(); $target = array_replace($identity, [$field => $valid]);
if ($field === 'workflow_run_id') $runStatusStub['body'] = json_encode(['id' => $valid, 'status' => 'stopped']);
runStatusSuccess(runStatusLookup($target), $field === 'workflow_run_id' ? 'stopped' : 'succeeded', $target['workflow_run_id']);
}
}
$invalidIdentities = [[]];
foreach (array_keys($identity) as $field) { $missing = $identity; unset($missing[$field]); $invalidIdentities[] = $missing; }
foreach (['task_id', 'workflow_run_id'] as $field) foreach (['', str_repeat('x', 129), 'a/b', '../run', 'https://untrusted.invalid',
'run%2Fother', "run\n", "run\0", 'run?x', 'run#x', 123, [], new stdClass()] as $bad) {
$invalidIdentities[] = array_replace($identity, [$field => $bad]);
}
foreach (['', 'QWEN', 'other', null, []] as $bad) $invalidIdentities[] = array_replace($identity, ['profile' => $bad]);
foreach ([0, -1, '1', 1.0, true, null] as $bad) $invalidIdentities[] = array_replace($identity, ['generation' => $bad]);
foreach ([str_repeat('x', 129), "user\n", "user\0", 1, [], null] as $bad) $invalidIdentities[] = array_replace($identity, ['user' => $bad]);
foreach ($invalidIdentities as $target) {
runStatusReset(); runStatusError(runStatusLookup($target), 'RUN_STATUS_IDENTITY_INVALID');
runStatusExpect($runStatusStub['config_calls'] === 0 && $runStatusStub['init_calls'] === 0, 'invalid original identity performs no config or transport work');
}
foreach ([0, -1, PHP_INT_MIN] as $timeout) {
runStatusReset(); runStatusError(runStatusLookup(null, $timeout), 'RUN_STATUS_TIMEOUT');
runStatusExpect($runStatusStub['config_calls'] === 0 && $runStatusStub['init_calls'] === 0, 'expired budget performs no config or transport work');
}
foreach ([null, false, 'bad', new stdClass()] as $bad) {
runStatusReset(['config' => $bad]); runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_INVALID');
runStatusExpect($runStatusStub['init_calls'] === 0, 'invalid config performs no transport');
}
foreach ([false, null, 0, 'true'] as $bad) {
runStatusReset(); $runStatusStub['config']['enable'] = $bad;
runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_DISABLED');
runStatusExpect($runStatusStub['init_calls'] === 0, 'disabled config performs no transport');
}
foreach ([[], null, 'bad', new stdClass(), ['qwen' => null], ['qwen' => 'bad'], ['qwen' => new stdClass()]] as $bad) {
runStatusReset(); $runStatusStub['config']['models'] = $bad;
runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_INVALID');
runStatusExpect($runStatusStub['init_calls'] === 0, 'profile has no fallback key');
}
foreach (['', "bad\r\nAuthorization: leak", ' padded ', "bad\tkey", "bad\0key", str_repeat('k', 4097), null, 123, [], new stdClass()] as $bad) {
runStatusReset(); $runStatusStub['config']['models']['qwen']['api_key'] = $bad;
runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_INVALID');
runStatusExpect($runStatusStub['init_calls'] === 0, 'malformed credential fails before cURL');
}
foreach (['', 'https://status-synthetic.invalid/v1/chat/completions', 'https://status-synthetic.invalid/v1/chat/completions/',
'https://status-synthetic.invalid/v1/CHAT/COMPLETIONS', 'https://status-synthetic.invalid/v1/chat/completions/other',
'https://status-synthetic.invalid/v1?x=1', 'https://status-synthetic.invalid/v1?',
'https://status-synthetic.invalid/v1#fragment', 'https://status-synthetic.invalid/v1#',
'https://name@status-synthetic.invalid/v1', 'https://name:pass@status-synthetic.invalid/v1',
'ftp://status-synthetic.invalid/v1', 'file:///tmp/status', '//status-synthetic.invalid/v1',
'http://', 'https://status-synthetic.invalid:999999/v1', 'https://status-synthetic.invalid/v1%2fchat-messages',
'https://status-synthetic.invalid/v1/../other', 'https://status-synthetic.invalid/v1\\other',
"https://status-synthetic.invalid/v1\n", "https://status-synthetic.invalid/v1\0", ' https://status-synthetic.invalid/v1',
'https://status-synthetic.invalid/v1 ', str_repeat('x', 2049), null, 1, [], new stdClass()] as $bad) {
runStatusReset(); $runStatusStub['config']['base_url'] = $bad;
runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_INVALID');
runStatusExpect($runStatusStub['init_calls'] === 0, 'invalid/non-Dify destination performs no transport');
}
foreach ([['ca_file' => __DIR__ . '/definitely-absent-status-ca.pem'], ['ca_file' => __DIR__], ['ca_file' => "bad\0path"],
['ca_file' => []], ['ca_file' => null], 'bad'] as $bad) {
runStatusReset(); $runStatusStub['config']['required_attachment_upload'] = $bad;
runStatusError(runStatusLookup(), 'RUN_STATUS_CONFIG_INVALID');
runStatusExpect($runStatusStub['init_calls'] === 0, 'invalid server CA fails before transport');
}
runStatusReset(); $runStatusStub['config']['required_attachment_upload'] = ['ca_file' => __FILE__];
runStatusSuccess(runStatusLookup()); runStatusTransportOptions();
runStatusExpect($runStatusStub['options'][CURLOPT_CAINFO] === __FILE__, 'existing server CA option is forwarded with peer/hostname verification');
foreach (['scheduled', 'running', 'succeeded', 'failed', 'stopped', 'partial-succeeded', 'paused'] as $status) {
runStatusReset(['body' => json_encode(['id' => $identity['workflow_run_id'], 'status' => $status,
'task_id' => $identity['task_id'], 'user' => $identity['user'], 'profile' => $identity['profile'],
'workflow_run_id' => $identity['workflow_run_id'], 'inputs' => ['secret' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL']])]);
runStatusSuccess(runStatusLookup(), $status);
}
foreach (['', 'cancelled', 'Canceled', 'success', 'SUCCEEDED', 'unknown', 'stopped ', null, 1, [], ['status' => 'stopped']] as $bad) {
runStatusReset(['body' => json_encode(['id' => $identity['workflow_run_id'], 'status' => $bad, 'error' => 'PRIVATE-PATIENT-OUTPUT-ERROR-CREDENTIAL'])]);
runStatusError(runStatusLookup(), 'RUN_STATUS_STATUS_UNCONFIRMED');
}
runStatusReset(['body' => json_encode(['id' => $identity['workflow_run_id'], 'result' => 'success'])]);
runStatusError(runStatusLookup(), 'RUN_STATUS_STATUS_UNCONFIRMED');
foreach (['', 'synthetic-run-other', strtoupper($identity['workflow_run_id']), null, [], 123] as $bad) {
runStatusReset(['body' => json_encode(['id' => $bad, 'status' => 'stopped'])]);
runStatusError(runStatusLookup(), 'RUN_STATUS_IDENTITY_MISMATCH');
}
foreach (['{}', '[]', 'null', 'true', '123', '"succeeded"', '{"status":"stopped"}',
'{"workflow_id":"synthetic-run-001","status":"stopped"}',
'{"data":{"id":"synthetic-run-001","status":"stopped"}}'] as $body) {
runStatusReset(['body' => $body]); runStatusError(runStatusLookup(), 'RUN_STATUS_IDENTITY_MISMATCH');
}
foreach (['task_id', 'user', 'profile', 'workflow_run_id'] as $field) foreach (['foreign', null, []] as $bad) {
runStatusReset(['body' => json_encode(['id' => $identity['workflow_run_id'], 'status' => 'stopped', $field => $bad])]);
runStatusError(runStatusLookup(), 'RUN_STATUS_IDENTITY_MISMATCH');
}
foreach (['', '{broken', "{\"id\":\"\xff\"}", str_repeat('[', 65) . '0' . str_repeat(']', 65)] as $body) {
runStatusReset(['body' => $body]); runStatusError(runStatusLookup(), 'RUN_STATUS_RESPONSE_INVALID');
}
foreach ([0, 201, 204, 301, 302, 303, 307, 308, 400, 401, 403, 404, 429, 500, 503] as $http) {
runStatusReset(['http' => $http]); runStatusError(runStatusLookup(), 'RUN_STATUS_HTTP_ERROR');
runStatusTransportOptions();
}
$peerVerificationError = defined('CURLE_PEER_FAILED_VERIFICATION') ? constant('CURLE_PEER_FAILED_VERIFICATION') : CURLE_SSL_CACERT;
foreach ([CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_CONNECT, CURLE_SSL_CONNECT_ERROR,
$peerVerificationError, CURLE_SSL_CACERT_BADFILE, CURLE_RECV_ERROR, CURLE_WRITE_ERROR] as $errno) {
runStatusReset(['errno' => $errno]); runStatusError(runStatusLookup(), 'RUN_STATUS_TRANSPORT_ERROR');
runStatusTransportOptions();
}
runStatusReset(['errno' => CURLE_OPERATION_TIMEDOUT, 'http' => 200]);
runStatusError(runStatusLookup(), 'RUN_STATUS_TIMEOUT');
foreach (['config_throw' => 'RUN_STATUS_CONFIG_UNAVAILABLE', 'init_false' => 'RUN_STATUS_TRANSPORT_ERROR',
'init_throw' => 'RUN_STATUS_TRANSPORT_ERROR', 'setopt_false' => 'RUN_STATUS_TRANSPORT_ERROR',
'setopt_throw' => 'RUN_STATUS_TRANSPORT_ERROR', 'exec_false' => 'RUN_STATUS_TRANSPORT_ERROR',
'exec_throw' => 'RUN_STATUS_TRANSPORT_ERROR'] as $flag => $code) {
runStatusReset([$flag => true]); runStatusError(runStatusLookup(), $code);
}
runStatusReset(['close_throw' => true]); runStatusSuccess(runStatusLookup());
$small = json_encode(['id' => $identity['workflow_run_id'], 'status' => 'succeeded', 'outputs' => ''], JSON_THROW_ON_ERROR);
$limitBody = substr($small, 0, -2) . str_repeat('x', 1048576 - strlen($small)) . '"}';
runStatusExpect(strlen($limitBody) === 1048576, 'synthetic exact body boundary is one MiB');
runStatusReset(['body' => $limitBody]); runStatusSuccess(runStatusLookup());
runStatusReset(['chunks' => [substr($limitBody, 0, 1048566), substr($limitBody, 1048566)]]);
runStatusSuccess(runStatusLookup());
foreach ([[$limitBody . ' '], [$limitBody, ' '], [str_repeat('x', 1048577)]] as $chunks) {
runStatusReset(['chunks' => $chunks]); runStatusError(runStatusLookup(), 'RUN_STATUS_RESPONSE_TOO_LARGE');
runStatusExpect(end($runStatusStub['write_results']) === 0, 'oversize body aborts cURL before accumulating beyond one MiB');
}
foreach ([1, 137, 713, 1000, 1500, PHP_INT_MAX] as $timeout) {
runStatusReset(); runStatusSuccess(runStatusLookup(null, $timeout));
runStatusTransportOptions(min(1000, $timeout));
}
runStatusReset(['config_delay_ms' => 100, 'init_delay_ms' => 50]);
runStatusSuccess(runStatusLookup()); runStatusTransportOptions(850);
foreach (['config_delay_ms', 'init_delay_ms', 'setopt_delay_ms', 'exec_delay_ms', 'after_body_ms', 'info_delay_ms', 'close_delay_ms'] as $stage) {
runStatusReset([$stage => 1000]); runStatusError(runStatusLookup(), 'RUN_STATUS_TIMEOUT');
if (in_array($stage, ['config_delay_ms', 'init_delay_ms', 'setopt_delay_ms'], true)) {
runStatusExpect($runStatusStub['exec_calls'] === 0, 'setup expiration performs no GET');
}
}
runStatusReset(['exec_delay_ms' => 999]); runStatusSuccess(runStatusLookup());
runStatusReset(['exec_delay_ms' => 1000, 'progress' => true]);
runStatusError(runStatusLookup(), 'RUN_STATUS_TIMEOUT');
runStatusExpect($runStatusStub['progress_result'] === 1 && $runStatusStub['write_results'] === [], 'progress callback aborts at exact monotonic deadline');
runStatusReset(['chunks' => ['{"id":', '"synthetic-run-001","status":"stopped"}'], 'chunk_delay_ms' => [100, 900]]);
runStatusError(runStatusLookup(), 'RUN_STATUS_TIMEOUT');
runStatusExpect($runStatusStub['write_results'] === [6, 0], 'late body chunk cannot supply a terminal');
runStatusReset(['config_delay_ms' => 999, 'init_delay_ms' => 1]);
runStatusError(runStatusLookup(), 'RUN_STATUS_TIMEOUT');
runStatusExpect($runStatusStub['exec_calls'] === 0, 'remaining fraction never becomes a fresh timeout');
echo 'Dify workflow exact-run status adapter: OK (' . $cases . ' synthetic cases, ' . $checks . " assertions; zero network/config-file/model/DB/control operations)\n";
}