From e838a22c3434c20bc740299895564d43a8619c63 Mon Sep 17 00:00:00 2001 From: Durable Workflow Date: Wed, 2 Sep 2026 11:58:05 +0000 Subject: [PATCH] Preserve playground readiness diagnostics --- scripts/playground | 16 +++++- tests/Unit/PlaygroundContractTest.php | 78 +++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/scripts/playground b/scripts/playground index 48d571d..031c3db 100755 --- a/scripts/playground +++ b/scripts/playground @@ -782,7 +782,21 @@ def dw_environment( def json_command( command: list[str], *, env: dict[str, str], timeout: float = 30 ) -> dict[str, Any]: - result = run(command, env=env, capture=True, timeout=timeout) + try: + result = run(command, env=env, capture=True, timeout=timeout) + except subprocess.CalledProcessError as error: + details = [] + for output in (error.stderr, error.stdout): + detail = (output or "").strip() + if detail and detail not in details: + details.append(detail) + message = ( + f"{shlex.join(command)} failed with exit status {error.returncode}" + ) + if details: + message = f"{message}:\n" + "\n".join(details) + raise PlaygroundError(message) from error + try: value = json.loads(result.stdout) except json.JSONDecodeError as error: diff --git a/tests/Unit/PlaygroundContractTest.php b/tests/Unit/PlaygroundContractTest.php index f1240b1..61aed27 100644 --- a/tests/Unit/PlaygroundContractTest.php +++ b/tests/Unit/PlaygroundContractTest.php @@ -439,6 +439,30 @@ class FakeWorker: "stale-worker", "current-worker", ] + +clock = {"now": 0} +def failed_roster(command, *, env, timeout=30): + raise PlaygroundError("dw worker:list failed with exit status 8: compatible client required") + +globals["json_command"] = failed_roster +globals["time"].monotonic = lambda: clock["now"] +globals["time"].sleep = lambda seconds: clock.update(now=61) +try: + wait_for_registration( + "shared-queue", + "current-worker", + "authored-workflow", + "authored-activity", + "https://runtime.example/namespaces/example", + "example", + "client-secret", + FakeWorker(), + ) +except PlaygroundError as error: + assert "compatible client required" in str(error) +else: + raise AssertionError("The final readiness error discarded the roster diagnostic") + print("invocation-registration-proof=ready") PYTHON; @@ -456,6 +480,60 @@ class FakeWorker: ); } + public function test_managed_readiness_preserves_the_cli_failure_diagnostic(): void + { + $harness = <<<'PYTHON' +import runpy +import subprocess +import sys + +playground = runpy.run_path(sys.argv[1]) +json_command = playground["json_command"] +PlaygroundError = playground["PlaygroundError"] +globals = json_command.__globals__ + +def fail(command, *, env=None, cwd=None, capture=False, timeout=None): + raise subprocess.CalledProcessError( + 8, + command, + output='{"error":"client_version_unsupported"}', + stderr="The installed client is outside the server-advertised supported range.", + ) + +globals["run"] = fail + +try: + json_command( + ["dw", "worker:list", "--task-queue=managed queue", "--json"], + env={}, + ) +except PlaygroundError as error: + message = str(error) + assert "dw 'worker:list'" not in message + assert "dw worker:list '--task-queue=managed queue' --json" in message + assert "exit status 8" in message + assert "client_version_unsupported" in message + assert "outside the server-advertised supported range" in message +else: + raise AssertionError("A failed roster command did not raise PlaygroundError") + +print("managed-readiness-diagnostic=preserved") +PYTHON; + + $process = new Process([ + 'python3', + '-c', + $harness, + $this->path('scripts/playground'), + ]); + $process->mustRun(); + + $this->assertStringContainsString( + 'managed-readiness-diagnostic=preserved', + $process->getOutput(), + ); + } + public function test_managed_runtime_keeps_worker_and_client_roles_isolated_for_every_sdk(): void { $filesystem = new Filesystem;