Repair cross-platform test isolation and retain Docker failure evidence

Use short pytest labels without reducing large Unicode inputs, block provider/catalog calls at their existing seams instead of OS-local sockets, and decode UTF-8 task-state fixtures explicitly. Retain browser/container facts on Docker UI failure without changing the acceptance condition or timeout. Seven test files only; production behavior is unchanged. Focused combined Windows-related modules: 72 passed.

Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
This commit is contained in:
Anton Razzhigaev 2026-09-09 01:04:57 +03:00
parent 081a9d3886
commit cc7269a5d5
7 changed files with 65 additions and 24 deletions

View file

@ -34,7 +34,7 @@ def _record(root, raw, *, closed=True, findings=None, evidence_manifest=None):
def _raw_state(root, task_id="large-plan"):
return json.loads(task_results.task_result_path(root, task_id).read_text())["plan_review_state"]
return json.loads(task_results.task_result_path(root, task_id).read_text(encoding="utf-8"))["plan_review_state"]
@pytest.mark.parametrize("field", ["goal", "acceptance_claims", "in_scope"])

View file

@ -763,7 +763,7 @@ def test_an_ordinary_contract_field_reaches_the_run_instructions_complete(tmp_pa
@pytest.mark.parametrize("objective", [
"UNIQUE_ATOMIC_OBJECTIVE",
"яё𐍈🚀\n" * 55_000 + "LARGE_COMPILED_OBJECTIVE_TAIL",
])
], ids=["short", "large-unicode"])
def test_atomic_compiled_work_order_sends_dynamic_brief_once(tmp_path, monkeypatch, objective):
from ouroboros.subagent_work_order import compile_external_work_order

View file

@ -101,7 +101,6 @@ def test_answer_frame_describes_only_work_that_continued(required):
def test_expired_wait_clock_precedes_post_tool_budget(tmp_path, monkeypatch, cold):
"""A saved tail may have spent its budget, but an expired clock keeps its cause."""
import json
import socket
import time
from ouroboros import loop, model_wait, owner_wait, task_pacing
from tests.test_owner_wait_cold_loop import cold_registry
@ -109,8 +108,9 @@ def test_expired_wait_clock_precedes_post_tool_budget(tmp_path, monkeypatch, col
def no_network(*_args, **_kwargs):
pytest.fail("synthetic wait must never contact a provider")
monkeypatch.setattr(socket.socket, "connect", no_network)
monkeypatch.setattr(socket.socket, "connect_ex", no_network)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat", no_network)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat_async", no_network)
monkeypatch.setattr("ouroboros.pricing._fetch_live_rows", no_network)
monkeypatch.setenv("OUROBOROS_TASK_ABS_CEILING_SEC", "21600")
registry = cold_registry(tmp_path, monkeypatch, task_pacing.CostCeiling(state="active", ceiling_usd=9.0))
ctx = registry._ctx

View file

@ -1,7 +1,6 @@
"""A cold grant finishes the saved round before another ordinary model/tool call."""
import json
import socket
from dataclasses import replace
import pytest
@ -30,9 +29,9 @@ def test_cold_grant_checks_saved_budget_before_ordinary_dispatch(tmp_path, monke
accounting.settle_attempt(held, {"prompt_tokens": 1, "completion_tokens": 1},
cost_usd=amount, cost_final=True)
def refuse_network(*args):
network.append(args)
raise AssertionError("fixture attempted network access")
def refuse_network(*args, **kwargs):
network.append((args, kwargs))
raise AssertionError("fixture attempted a provider call")
def forced(call, **kwargs):
calls.append((phase, "forced", call.round_idx, call.active_model))
@ -55,8 +54,8 @@ def test_cold_grant_checks_saved_budget_before_ordinary_dispatch(tmp_path, monke
return {"role": "assistant", "content": "", "tool_calls": [{"id": phase, "type": "function",
"function": {"name": name, "arguments": json.dumps(args)}}]}, .2
monkeypatch.setattr(socket.socket, "connect", refuse_network)
monkeypatch.setattr(socket.socket, "connect_ex", refuse_network)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat", refuse_network)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat_async", refuse_network)
monkeypatch.setattr(pricing, "_fetch_live_rows", lambda provider: {})
monkeypatch.setattr(loop, "_dispatch_round_model", ordinary)
monkeypatch.setattr(loop, "_call_forced_model_once", forced)

View file

@ -1,5 +1,6 @@
"""Cold continuation reaches the real loop with its original route and ceiling."""
import asyncio
from dataclasses import replace
import json
import socket
@ -21,19 +22,27 @@ from tests.test_loop_transport_wait import _loop_kwargs
@pytest.fixture(autouse=True)
def no_network(monkeypatch):
"""These synthetic model/route fixtures must never reach a provider or catalog."""
"""Block provider/catalog calls, leaving asyncio's local IPC available."""
attempted = []
def refuse_connect(_socket, address):
attempted.append(address)
raise AssertionError("cold-loop fixture attempted network access")
def refuse_provider(*args, **kwargs):
attempted.append((args, kwargs))
raise AssertionError("cold-loop fixture attempted a provider/catalog call")
monkeypatch.setattr(socket.socket, "connect", refuse_connect)
monkeypatch.setattr(socket.socket, "connect_ex", refuse_connect)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat", refuse_provider)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat_async", refuse_provider)
monkeypatch.setattr("ouroboros.pricing._fetch_live_rows", refuse_provider)
yield
assert attempted == []
def test_provider_isolation_preserves_event_loop_self_pipe(monkeypatch):
# Windows uses this TCP fallback for asyncio's internal wakeup channel.
monkeypatch.setattr(socket, "socketpair", socket._fallback_socketpair)
event_loop = asyncio.new_event_loop()
event_loop.close()
def cold_registry(tmp_path, monkeypatch, ceiling=None):
registry = ToolRegistry(repo_dir=tmp_path, drive_root=tmp_path)
ctx = registry._ctx

View file

@ -4,7 +4,6 @@ import copy
from dataclasses import replace
import json
import queue as stdqueue
import socket
import threading
import time
from types import SimpleNamespace
@ -24,14 +23,16 @@ from tests.test_loop_transport_wait import _loop_kwargs
@pytest.fixture(autouse=True)
def no_network(monkeypatch):
"""Block provider/catalog calls without interfering with local event-loop IPC."""
attempted = []
def refuse(*args):
attempted.append(args)
raise AssertionError("owner-wait model-state fixture attempted network")
def refuse(*args, **kwargs):
attempted.append((args, kwargs))
raise AssertionError("owner-wait model-state fixture attempted a provider/catalog call")
monkeypatch.setattr(socket.socket, "connect", refuse)
monkeypatch.setattr(socket.socket, "connect_ex", refuse)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat", refuse)
monkeypatch.setattr("ouroboros.llm.LLMClient.chat_async", refuse)
monkeypatch.setattr("ouroboros.pricing._fetch_live_rows", refuse)
monkeypatch.setenv("OUROBOROS_CONTEXT_MODE", "max")
monkeypatch.setenv("OUROBOROS_TASK_REVIEW_MODE", "off")
monkeypatch.setenv("OUROBOROS_TASK_ABS_CEILING_SEC", "21600")

View file

@ -9,6 +9,7 @@ import sys
import textwrap
import time
import urllib.request
from collections import deque
import pytest
@ -189,14 +190,36 @@ def _run_docker_ui_assertions(url: str) -> None:
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 390, "height": 844})
events = deque(maxlen=40)
page.on("console", lambda msg: events.append(["console", msg.type, msg.text[:1000]]))
page.on("pageerror", lambda error: events.append(["pageerror", str(error)[:1000]]))
page.on("requestfailed", lambda req: events.append(["requestfailed", req.url, req.failure]))
page.on("response", lambda res: events.append(["response", res.status, res.url])
if res.request.resource_type in {"document", "script"} or res.status >= 400 else None)
response = None
try:
page.goto(url, wait_until="domcontentloaded", timeout=30_000)
response = page.goto(url, wait_until="domcontentloaded", timeout=30_000)
if page.locator("#onboarding-overlay").count():
overlay_text = page.locator("#onboarding-overlay").inner_text(timeout=5_000)
if "Ouroboros" in overlay_text:
return
page.wait_for_selector("#page-chat", timeout=30_000)
assert page.locator("#page-chat").count() == 1
except Exception:
print("DOCKER_UI_BROWSER_EVENTS " + json.dumps(list(events)), file=sys.stderr)
try:
print("DOCKER_UI_PAGE " + json.dumps({
"url": page.url, "title": page.title(),
"document_status": response.status if response is not None else None,
"nodes": {selector: {"count": page.locator(selector).count(),
"visible": page.locator(selector).is_visible()}
for selector in ("#page-chat", "#onboarding-overlay", ".onboarding-frame")},
"body": page.locator("body").inner_text(timeout=1000)[:4000],
"html": page.content()[:8000],
}), file=sys.stderr)
except Exception as diagnostic_error:
print(f"DOCKER_UI_PAGE unavailable: {diagnostic_error}", file=sys.stderr)
raise
finally:
browser.close()
except PlaywrightError as exc:
@ -2221,6 +2244,15 @@ def test_ui_smoke_docker_mode_loads_health():
url = f"http://127.0.0.1:{port}"
_wait_health(url, timeout_sec=45)
_run_docker_ui_assertions(url)
except Exception:
try:
logs = subprocess.run(["docker", "logs", "--tail", "100", cid],
capture_output=True, text=True, timeout=10)
print(f"DOCKER_UI_CONTAINER {cid} logs_exit={logs.returncode}\n"
f"{logs.stdout[-10000:]}\n{logs.stderr[-10000:]}", file=sys.stderr)
except Exception as diagnostic_error:
print(f"DOCKER_UI_CONTAINER logs unavailable: {diagnostic_error}", file=sys.stderr)
raise
finally:
subprocess.run(["docker", "stop", cid], capture_output=True, text=True, timeout=30)