修复
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\common\service {
|
||||
require __DIR__ . '/fixtures/CancellableCurlMultiStub.php';
|
||||
function config(string $name): array { return $GLOBALS['readinessConfig']; }
|
||||
function curl_init(): \stdClass { return (object) ['errno' => 0]; }
|
||||
function curl_setopt_array(\stdClass $handle, array $options): bool { $handle->options = $options; return true; }
|
||||
function curl_exec(\stdClass $handle): string|bool {
|
||||
$url = $handle->options[CURLOPT_URL];
|
||||
if (str_ends_with($url, '/stop')) { ++$GLOBALS['readinessStops']; return '{"result":"success"}'; }
|
||||
++$GLOBALS['readinessSends'];
|
||||
$GLOBALS['readinessReady'] = false;
|
||||
if ($GLOBALS['readinessMode'] === 'blocking') {
|
||||
$handle->httpCode = 404;
|
||||
return '{"code":"not_found","message":"Not Found"}';
|
||||
}
|
||||
$header = $handle->options[CURLOPT_HEADERFUNCTION];
|
||||
$write = $handle->options[CURLOPT_WRITEFUNCTION];
|
||||
$header($handle, "HTTP/1.1 200 OK\r\n");
|
||||
$write($handle, "data: {\"event\":\"workflow_started\",\"task_id\":\"synthetic-unconfirmed\"}\n\n");
|
||||
return true; // EOF without message_end/workflow terminal; never a real network call.
|
||||
}
|
||||
function curl_errno(\stdClass $handle): int { return $handle->errno; }
|
||||
function curl_getinfo(\stdClass $handle, int $option): int { return $handle->httpCode ?? 200; }
|
||||
function curl_close(\stdClass $handle): void {}
|
||||
}
|
||||
namespace {
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
use app\common\service\DifyChatService;
|
||||
use app\adminapi\service\AssistantRequestControl;
|
||||
$assertions = 0;
|
||||
function readinessCheck(bool $ok, string $message): void {
|
||||
++$GLOBALS['assertions']; if (!$ok) throw new RuntimeException($message);
|
||||
}
|
||||
$readinessConfig = ['enable' => true, 'base_url' => 'https://synthetic.invalid/v1', 'timeout' => 5,
|
||||
'models' => ['qwen' => ['name' => 'synthetic-qwen', 'api_key' => 'offline-only'],
|
||||
'openai' => ['name' => 'synthetic-openai', 'api_key' => 'offline-only']]];
|
||||
$directory = sys_get_temp_dir() . '/zyt-readiness-' . bin2hex(random_bytes(8));
|
||||
try {
|
||||
foreach (['qwen', 'openai'] as $profile) {
|
||||
foreach (['stream', 'blocking'] as $readinessMode) {
|
||||
$readinessSends = 0; $readinessReady = true; $readinessStops = 0;
|
||||
$control = new AssistantRequestControl(7, 11, '12345678-1234-4234-8234-' . sprintf('%012d', $assertions), $directory);
|
||||
readinessCheck($control->begin(), 'isolated control begins');
|
||||
$options = ['knowledge_mode' => 'evidence',
|
||||
'can_start_attempt' => static fn (): bool => $GLOBALS['readinessReady'] && $control->canStartAttempt(),
|
||||
'on_task' => static function (string $task) use ($control, $profile): void { $control->bindTask($profile, 'synthetic-user', $task); },
|
||||
'on_transfer_end' => static function (string $task) use ($control): void { $control->clearTask($task); }];
|
||||
$result = $readinessMode === 'stream'
|
||||
? DifyChatService::streamChat($profile, [], 'synthetic', 'synthetic-user', static fn ($text): bool => true, null, [], $options)
|
||||
: DifyChatService::chat($profile, [], 'synthetic', 'synthetic-user', [], $options);
|
||||
readinessCheck(($result['error_code'] ?? '') === 'UPSTREAM_CLEANUP_PENDING' && $readinessSends === 1,
|
||||
'An internal protocol retry cannot start another generation while cleanup is pending.');
|
||||
readinessCheck(DifyChatService::stopTask($profile, 'synthetic-unconfirmed', 'synthetic-user') && $readinessStops === 1,
|
||||
'The generation gate never blocks the stop endpoint.');
|
||||
if ($readinessMode === 'stream') readinessCheck(!$control->canStartAttempt(), 'EOF never clears an unconfirmed remote identity.');
|
||||
}
|
||||
$control = new AssistantRequestControl(7, 11, '22345678-1234-4234-8234-' . sprintf('%012d', $assertions), $directory);
|
||||
readinessCheck($control->begin(), 'receipt control begins');
|
||||
$control->bindTask($profile, 'synthetic-user', 'old-task'); $control->signalValidationFailure();
|
||||
$control->markStop(true, 'old-task');
|
||||
try { $control->bindTask($profile, 'synthetic-user', 'race-task'); } catch (RuntimeException $expected) {}
|
||||
$control->markTerminal('old-task', 'stopped'); $control->clearTask('old-task');
|
||||
$state = $control->publicState();
|
||||
readinessCheck(!$state['upstream_terminal_confirmed'] && !$state['upstream_stop_confirmed']
|
||||
&& $state['upstream_pending_task_count'] === 1 && !$control->canStartAttempt(),
|
||||
'A pending quarantined task cannot be hidden by a previous task terminal.');
|
||||
readinessCheck(!str_contains(json_encode($state), 'race-task'), 'Public receipts never expose task identities.');
|
||||
}
|
||||
echo "Dify per-attempt readiness / cleanup receipt: {$assertions} assertions passed (pure cURL stubs).\n";
|
||||
} finally {
|
||||
foreach (glob($directory . '/*') ?: [] as $path) unlink($path);
|
||||
if (is_file($directory . '/.gc.lock')) unlink($directory . '/.gc.lock');
|
||||
if (is_dir($directory)) rmdir($directory);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user