Files
odysseus/tests/test_session_load_404s.py
T
pewdiepie-archdaemonandClaude Opus 5.5 364229f2dc feat(harness): authority, turn contract, tools, clean_agent_preview
Chat/agent harness work: request-authority model, turn contract, tool
policy/parsing/execution, clean_agent_preview streaming/thinking, ask_user
persistence, session probes, and scheduler.

Source: vendor/photocraft (ad1cff44) harness files, 3-way merged onto
upstream/dev (a8c147b2). Harness core files were independently developed
in both lineages; resolution policy documented in pr_work/C_conflicts.md
(vendor kept where ahead, upstream kept where vendor is behind; full delta
preserved in pr_work/C_upstream_delta.patch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:41:53 +00:00

109 lines
4.1 KiB
Python

"""Session-load 404 regression (passover item #9).
Loading a chat session used to fire two benign 404s in the browser console:
GET /api/research/status/<id> (no research for this session)
GET /api/chat/stream_status/<id> (no active stream for this session)
Both are now a normal "empty" state: HTTP 200 with an explicit status
("none" / "idle"). The clients already branch on the body status, so this is
backward compatible (an older server's 404 is still tolerated).
"""
import asyncio
import inspect
from types import SimpleNamespace
from unittest.mock import MagicMock
from routes import chat_routes
from routes import research_routes
def _route_endpoint(router, path, method="GET"):
for route in router.routes:
if getattr(route, "path", "") != path:
continue
if method in getattr(route, "methods", set()):
return route.endpoint
raise AssertionError(f"{method} {path} not registered")
def _req(user="alice"):
return SimpleNamespace(state=SimpleNamespace(current_user=user))
# --- /api/research/status/<id> -------------------------------------------
def test_research_status_returns_200_none_when_absent(tmp_path, monkeypatch):
monkeypatch.setattr(research_routes, "DEEP_RESEARCH_DIR", str(tmp_path / "deep_research"))
handler = MagicMock()
handler._active_tasks = {}
router = research_routes.setup_research_routes(handler)
endpoint = _route_endpoint(router, "/api/research/status/{session_id}")
out = asyncio.run(endpoint(session_id="sess-abc", request=_req()))
assert out == {"status": "none"}
def test_research_status_returns_status_when_present(tmp_path, monkeypatch):
monkeypatch.setattr(research_routes, "DEEP_RESEARCH_DIR", str(tmp_path / "deep_research"))
handler = MagicMock()
handler._active_tasks = {"sess-abc": {"owner": "alice"}}
handler.get_status.return_value = {"status": "running", "progress": {"phase": "searching"}}
handler.get_source_state.return_value = None
handler.get_source_coverage.return_value = None
handler.get_navigation_trace.return_value = None
handler.get_action_trace.return_value = None
router = research_routes.setup_research_routes(handler)
endpoint = _route_endpoint(router, "/api/research/status/{session_id}")
out = asyncio.run(endpoint(session_id="sess-abc", request=_req()))
assert out["status"] == "running"
# --- /api/chat/stream_status/<id> ----------------------------------------
def _chat_stream_status_endpoint(monkeypatch):
monkeypatch.setattr(chat_routes, "_verify_session_owner", lambda request, session_id: None)
router = chat_routes.setup_chat_routes(
MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock()
)
return _route_endpoint(router, "/api/chat/stream_status/{session_id}")
def test_stream_status_returns_200_idle_when_no_stream(monkeypatch):
endpoint = _chat_stream_status_endpoint(monkeypatch)
chat_routes._active_streams.pop("sess-idle", None)
out = asyncio.run(endpoint(SimpleNamespace(), "sess-idle"))
assert out == {"status": "idle"}
def test_stream_status_returns_record_when_streaming(monkeypatch):
endpoint = _chat_stream_status_endpoint(monkeypatch)
chat_routes._active_streams["sess-live"] = {"status": "streaming", "mode": "agent"}
try:
out = asyncio.run(endpoint(SimpleNamespace(), "sess-live"))
finally:
chat_routes._active_streams.pop("sess-live", None)
assert out["status"] == "streaming"
# --- client compatibility (source-level) ---------------------------------
def test_chat_watchdog_accepts_200_idle_body():
src = inspect.getsource(chat_routes) # ensure chat_routes imported
chatjs = open("static/js/chat.js", encoding="utf-8").read()
# The stale-stream watchdog must key off the body status, not only a 404.
assert "_noActiveStream" in chatjs
assert "_probe.status !== 'streaming'" in chatjs
assert src # keep flake happy
def test_sessions_check_uses_body_status_not_ok_only():
sj = open("static/js/sessions.js", encoding="utf-8").read()
assert "info.status !== 'streaming'" in sj
assert "status !== 'streaming'" in sj