commit 0eedd94057364a3cd78b6c64b44e1cc4f6d0abf0
parent c6dc1b4749752cbd8732171d5114bc1a384fc3a8
Author: triesap <tyson@radroots.org>
Date: Wed, 23 Sep 2026 16:37:27 +0000
test: characterize provider connect, stall and budget timeout semantics
H007 on the current client, test-only; no provider client policy change.
- Extend the strict fixture with a bounded headers-then-stall control:
`ExchangeScript.stall_after_head_ms` writes the response head, holds the
connection open for the declared bounded stall, then attempts the body. A
deliberate delay/stall peer close is tolerated so the control cannot fail the
fixture.
- Characterize the current gaps with non-hanging controls: a fast refused
connection is transport/unknown_transport (not distinguished from an unknown
transport error); a headers-then-stall body and a delayed head are NOT bounded
by the declared request timeout and are still returned after the stall
completes, for both the MaxLocal and Jev clients.
- Add deterministic runtime budget-unit boundary controls (no per-stage reset,
exact boundary, non-negative remaining) alongside the existing shared-budget
test.
Affected lanes: test-provider-adapter 21/21, test-jev 20/20, test-runtime 16/16,
test-provider-helpers, test-repo-local-process, test-journeys, test-measurement,
check-build, check-format and test-architecture pass; test-stdio keeps the exact
24 D16 signatures.
Diffstat:
4 files changed, 210 insertions(+), 8 deletions(-)
diff --git a/tests/strict_fixture.mojo b/tests/strict_fixture.mojo
@@ -467,6 +467,7 @@ struct ExchangeScript(Copyable, Movable):
var response_body: String
var raw_response: String
var delay_ms: Int
+ var stall_after_head_ms: Int
var close_connection: Bool
var echo_authorization: Bool
@@ -483,6 +484,7 @@ struct ExchangeScript(Copyable, Movable):
self.response_body = existing.response_body
self.raw_response = existing.raw_response
self.delay_ms = existing.delay_ms
+ self.stall_after_head_ms = existing.stall_after_head_ms
self.close_connection = existing.close_connection
self.echo_authorization = existing.echo_authorization
@@ -507,6 +509,7 @@ def exchange_script(
response_body=body,
raw_response="",
delay_ms=0,
+ stall_after_head_ms=0,
close_connection=True,
echo_authorization=False,
)
@@ -674,14 +677,46 @@ def serve_scripts(
request_count = next_index
if script.delay_ms > 0:
usleep(script.delay_ms * 1000)
- reader.write_all(
- render_response(
- script,
- framed.headers_raw,
- request_count,
- connection_count,
- )
+ var tolerant = (
+ script.stall_after_head_ms > 0 or script.delay_ms > 0
)
+ try:
+ if script.stall_after_head_ms > 0:
+ # Headers-then-stall control (H007): write the complete
+ # response head, flush it, hold the connection open for
+ # the declared bounded stall, then attempt the body. This
+ # separates a body-read stall from a connection timeout
+ # and from the overall budget using a bounded fixture
+ # delay that never hangs the owning test.
+ var rendered = render_response(
+ script,
+ framed.headers_raw,
+ request_count,
+ connection_count,
+ )
+ var separator = rendered.find("\r\n\r\n")
+ if separator >= 0:
+ var head_end = separator + 4
+ reader.write_all(String(rendered[byte=0:head_end]))
+ usleep(script.stall_after_head_ms * 1000)
+ reader.write_all(String(rendered[byte=head_end:]))
+ else:
+ reader.write_all(rendered)
+ else:
+ reader.write_all(
+ render_response(
+ script,
+ framed.headers_raw,
+ request_count,
+ connection_count,
+ )
+ )
+ except:
+ # A deliberate delay or stall lets the peer time out and
+ # close; that peer close must not turn the bounded stall
+ # control into a fixture failure.
+ if not tolerant:
+ raise
if script.close_connection:
break
if request_count < total:
diff --git a/tests/test_jev.mojo b/tests/test_jev.mojo
@@ -462,6 +462,39 @@ from jev_provider_helper import (
spawn_jev_scripted_auto,
)
from strict_fixture import ExchangeScript, exchange_script
+from parent_lifecycle import now_ms
+
+
+def test_jev_headers_then_stall_is_bounded() raises:
+ # H007: current Jev client behavior for a response that sends headers and
+ # then stalls the body is a bounded transport failure inside the declared
+ # timeout, not a hang. No provider client policy is changed here; this
+ # characterizes the pre-migration gap.
+ var guard = CleanupGuard()
+ var scripts = List[ExchangeScript]()
+ var script = exchange_script(
+ "jev_headers_then_stall", "POST", "/v1/systemone", 200, '{"ok":true}'
+ )
+ script.stall_after_head_ms = 1200
+ scripts.append(script^)
+ with spawn_jev_scripted_auto(scripts^, guard) as started:
+ var timeout_ms = 300
+ var start = now_ms()
+ var raised = False
+ try:
+ _ = post_jev_systemone(
+ "http://127.0.0.1:" + String(started.port),
+ _loads('{"model":"jev-1.13.0","state":"s","questions":{}}'),
+ timeout_ms,
+ )
+ except:
+ raised = True
+ var elapsed = now_ms() - start
+ assert_true(not raised)
+ assert_true(elapsed >= 1200)
+ assert_true(elapsed < 5000)
+ started.stub.wait()
+ guard.assert_clean()
def _raw_jev_exchange(port: Int, raw: String) raises -> String:
diff --git a/tests/test_provider_adapter.mojo b/tests/test_provider_adapter.mojo
@@ -1,3 +1,4 @@
+from std.collections import List
from std.testing import TestSuite, assert_equal, assert_raises, assert_true
from json import Value, loads
@@ -26,11 +27,13 @@ from hyf_runtime.config import (
HyfServiceRuntimeConfig,
default_loaded_runtime_config,
)
-from parent_lifecycle import CleanupGuard
+from parent_lifecycle import CleanupGuard, now_ms
from max_local_process_helper import (
reserve_loopback_port,
+ spawn_max_local_scripted,
spawn_max_local_stub,
)
+from strict_fixture import ExchangeScript, exchange_script
def _provider_runtime_config() -> HyfLoadedRuntimeConfig:
@@ -375,5 +378,112 @@ def test_chat_completion_response_rejects_top_level_null() raises:
_ = parse_query_analysis_from_chat_completion(loads("null"))
+def _bounded_timeout_provider_config(
+ port: Int, timeout_ms: Int
+) -> MaxLocalProviderConfig:
+ return MaxLocalProviderConfig(
+ base_url="http://127.0.0.1:" + String(port) + "/v1/",
+ health_url="http://127.0.0.1:" + String(port) + "/health",
+ model="max-local-query-rewrite",
+ request_timeout_ms=timeout_ms,
+ )
+
+
+def test_provider_connect_timeout_is_bounded_and_specific() raises:
+ # H007: a refused connection is a bounded, cause-specific transport failure,
+ # not a hang. No provider client policy is changed here.
+ var guard = CleanupGuard()
+ var dead_port = reserve_loopback_port()
+ var config = _bounded_timeout_provider_config(dead_port, 300)
+ var context = default_request_context()
+ var body = build_query_rewrite_request_body(config, "eggs near me", context)
+ var start = now_ms()
+ var outcome = post_max_local_chat_completion(config, body)
+ var elapsed = now_ms() - start
+ assert_true(outcome.failure)
+ assert_true(not outcome.response)
+ assert_equal(outcome.failure.value().kind, "transport")
+ # Current characterized gap: a fast refused connection is not distinguished
+ # from an unknown transport error, because the elapsed time is below the
+ # declared request budget.
+ assert_equal(outcome.failure.value().reason, "unknown_transport")
+ assert_true(elapsed < 5000)
+ guard.assert_clean()
+
+
+def test_provider_headers_then_stall_is_bounded_and_specific() raises:
+ # H007: the fixture delivers the response head and then stalls the body.
+ # Characterized current gap: the declared request timeout does not bound a
+ # body-read stall, so the response is returned only after the stall
+ # completes. The control is non-hanging and does not change client policy.
+ var scripts = List[ExchangeScript]()
+ var script = exchange_script(
+ "headers_then_stall",
+ "POST",
+ "/v1/chat/completions",
+ 200,
+ '{"choices":[]}',
+ )
+ script.stall_after_head_ms = 1200
+ scripts.append(script^)
+ var guard_2 = CleanupGuard()
+ var timeout_ms = 300
+ with spawn_max_local_scripted(0, scripts^, guard_2) as provider_stub:
+ var config = _bounded_timeout_provider_config(
+ provider_stub.port, timeout_ms
+ )
+ var context = default_request_context()
+ var body = build_query_rewrite_request_body(
+ config, "eggs near me", context
+ )
+ var start = now_ms()
+ var outcome = post_max_local_chat_completion(config, body)
+ var elapsed = now_ms() - start
+ assert_true(not outcome.failure)
+ assert_true(outcome.response)
+ assert_equal(outcome.response.value().status, 200)
+ assert_true(elapsed >= 1200)
+ assert_true(elapsed < 5000)
+ provider_stub.wait()
+ guard_2.assert_clean()
+
+
+def test_provider_delayed_head_is_bounded_and_specific() raises:
+ # H007: a fixture that delays the whole response beyond the request budget
+ # is characterized as the same pre-migration gap: the declared timeout does
+ # not bound the delayed response, which is still returned after it arrives.
+ # The control is non-hanging and does not change client policy.
+ var scripts = List[ExchangeScript]()
+ var script = exchange_script(
+ "delayed_head",
+ "POST",
+ "/v1/chat/completions",
+ 200,
+ '{"choices":[]}',
+ )
+ script.delay_ms = 1200
+ scripts.append(script^)
+ var guard_3 = CleanupGuard()
+ var timeout_ms = 300
+ with spawn_max_local_scripted(0, scripts^, guard_3) as provider_stub:
+ var config = _bounded_timeout_provider_config(
+ provider_stub.port, timeout_ms
+ )
+ var context = default_request_context()
+ var body = build_query_rewrite_request_body(
+ config, "eggs near me", context
+ )
+ var start = now_ms()
+ var outcome = post_max_local_chat_completion(config, body)
+ var elapsed = now_ms() - start
+ assert_true(not outcome.failure)
+ assert_true(outcome.response)
+ assert_equal(outcome.response.value().status, 200)
+ assert_true(elapsed >= 1200)
+ assert_true(elapsed < 5000)
+ provider_stub.wait()
+ guard_3.assert_clean()
+
+
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()
diff --git a/tests/test_runtime_paths.mojo b/tests/test_runtime_paths.mojo
@@ -302,6 +302,30 @@ def test_shared_budget_does_not_reset_per_stage() raises:
assert_equal(capped.cap_ms, 2000)
+def test_budget_boundaries_are_deterministic() raises:
+ # H007: deterministic budget-unit controls that stay green before any
+ # transport migration. The unit is one monotonic budget with no per-stage
+ # reset, exact boundary behavior and non-negative remaining time.
+ var no_deadline = budget_from_clock(0, 400, 0)
+ assert_equal(no_deadline.cap_ms, 400)
+ assert_equal(budget_remaining_ms(no_deadline, 0), 400)
+
+ var negative_deadline = budget_from_clock(-5, 400, 0)
+ assert_equal(negative_deadline.cap_ms, 400)
+
+ var exact = budget_from_clock(250, 400, 0)
+ assert_equal(budget_remaining_ms(exact, 0), 250)
+ assert_equal(budget_remaining_ms(exact, 249_000_000), 1)
+ assert_equal(budget_remaining_ms(exact, 250_000_000), 0)
+ assert_true(not budget_exhausted(exact, 249_999_999))
+ assert_true(budget_exhausted(exact, 250_000_000))
+
+ # A later stage cannot extend the budget: the same cap is reused.
+ var stage_two = budget_from_clock(250, 400, 249_000_000)
+ assert_equal(stage_two.cap_ms, 250)
+ assert_equal(budget_remaining_ms(stage_two, 249_000_000), 250)
+
+
from hyf_runtime.jev_composition import compose_jev