mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
test: update boot handshake test for synchronous warmup (#73083)
The old test asserted _warm_gateway_module was fire-and-forget (startup completes in << SLOW_SECONDS). PR #73291 intentionally reversed this: the import now runs synchronously before the lifespan yield because run_in_executor didn't release the GIL on Windows + Python 3.11. Updated the test to assert startup blocks for >= SLOW_SECONDS.
This commit is contained in:
parent
a9af49df09
commit
f199c4c92e
2 changed files with 21 additions and 17 deletions
|
|
@ -198,4 +198,4 @@ function verifyHermesCli(hermesCommand: string, opts?: { shell?: boolean }) {
|
|||
}
|
||||
}
|
||||
|
||||
export { canImportHermesCli, hermesRuntimeImportProbe, PROBE_TIMEOUT_MS, DEFAULT_PROBE_TIMEOUT_MS, resolveProbeTimeoutMs, shouldTrustHermesOverride, verifyHermesCli }
|
||||
export { canImportHermesCli, DEFAULT_PROBE_TIMEOUT_MS, hermesRuntimeImportProbe, PROBE_TIMEOUT_MS, resolveProbeTimeoutMs, shouldTrustHermesOverride, verifyHermesCli }
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@ Integration tests for the desktop boot handshake fix (PR #50231 / issue #50209).
|
|||
|
||||
Simulates a slow hermes_cli.gateway import (15-30 s on a fresh Windows install
|
||||
with Defender scanning every new .pyc) by patching the two helpers that touch
|
||||
the blocking import and measuring event-loop freedom + response latency.
|
||||
the blocking import and measuring response latency.
|
||||
|
||||
Three scenarios are covered:
|
||||
|
||||
1. _lifespan fire-and-forget: patched _warm_gateway_module sleeps N seconds in
|
||||
a thread; TestClient startup must complete in << N seconds (event loop not
|
||||
blocked, HERMES_DASHBOARD_READY would fire immediately).
|
||||
1. _lifespan synchronous warmup: patched _warm_gateway_module sleeps N seconds;
|
||||
TestClient startup blocks for >= N seconds (intentional — see #73083/#73291:
|
||||
the import doesn't release the GIL on Windows + Python 3.11, so
|
||||
run_in_executor still froze the event loop. Absorbing the cost before the
|
||||
lifespan yield ensures the server socket doesn't accept probes until the
|
||||
import is complete).
|
||||
|
||||
2. get_status run_in_executor: patched _resolve_restart_drain_timeout sleeps N
|
||||
seconds in a thread; a concurrent fast endpoint (/api/version) must respond
|
||||
|
|
@ -53,15 +56,16 @@ def _make_slow_drain(seconds: float):
|
|||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Test 1 — _lifespan fire-and-forget does not block the event loop
|
||||
# Test 1 — _lifespan synchronous warmup blocks startup (intentional)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_lifespan_warmup_is_nonblocking():
|
||||
def test_lifespan_warmup_is_synchronous():
|
||||
"""
|
||||
_warm_gateway_module runs in an executor (fire-and-forget).
|
||||
Even if it sleeps for SLOW_SECONDS, TestClient startup must complete
|
||||
in well under that time — proving the event loop was never blocked and
|
||||
HERMES_DASHBOARD_READY would have fired without delay.
|
||||
_warm_gateway_module runs synchronously before the lifespan yield (#73083).
|
||||
Startup blocks for at least SLOW_SECONDS — this is intentional: on Windows
|
||||
+ Python 3.11 the import doesn't release the GIL, so run_in_executor still
|
||||
froze the event loop. Absorbing the cost before the yield ensures probes
|
||||
arrive after the import is complete.
|
||||
"""
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
|
@ -70,13 +74,13 @@ def test_lifespan_warmup_is_nonblocking():
|
|||
with TestClient(web_server_mod.app, raise_server_exceptions=False) as _client:
|
||||
startup_ms = (time.perf_counter() - t0) * 1000
|
||||
|
||||
# Startup must complete in under half of SLOW_SECONDS (generous margin).
|
||||
# If the import were synchronous, startup would block for >= SLOW_SECONDS.
|
||||
threshold_ms = (SLOW_SECONDS * 1000) / 2
|
||||
assert startup_ms < threshold_ms, (
|
||||
f"_lifespan blocked the event loop: startup took {startup_ms:.0f} ms "
|
||||
# Startup must block for at least SLOW_SECONDS (the import runs synchronously).
|
||||
# If startup were faster, the import would still be fire-and-forget (old behavior).
|
||||
threshold_ms = (SLOW_SECONDS * 1000) * 0.8
|
||||
assert startup_ms >= threshold_ms, (
|
||||
f"_lifespan did not block for the warmup import: startup took {startup_ms:.0f} ms "
|
||||
f"but slow import is {SLOW_SECONDS * 1000:.0f} ms — "
|
||||
f"fire-and-forget is not working."
|
||||
f"warmup is not synchronous."
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue