hyf

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

test_repo_local_process_contract.mojo (11865B)


      1 from std.testing import TestSuite, assert_equal, assert_true
      2 from std.collections import List
      3 from safe_tempdir import SafeTempDir
      4 
      5 from json import Value
      6 from fixture_assertions import load_scenario_request_json
      7 from parent_lifecycle import (
      8     IO_DEADLINE_EXPIRED,
      9     IO_FAULT_EINTR_UNBOUNDED,
     10     POLLIN,
     11     close_fd,
     12     make_pipe,
     13     now_ms,
     14     read_fd,
     15     write_fd_chunk,
     16     write_raw,
     17 )
     18 from stdio_process_helper import (
     19     HYF_PATHS_PROFILE_ENV,
     20     HYF_PATHS_REPO_LOCAL_ROOT_ENV,
     21     ScopedEnvVar,
     22     drain_ready,
     23     run_stdio_binary_with_deadline,
     24     run_stdio_entrypoint,
     25     run_stdio_entrypoint_with_deadline,
     26 )
     27 
     28 
     29 def _assert_under_repo_local_root(repo_local_root: String, path: String) raises:
     30     assert_true(path.startswith(repo_local_root + "/"))
     31 
     32 
     33 def _assert_runtime_status_path_under_repo_local_root(
     34     runtime_status: Value, key: String, repo_local_root: String
     35 ) raises:
     36     _assert_under_repo_local_root(
     37         repo_local_root,
     38         runtime_status["paths"][key].string_value(),
     39     )
     40 
     41 
     42 def test_src_main_consumes_repo_local_env_without_outer_wrapper() raises:
     43     with SafeTempDir() as repo_local_root:
     44         with ScopedEnvVar(HYF_PATHS_PROFILE_ENV, "repo_local"):
     45             with ScopedEnvVar(HYF_PATHS_REPO_LOCAL_ROOT_ENV, repo_local_root):
     46                 var response = run_stdio_entrypoint(
     47                     "src/main.mojo",
     48                     load_scenario_request_json("scenarios/status_ok.json"),
     49                 )
     50 
     51                 var runtime_status = response["output"]["runtime"].clone()
     52                 assert_equal(
     53                     runtime_status["paths_profile"].string_value(), "repo_local"
     54                 )
     55                 assert_equal(
     56                     runtime_status["repo_local_base_root"].string_value(),
     57                     repo_local_root,
     58                 )
     59 
     60                 _assert_runtime_status_path_under_repo_local_root(
     61                     runtime_status, "config_dir", repo_local_root
     62                 )
     63                 _assert_runtime_status_path_under_repo_local_root(
     64                     runtime_status, "config_path", repo_local_root
     65                 )
     66                 _assert_runtime_status_path_under_repo_local_root(
     67                     runtime_status, "data_dir", repo_local_root
     68                 )
     69                 _assert_runtime_status_path_under_repo_local_root(
     70                     runtime_status, "cache_dir", repo_local_root
     71                 )
     72                 _assert_runtime_status_path_under_repo_local_root(
     73                     runtime_status, "logs_dir", repo_local_root
     74                 )
     75                 _assert_runtime_status_path_under_repo_local_root(
     76                     runtime_status, "diagnostics_dir", repo_local_root
     77                 )
     78                 _assert_runtime_status_path_under_repo_local_root(
     79                     runtime_status, "run_dir", repo_local_root
     80                 )
     81                 _assert_runtime_status_path_under_repo_local_root(
     82                     runtime_status, "secrets_dir", repo_local_root
     83                 )
     84                 _assert_runtime_status_path_under_repo_local_root(
     85                     runtime_status, "identity_path", repo_local_root
     86                 )
     87                 _assert_under_repo_local_root(
     88                     repo_local_root,
     89                     runtime_status["config"]["artifact_path"].string_value(),
     90                 )
     91                 assert_equal(
     92                     runtime_status["config"][
     93                         "artifact_path_source"
     94                     ].string_value(),
     95                     "canonical_runtime_path",
     96                 )
     97                 assert_equal(
     98                     runtime_status["config"]["artifact_present"].bool_value(),
     99                     False,
    100                 )
    101                 assert_equal(
    102                     runtime_status["config"]["load_state"].string_value(),
    103                     "not_found",
    104                 )
    105 
    106 
    107 def test_run_stdio_entrypoint_reaps_stalled_child_under_deadline() raises:
    108     # FX06/FX08: the parent deadline (not the child alarm) must terminate and
    109     # reap the owned child, and the raised error must carry the reap result.
    110     var message = ""
    111     try:
    112         _ = run_stdio_entrypoint_with_deadline(
    113             "tests/stdio_stall_entrypoint.mojo", "{}", "", "", 3000
    114         )
    115     except e:
    116         message = String(e)
    117     assert_true(message.find("stdio-entrypoint") >= 0)
    118     assert_true(message.find("signal=") >= 0 or message.find("exited=") >= 0)
    119     assert_true(message.find("cleanup_error=") >= 0)
    120 
    121 
    122 def test_run_stdio_entrypoint_rejects_unread_request() raises:
    123     # PC03: an incomplete request write is a cause-specific failure even though
    124     # the child emits valid JSON and exits zero, and the failure still exposes
    125     # the owned child's cleanup truth.
    126     var request = String("")
    127     for _ in range(150000):
    128         request += "r"
    129     var message = ""
    130     try:
    131         _ = run_stdio_entrypoint_with_deadline(
    132             "tests/stdio_no_read_entrypoint.mojo", request, "", "", 10000
    133         )
    134     except e:
    135         message = String(e)
    136     assert_true(message.find("write_") >= 0)
    137     assert_true(message.find("cleanup_error=") >= 0)
    138 
    139 
    140 def test_run_stdio_entrypoint_rejects_late_success() raises:
    141     # PC03: the ordinary compile/run helper keeps its declared total budget, so
    142     # a late exit is rejected inside it and the bounded cleanup grace is not
    143     # extra successful work. The phase-isolated proof of the late-exit child is
    144     # test_run_stdio_binary_rejects_late_exit_after_wait_phase below; this lane
    145     # only characterizes the one-shot compile/run helper's own budget.
    146     var start = now_ms()
    147     var message = ""
    148     try:
    149         _ = run_stdio_entrypoint_with_deadline(
    150             "tests/stdio_late_exit_entrypoint.mojo", "{}", "", "", 1200
    151         )
    152     except e:
    153         message = String(e)
    154     var elapsed = now_ms() - start
    155     assert_true(message.find("stdio-entrypoint") >= 0)
    156     assert_true(
    157         message.find("timeout") >= 0
    158         or message.find("read_deadline_expired") >= 0
    159     )
    160     assert_true(message.find("cleanup_error=") >= 0)
    161     assert_true(message.find("child_failed") < 0)
    162     assert_true(elapsed >= 1150)
    163     assert_true(elapsed <= 3700)
    164 
    165 
    166 comptime LATE_EXIT_SH_SCRIPT = String(
    167     "printf '{\"ok\":true}\\n'; exec 1>&- 2>&-; exec sleep 3"
    168 )
    169 
    170 
    171 def test_run_stdio_binary_rejects_late_exit_after_wait_phase() raises:
    172     # RA03 phase isolation: an already-launched control child (compilation is
    173     # entirely outside this assertion) must reach valid output, closed output
    174     # and the wait phase *before* its late exit is rejected. A generic
    175     # compile/startup/read timeout cannot satisfy these phase assertions.
    176     var start = now_ms()
    177     var message = ""
    178     try:
    179         _ = run_stdio_binary_with_deadline(
    180             "/bin/sh", "{}", "-c", LATE_EXIT_SH_SCRIPT, 1200
    181         )
    182     except e:
    183         message = String(e)
    184     var elapsed = now_ms() - start
    185     assert_true(message.find("stdio-entrypoint") >= 0)
    186     assert_true(message.find("phase=wait") >= 0)
    187     assert_true(message.find("request_sent=true") >= 0)
    188     assert_true(message.find("stdout_eof=true") >= 0)
    189     assert_true(message.find("stderr_eof=true") >= 0)
    190     assert_true(message.find("output_valid=true") >= 0)
    191     assert_true(message.find("output_bytes=") >= 0)
    192     assert_true(message.find("reason=timeout") >= 0)
    193     assert_true(message.find("cleanup_error=") >= 0)
    194     assert_true(message.find("child_failed") < 0)
    195     # Work time stays inside the declared budget; only the bounded cleanup
    196     # allowance is added afterwards.
    197     assert_true(elapsed >= 1150)
    198     assert_true(elapsed <= 1200 + 2500)
    199 
    200 
    201 def test_run_stdio_binary_reports_success_within_budget() raises:
    202     # RA03 positive control for the same launch seam: a child that emits valid
    203     # output and exits inside the budget is accepted.
    204     var response = run_stdio_binary_with_deadline(
    205         "/bin/sh", "{}", "-c", "printf '{\"ok\":true}\\n'", 5000
    206     )
    207     assert_true(response["ok"].bool_value())
    208 
    209 
    210 def test_run_stdio_entrypoint_classifies_loader_failure() raises:
    211     var message = ""
    212     try:
    213         _ = run_stdio_entrypoint_with_deadline(
    214             "tests/does_not_exist_entrypoint.mojo", "{}", "", "", 30000
    215         )
    216     except e:
    217         message = String(e)
    218     assert_true(message.find("child_failed") >= 0)
    219     assert_true(message.find("timeout") < 0)
    220 
    221 
    222 def test_run_stdio_entrypoint_drains_stdout_concurrently() raises:
    223     # LC04: a child that floods stdout before reading stdin must not deadlock
    224     # the parent's large request write; a correct interleaving succeeds.
    225     var request = String("")
    226     for _ in range(150000):
    227         request += "r"
    228     var response = Value(None)
    229     var message = ""
    230     var failed = False
    231     try:
    232         response = run_stdio_entrypoint_with_deadline(
    233             "tests/stdio_stdout_flood_entrypoint.mojo",
    234             request,
    235             "",
    236             "",
    237             30000,
    238         )
    239     except e:
    240         failed = True
    241         message = String(e)
    242     assert_true(not failed)
    243     assert_equal(message, "")
    244     assert_true(response["ok"].bool_value())
    245 
    246 
    247 def test_diagnostics_overflow_is_bounded_with_cause() raises:
    248     # LC04: stderr diagnostics are capped and an overflow is a distinct cause.
    249     var pipe = make_pipe()
    250     var chunk = String("")
    251     for _ in range(4096):
    252         chunk += "e"
    253     var out = List[UInt8]()
    254     var overflow_reason = ""
    255     for _ in range(20):
    256         _ = write_raw(pipe.write_fd, chunk)
    257         var d = drain_ready(pipe.read_fd, out, 65536, POLLIN, 2000)
    258         if d.reason != "":
    259             overflow_reason = d.reason
    260             break
    261     close_fd(pipe.read_fd)
    262     close_fd(pipe.write_fd)
    263     assert_equal(overflow_reason, "stream_overflow")
    264     assert_true(len(out) <= 65536)
    265 
    266 
    267 def test_diagnostics_read_failure_is_distinct_from_eof() raises:
    268     # LC04: an unavailable descriptor is a read failure, not a clean EOF.
    269     var out = List[UInt8]()
    270     var d = drain_ready(-1, out, 16, POLLIN, 2000)
    271     assert_true(d.eof)
    272     assert_equal(d.reason, "read_error")
    273 
    274 
    275 def test_diagnostics_retry_is_bounded_by_deadline() raises:
    276     # RA03: an EINTR-style retry inside the stdio drain path must return to the
    277     # deadline owner instead of retrying without bound. The retry count is a
    278     # bounded test seam; no host signal state is involved.
    279     var pipe = make_pipe()
    280     var buf = InlineArray[Byte, 8](fill=0)
    281     var start = now_ms()
    282     var rc = read_fd(
    283         pipe.read_fd, buf.unsafe_ptr(), 8, 60, IO_FAULT_EINTR_UNBOUNDED
    284     )
    285     var elapsed = now_ms() - start
    286     close_fd(pipe.read_fd)
    287     close_fd(pipe.write_fd)
    288     assert_equal(rc, IO_DEADLINE_EXPIRED)
    289     assert_true(elapsed >= 50)
    290 
    291 
    292 def test_diagnostics_write_retry_is_bounded_by_deadline() raises:
    293     # RA03: the same bound holds for the stdio request-write retry loop.
    294     var pipe = make_pipe()
    295     var payload = String('{"ok":true}')
    296     var start = now_ms()
    297     var cw = write_fd_chunk(
    298         pipe.write_fd, payload, 0, 60, IO_FAULT_EINTR_UNBOUNDED
    299     )
    300     var elapsed = now_ms() - start
    301     close_fd(pipe.read_fd)
    302     close_fd(pipe.write_fd)
    303     assert_equal(cw.reason, "write_deadline_expired")
    304     assert_true(elapsed >= 50)
    305 
    306 
    307 def test_run_stdio_entrypoint_drains_stderr_concurrently_and_bounds_it() raises:
    308     # LC04: a child that floods stderr before producing output must not
    309     # deadlock the parent, and the overflow is reported with its own cause.
    310     var message = ""
    311     try:
    312         _ = run_stdio_entrypoint_with_deadline(
    313             "tests/stdio_stderr_flood_entrypoint.mojo", "{}", "", "", 30000
    314         )
    315     except e:
    316         message = String(e)
    317     assert_true(message.find("stderr_stream_overflow") >= 0)
    318     assert_true(message.find("timeout") < 0)
    319 
    320 
    321 def main() raises:
    322     TestSuite.discover_tests[__functions_in_module()]().run()