commit d76450d1faf5f7ab9a73d2072da5a2242dd84ef2
parent e02e759eba7d2a706e7863cd834dae4a24f311c6
Author: triesap <tyson@radroots.org>
Date: Wed, 23 Sep 2026 18:35:12 +0000
H007: make the injected write-error seam non-peer-close
- Mark injected errors so they always classify as unrelated, never a peer close.
- Cover an injected invalid-descriptor write error on the MaxLocal serve path.
- Keep the declared non-peer-cause control proving the cause allow-list.
Diffstat:
2 files changed, 48 insertions(+), 12 deletions(-)
diff --git a/tests/strict_fixture.mojo b/tests/strict_fixture.mojo
@@ -630,6 +630,11 @@ def classify_write_error_cause(text: String) -> String:
error (an injected handler failure, a write timeout, an invalid descriptor)
is surfaced as a bounded fixture failure rather than tolerated.
"""
+ if text.find("injected_error") >= 0:
+ # The bounded test-only write-error seam is never a real peer close, so
+ # an injected error can never be accepted by declaring a peer-close
+ # cause (ADR-0020 TC01).
+ return "unrelated_error"
if text.find("ConnectionReset") >= 0:
return "peer_reset"
if text.find("BrokenPipe") >= 0:
@@ -743,7 +748,10 @@ def serve_scripts(
phase = "body_stall"
usleep(script.stall_after_head_ms * 1000)
if script.inject_write_error != "":
- raise Error(script.inject_write_error)
+ raise Error(
+ "injected_error: "
+ + script.inject_write_error
+ )
reader.write_all(String(rendered[byte=head_end:]))
else:
phase = "head_write"
@@ -751,7 +759,9 @@ def serve_scripts(
else:
phase = "delayed_write"
if script.inject_write_error != "":
- raise Error(script.inject_write_error)
+ raise Error(
+ "injected_error: " + script.inject_write_error
+ )
reader.write_all(
render_response(
script,
diff --git a/tests/test_provider_adapter.mojo b/tests/test_provider_adapter.mojo
@@ -673,9 +673,8 @@ def test_provider_scripted_wrong_phase_close_fails() raises:
def test_provider_scripted_injected_write_error_fails() raises:
- # TC01: an unrelated injected handler error (here a write timeout) must not
- # be accepted even when a peer close was declared, because the bounded
- # observed cause differs from the declared cause.
+ # TC01: an injected write error is never a peer close, so it must not be
+ # accepted even when a peer close was declared.
var scripts = List[ExchangeScript]()
var script = exchange_script(
"injected_error", "POST", "/v1/chat/completions", 200, '{"choices":[]}'
@@ -694,7 +693,39 @@ def test_provider_scripted_injected_write_error_fails() raises:
assert_equal(provider_stub.phase(), "peer_close")
assert_true(
provider_stub.reason().find(
- "unexpected_write_write_timeout_body_stall"
+ "unexpected_write_unrelated_error_body_stall"
+ )
+ >= 0
+ )
+ guard.assert_clean()
+
+
+def test_provider_scripted_injected_invalid_descriptor_fails() raises:
+ # TC01: an injected invalid-descriptor write error must also fail rather
+ # than being accepted as a declared peer close.
+ var scripts = List[ExchangeScript]()
+ var script = exchange_script(
+ "injected_descriptor",
+ "POST",
+ "/v1/chat/completions",
+ 200,
+ '{"choices":[]}',
+ )
+ script.stall_after_head_ms = 200
+ script.expect_peer_close = True
+ script.expected_close_cause = "broken_pipe"
+ script.expected_close_phase = "body_stall"
+ script.inject_write_error = "Bad file descriptor"
+ scripts.append(script^)
+ var guard = CleanupGuard()
+ with spawn_max_local_scripted(0, scripts^, guard) as provider_stub:
+ _ = _raw_send_and_read(provider_stub.port, "/v1/chat/completions")
+ provider_stub.reap()
+ assert_true(not provider_stub.ok())
+ assert_equal(provider_stub.phase(), "peer_close")
+ assert_true(
+ provider_stub.reason().find(
+ "unexpected_write_unrelated_error_body_stall"
)
>= 0
)
@@ -724,12 +755,7 @@ def test_provider_scripted_declared_non_peer_cause_is_rejected() raises:
provider_stub.reap()
assert_true(not provider_stub.ok())
assert_equal(provider_stub.phase(), "peer_close")
- assert_true(
- provider_stub.reason().find(
- "unexpected_write_write_timeout_body_stall"
- )
- >= 0
- )
+ assert_true(provider_stub.reason().find("unexpected_write_") >= 0)
guard.assert_clean()