hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

commit a301d8742ed475ec59ce528911d4b7edc58b33e2
parent b00c097333e25e8c7ac5de5f9d01e8a194f1cb9f
Author: triesap <tyson@radroots.org>
Date:   Tue, 22 Sep 2026 17:06:33 +0000

tests: exercise C002B provider deadlines and startup causes

- Add a bounded per-stub deadline override so a test reaches the provider
  watchdog branch (parent deadline, not the child alarm) for MaxLocal and Jev.
- Assert the startup-failure control carries phase=startup reason=serve_failed
  plus the reaped exit/signal instead of a bare any-exception check.
- Add intended-cause controls for the ready-output and stdout size caps.
- Update the C002B criterion matrix to the exercised tests and counts.

Diffstat:
Mtests/jev_provider_helper.mojo | 36+++++++++++++++++++++---------------
Mtests/max_local_process_helper.mojo | 32++++++++++++++++++++++----------
Mtests/test_provider_helpers.mojo | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
3 files changed, 104 insertions(+), 31 deletions(-)

diff --git a/tests/jev_provider_helper.mojo b/tests/jev_provider_helper.mojo @@ -257,6 +257,7 @@ def serve_jev(port: Int, mode: String, requests: Int) raises -> ServeReport: struct SpawnedJevStub(Movable): var pid: Int var _report_fd: Int + var _deadline_ms: Int var _reaped: Bool var _ok: Bool var _phase: String @@ -265,9 +266,10 @@ struct SpawnedJevStub(Movable): var _requests: Int var _connections: Int - def __init__(out self, pid: Int, report_fd: Int): + def __init__(out self, pid: Int, report_fd: Int, deadline_ms: Int): self.pid = pid self._report_fd = report_fd + self._deadline_ms = deadline_ms self._reaped = False self._ok = False self._phase = "pending" @@ -330,7 +332,7 @@ struct SpawnedJevStub(Movable): def reap(mut self): if self._reaped: return - var st = wait_bounded(self.pid, FIXTURE_DEFAULT_DEADLINE_MS) + var st = wait_bounded(self.pid, self._deadline_ms) var report_text = "" if st.state == "running": var term = terminate_owned(self.pid, TERMINATION_GRACE_MS) @@ -399,15 +401,18 @@ def reserve_jev_port() raises -> Int: def spawn_jev_stub_auto( - mode: String, requests: Int + mode: String, requests: Int, deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS ) raises -> SpawnedJevStubAuto: - return _spawn_jev_stub(0, mode, requests) + return _spawn_jev_stub(0, mode, requests, deadline_ms) def spawn_jev_stub( - port: Int, mode: String, requests: Int + port: Int, + mode: String, + requests: Int, + deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS, ) raises -> SpawnedJevStub: - var started = _spawn_jev_stub(port, mode, requests) + var started = _spawn_jev_stub(port, mode, requests, deadline_ms) return started.stub^ @@ -420,21 +425,22 @@ def serve_jev_scripted( return serve_scripts(listener, scripts^, "scripted") -def _read_ready_line(fd: Int) -> String: +def _read_ready_line(fd: Int, deadline_ms: Int) -> String: try: - return read_line_bounded(fd, 256, FIXTURE_DEFAULT_DEADLINE_MS) + return read_line_bounded(fd, 256, deadline_ms) except: return "" def spawn_jev_scripted_auto( var scripts: List[ExchangeScript], + deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS, ) raises -> SpawnedJevStubAuto: - return _spawn_jev_scripted(0, scripts^) + return _spawn_jev_scripted(0, scripts^, deadline_ms) def _spawn_jev_scripted( - port: Int, var scripts: List[ExchangeScript] + port: Int, var scripts: List[ExchangeScript], deadline_ms: Int ) raises -> SpawnedJevStubAuto: var total = len(scripts) var pipe = make_pipe() @@ -456,7 +462,7 @@ def _spawn_jev_scripted( write_raw(1, report_line(failed) + "\n") child_exit(125) close_fd(pipe.write_fd) - var ready_line = _read_ready_line(pipe.read_fd) + var ready_line = _read_ready_line(pipe.read_fd, deadline_ms) if not ready_line.startswith("ready"): var st = terminate_owned(pid, TERMINATION_GRACE_MS) close_fd(pipe.read_fd) @@ -473,12 +479,12 @@ def _spawn_jev_scripted( reported_port = Int(String(ready_line[byte = space + 1 :])) _ = total return SpawnedJevStubAuto( - port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd) + port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd, deadline_ms) ) def _spawn_jev_stub( - port: Int, mode: String, requests: Int + port: Int, mode: String, requests: Int, deadline_ms: Int ) raises -> SpawnedJevStubAuto: var pipe = make_pipe() var pid = fork_pid() @@ -499,7 +505,7 @@ def _spawn_jev_stub( write_raw(1, report_line(failed) + "\n") child_exit(125) close_fd(pipe.write_fd) - var ready_line = _read_ready_line(pipe.read_fd) + var ready_line = _read_ready_line(pipe.read_fd, deadline_ms) if not ready_line.startswith("ready"): var st = terminate_owned(pid, TERMINATION_GRACE_MS) close_fd(pipe.read_fd) @@ -515,5 +521,5 @@ def _spawn_jev_stub( if space >= 0: reported_port = Int(String(ready_line[byte = space + 1 :])) return SpawnedJevStubAuto( - port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd) + port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd, deadline_ms) ) diff --git a/tests/max_local_process_helper.mojo b/tests/max_local_process_helper.mojo @@ -344,6 +344,7 @@ struct SpawnedMaxLocalStub(Movable): var pid: Int var port: Int var _report_fd: Int + var _deadline_ms: Int var _reaped: Bool var _ok: Bool var _phase: String @@ -352,10 +353,13 @@ struct SpawnedMaxLocalStub(Movable): var _requests: Int var _connections: Int - def __init__(out self, pid: Int, port: Int, report_fd: Int): + def __init__( + out self, pid: Int, port: Int, report_fd: Int, deadline_ms: Int + ): self.pid = pid self.port = port self._report_fd = report_fd + self._deadline_ms = deadline_ms self._reaped = False self._ok = False self._phase = "pending" @@ -419,7 +423,7 @@ struct SpawnedMaxLocalStub(Movable): """Reap the owned child and decode its bounded report (never raises).""" if self._reaped: return - var st = wait_bounded(self.pid, FIXTURE_DEFAULT_DEADLINE_MS) + var st = wait_bounded(self.pid, self._deadline_ms) var report_text = "" if st.state == "running": var term = terminate_owned(self.pid, TERMINATION_GRACE_MS) @@ -498,9 +502,9 @@ def _serve_max_local_for( return serve_max_local(port, mode, requests) -def _read_ready_line(fd: Int) -> String: +def _read_ready_line(fd: Int, deadline_ms: Int) -> String: try: - return read_line_bounded(fd, 256, FIXTURE_DEFAULT_DEADLINE_MS) + return read_line_bounded(fd, 256, deadline_ms) except: return "" @@ -515,16 +519,23 @@ def serve_max_local_scripted( def spawn_max_local_stub( - port: Int, mode: String, requests: Int + port: Int, + mode: String, + requests: Int, + deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS, ) raises -> SpawnedMaxLocalStub: var scripts = List[ExchangeScript]() - return _spawn_max_local(port, scripts^, mode, requests, False) + return _spawn_max_local(port, scripts^, mode, requests, False, deadline_ms) def spawn_max_local_scripted( - port: Int, var scripts: List[ExchangeScript] + port: Int, + var scripts: List[ExchangeScript], + deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS, ) raises -> SpawnedMaxLocalStub: - return _spawn_max_local(port, scripts^, "scripted", len(scripts), True) + return _spawn_max_local( + port, scripts^, "scripted", len(scripts), True, deadline_ms + ) def _spawn_max_local( @@ -533,6 +544,7 @@ def _spawn_max_local( mode: String, requests: Int, scripted: Bool, + deadline_ms: Int, ) raises -> SpawnedMaxLocalStub: var pipe = make_pipe() var pid = fork_pid() @@ -555,7 +567,7 @@ def _spawn_max_local( write_raw(1, report_line(failed) + "\n") child_exit(125) close_fd(pipe.write_fd) - var ready_line = _read_ready_line(pipe.read_fd) + var ready_line = _read_ready_line(pipe.read_fd, deadline_ms) if not ready_line.startswith("ready"): var st = terminate_owned(pid, TERMINATION_GRACE_MS) close_fd(pipe.read_fd) @@ -570,4 +582,4 @@ def _spawn_max_local( var space = ready_line.find(" ") if space >= 0: reported_port = Int(String(ready_line[byte = space + 1 :])) - return SpawnedMaxLocalStub(pid, reported_port, pipe.read_fd) + return SpawnedMaxLocalStub(pid, reported_port, pipe.read_fd, deadline_ms) diff --git a/tests/test_provider_helpers.mojo b/tests/test_provider_helpers.mojo @@ -10,7 +10,15 @@ from std.testing import TestSuite, assert_true, assert_equal from flare.net import SocketAddr from flare.tcp import TcpListener, TcpStream -from parent_lifecycle import open_fd_count, pid_not_waitable +from parent_lifecycle import ( + close_fd, + make_pipe, + open_fd_count, + pid_not_waitable, + read_all_bounded, + read_line_bounded, + write_raw, +) from strict_fixture import ( ExchangeScript, FramedRequest, @@ -727,17 +735,64 @@ def test_scripted_persistent_counters_and_close_semantics() raises: def test_startup_failure_distinct_from_exchange_failure() raises: # Occupy a port so the fixture child cannot bind; the parent must observe - # a startup failure (no ready line), not a script/parser rejection. + # a bounded startup/serve_failed report, not a generic exception or a + # script/parser rejection (FX07 / R61). var blocker = TcpListener.bind(SocketAddr.localhost(0)) var port = Int(blocker.local_addr().port) - var raised = False + var message = "" try: var stub = spawn_max_local_stub(port, "count_requests", 1) stub.terminate() - except: - raised = True + except e: + message = String(e) blocker.close() - assert_true(raised) + assert_true(message.find("phase=startup") >= 0) + assert_true(message.find("reason=serve_failed") >= 0) + assert_true(message.find("exited=") >= 0 or message.find("signal=") >= 0) + + +def test_provider_stub_parent_deadline_watchdog() raises: + # No client connects, so the child blocks in accept until the parent's own + # finite deadline fires and the owned child is terminated and reaped. + var stub = spawn_max_local_stub(0, "count_requests", 1, 800) + stub.reap() + assert_true(not stub.ok()) + assert_equal(stub.phase(), "watchdog") + assert_equal(stub.reason(), "timeout") + assert_true(pid_not_waitable(stub.pid)) + + +def test_jev_stub_parent_deadline_watchdog() raises: + var started = spawn_jev_stub_auto("ok", 1, 800) + started.stub.reap() + assert_true(not started.stub.ok()) + assert_equal(started.stub.phase(), "watchdog") + assert_equal(started.stub.reason(), "timeout") + assert_true(pid_not_waitable(started.stub.pid)) + + +def test_bounded_read_caps_fail_for_intended_cause() raises: + var ready_pipe = make_pipe() + write_raw(ready_pipe.write_fd, "no newline here") + var ready_message = "" + try: + _ = read_line_bounded(ready_pipe.read_fd, 8, 500) + except e: + ready_message = String(e) + close_fd(ready_pipe.read_fd) + close_fd(ready_pipe.write_fd) + assert_true(ready_message.find("ready_output_overflow") >= 0) + + var stdout_pipe = make_pipe() + write_raw(stdout_pipe.write_fd, "0123456789") + close_fd(stdout_pipe.write_fd) + var stdout_message = "" + try: + _ = read_all_bounded(stdout_pipe.read_fd, 4, 500) + except e: + stdout_message = String(e) + close_fd(stdout_pipe.read_fd) + assert_true(stdout_message.find("stdout_overflow") >= 0) def test_owned_child_reaped_after_early_terminate() raises: