From 000e4f4e9e7c87101485b0b66b99ce994c11bada Mon Sep 17 00:00:00 2001 From: EMRG Evolution Date: Thu, 20 Aug 2026 17:00:40 +0800 Subject: [PATCH] =?UTF-8?q?emrg:=20conftest=20hermeticity=20guard=20?= =?UTF-8?q?=E2=80=94=20any=20un-isolated=20stop=5Fdaemon=20call=20fails=20?= =?UTF-8?q?loudly=20(=E2=9B=94=20red=20line=20backstop)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 2026-08-20 16:18/16:25 incidents killed the live emrgd twice because tests/test_stop_all.py:677 lacked stop_daemon isolation. PR #889 fixed the specific leak; this adds the definitive anti-regression layer: an autouse conftest fixture replaces emrg._stop_all.stop_daemon with a raiser so any future test that triggers a real stop path fails with AssertionError instead of killing the daemon. A canary test verifies the guard stays installed. Agent.md pytest count synced 981 -> 982. --- Agent.md | 2 +- tests/conftest.py | 23 +++++++++++++++++++++++ tests/test_stop_all.py | 13 +++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Agent.md b/Agent.md index cbc59fce..12c4196c 100644 --- a/Agent.md +++ b/Agent.md @@ -118,7 +118,7 @@ Community needs voiced in HN agent-UI discussions map directly to EMRG's design: pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.token; python -m emrg ``` -Python: `uv run pytest tests/ -v` (981) — import check: `uv run python -c "from emrg.client.app import run_client"` +Python: `uv run pytest tests/ -v` (982) — import check: `uv run python -c "from emrg.client.app import run_client"` GUI: `cd emrg/gui && npm test` (260: 45 daemon_client + 19 conn-manager + 22 app-commands + 131 renderer smoke + 16 i18n + 7 integration + 3 commands + 8 build-config + 7 gui-state + 2 tool-group) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` CI: `uv run pytest` (ubuntu + **windows-2025 matrix** — Windows pytest 回归在 PR CI 即失败,v0.2.29 教训 #725) + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文) Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响) diff --git a/tests/conftest.py b/tests/conftest.py index ae011105..c92abb62 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -69,3 +69,26 @@ def _redirect_sessions_index(monkeypatch, tmp_path): sidx, "sessions_index_path", lambda: tmp_path / "sessions_index.json", ) + + +@pytest.fixture(autouse=True) +def _guard_stop_daemon_hermeticity(monkeypatch): + """⛔ Red line (host 2026-08-18T22:58): tests must NEVER trigger a real + stop_daemon() — it sends a shutdown over the websocket and kills the live + emrgd (2026-08-20 16:18/16:25 real incidents; the daemon is EMRG's life + core; rant 2026-08-20T16:32:30). Any test that calls stop_all()/ + stop_daemon() without isolating stop_daemon now fails loudly with + AssertionError instead of killing the daemon. Tests that DO isolate it + (monkeypatch.setattr(_stop_all, "stop_daemon", lambda: None)) patch after + this fixture and override it as usual. + """ + import emrg._stop_all as stop_mod + + def _no_real_stop_daemon(*args, **kwargs): + raise AssertionError( + "test triggered a REAL stop_daemon() — ⛔ red-line violation " + "(host 2026-08-18T22:58); tests must isolate it via " + "monkeypatch.setattr(emrg._stop_all, 'stop_daemon', lambda: None)" + ) + + monkeypatch.setattr(stop_mod, "stop_daemon", _no_real_stop_daemon) diff --git a/tests/test_stop_all.py b/tests/test_stop_all.py index b78cab17..44e66904 100644 --- a/tests/test_stop_all.py +++ b/tests/test_stop_all.py @@ -966,3 +966,16 @@ def test_stop_all_prints_tee_path(self, monkeypatch, tmp_path, capsys): assert "log also written to" in out assert str(tmp_path / ".emrg" / "logs") in out assert "exit code 0 (clean)" in out + + +class TestStopDaemonHermeticityGuard: + """⛔ Red line (host 2026-08-18T22:58): conftest's autouse guard must keep + stop_daemon patched so a forgetful test can never kill the live daemon + again (rant 2026-08-20T16:32:30). Canary checks function identity only — + it never calls any stop path.""" + + def test_conftest_guard_stop_daemon_is_installed(self): + import emrg._stop_all as stop_mod + assert stop_mod.stop_daemon.__name__ == "_no_real_stop_daemon", ( + "conftest stop_daemon guard missing — real stop_daemon reachable from tests" + )