mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-27 20:14:49 +02:00
[eric] ci: a test process never spawns a router (OSW_NEVER_SPAWN_ROUTER), the boot hook's sync npm install hung the suite on every router-less runner; the slot-size test locates its source on Windows (ENG-486)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
96cb953a27
commit
51bd1f990a
@@ -28,6 +28,7 @@ jobs:
|
||||
env:
|
||||
OSW_NEVER_KILL_ROUTER: '1'
|
||||
OSW_DISABLE_AUTO_RESUME: '1'
|
||||
OSW_NEVER_SPAWN_ROUTER: '1'
|
||||
# The packaged app spawns its backend with PYTHONUTF8=1 (electron/main.js); the suite runs the way the product runs, or Windows' cp1252 default fails collection on the first source read.
|
||||
PYTHONUTF8: '1'
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
||||
|
||||
@@ -414,10 +414,28 @@ def p_report_start_failure(reason: str, *, detail: str = "", **fields: Any) -> N
|
||||
logger.debug("9router start-failure diagnostic submit failed", exc_info=True)
|
||||
|
||||
|
||||
def spawn_held_because() -> str | None:
|
||||
"""A declared switch, never an incidental fact: the suite and CI set it, the product never does."""
|
||||
if os.environ.get("OSW_NEVER_SPAWN_ROUTER") == "1":
|
||||
return "OSW_NEVER_SPAWN_ROUTER=1 (a test run adopts a router or has none)"
|
||||
return None
|
||||
|
||||
|
||||
p_spawn_hold_said = False
|
||||
|
||||
|
||||
async def ensure_running():
|
||||
"""Start 9Router if not already running. Serialized so concurrent callers
|
||||
(the background auto-start + a dispatch-time ensure) can't double-spawn."""
|
||||
global p_start_lock
|
||||
global p_start_lock, p_spawn_hold_said
|
||||
held = spawn_held_because()
|
||||
if held:
|
||||
# The boot-time start ran a sync `npm install 9router` (up to 300 s) INSIDE the event loop on a runner with no
|
||||
# router, and the whole suite hung behind it (macOS CI, 2026-09-06). A held spawn says so once and never blocks.
|
||||
if not p_spawn_hold_said:
|
||||
p_spawn_hold_said = True
|
||||
logger.warning(f"[9router] NOT starting the router because {held}; every lane that needs it is unserved in this process")
|
||||
return
|
||||
if p_start_lock is None:
|
||||
p_start_lock = asyncio.Lock()
|
||||
async with p_start_lock:
|
||||
@@ -566,7 +584,6 @@ async def p_ensure_running_impl():
|
||||
if p_process is not None and p_process.poll() is None:
|
||||
logger.info("9Router already running (ours) on port %d", NINE_ROUTER_PORT)
|
||||
return
|
||||
import subprocess as p_sp
|
||||
try:
|
||||
p_stale = stale_router_pids()
|
||||
if p_stale and router_kill_held_because():
|
||||
|
||||
@@ -24,7 +24,6 @@ assert "backend.config.paths" not in sys.modules, "conftest must set OPENSWARM_D
|
||||
os.environ["OPENSWARM_DATA_ROOT"] = tempfile.mkdtemp(prefix="osw_test_data_")
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
@@ -48,6 +47,8 @@ def _isolate_browser_state(monkeypatch):
|
||||
monkeypatch.setenv("OSW_DISABLE_AUTO_RESUME", "1")
|
||||
# A suite run must never kill a router it did not start; that is the user's app (ENG-393).
|
||||
monkeypatch.setenv("OSW_NEVER_KILL_ROUTER", "1")
|
||||
# ...and never START one: the boot hook ran `npm install 9router` inside the event loop on a router-less CI runner and hung the suite.
|
||||
monkeypatch.setenv("OSW_NEVER_SPAWN_ROUTER", "1")
|
||||
monkeypatch.setenv("OSW_PRESTAGE", "0")
|
||||
monkeypatch.setenv("OSW_FASTREAD_HOP", "0")
|
||||
monkeypatch.setenv("OSW_PRELUDE_TRIM", "0")
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
"""A test process never starts a router: the boot hook's sync npm install hung the whole suite on a router-less runner."""
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from backend.apps.nine_router import process
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_held_spawn_never_reaches_the_installer_and_says_so_once(monkeypatch):
|
||||
monkeypatch.setenv("OSW_NEVER_SPAWN_ROUTER", "1")
|
||||
process.p_spawn_hold_said = False
|
||||
|
||||
async def boom():
|
||||
raise AssertionError("the installer ran under a held spawn")
|
||||
|
||||
monkeypatch.setattr(process, "p_ensure_running_impl", boom)
|
||||
seen = []
|
||||
handler = logging.Handler()
|
||||
handler.emit = lambda rec: seen.append(rec.getMessage())
|
||||
process.logger.addHandler(handler)
|
||||
try:
|
||||
await process.ensure_running()
|
||||
await process.ensure_running()
|
||||
finally:
|
||||
process.logger.removeHandler(handler)
|
||||
said = [m for m in seen if "NOT starting the router" in m]
|
||||
assert len(said) == 1, said
|
||||
|
||||
|
||||
def test_the_suite_and_ci_declare_the_hold():
|
||||
import os
|
||||
import pathlib
|
||||
assert os.environ.get("OSW_NEVER_SPAWN_ROUTER") == "1", "conftest must set it"
|
||||
ci = pathlib.Path(".github/workflows/suites-matrix.yml").read_text(encoding="utf-8")
|
||||
assert "OSW_NEVER_SPAWN_ROUTER: '1'" in ci
|
||||
@@ -85,9 +85,10 @@ test('the component CSS still matches the constants this file tests', async () =
|
||||
const url = await import('node:url');
|
||||
const here = url.fileURLToPath(new URL('.', import.meta.url));
|
||||
// The test runs from .test-build, so resolve the source next to it by name.
|
||||
// Both separators: on Windows `here` is D:\...\.test-build\..., and a '/'-only replace found nothing (CI, 2026-09-06).
|
||||
const candidates = [
|
||||
here + 'AgentChat.tsx',
|
||||
here.replace('/.test-build/', '/src/') + 'AgentChat.tsx',
|
||||
here.replace(/([\\/])\.test-build([\\/])/, '$1src$2') + 'AgentChat.tsx',
|
||||
];
|
||||
const path = candidates.find((p) => fs.existsSync(p));
|
||||
assert.ok(path, `could not locate AgentChat.tsx from ${here}`);
|
||||
|
||||
Reference in New Issue
Block a user