Files
OpenJarvis/tests/server/test_api_routes.py
T
2bdd860b54 test: tolerate a 503 memory backend in the route-wiring tests (#685)
``TestMemoryRoutes.test_search`` and ``test_stats`` assert the status code
is in ``(200, 500)``. That list dates from the initial commit; #527 later
made the memory routes raise 503 when the native ``openjarvis_rust``
extension is missing, so both tests now fail on any checkout where the
extension has not been built — which is every contributor who has not run
``maturin develop``.

The failure is spurious: these two tests only check that the routes are
wired up, and their own comment ("May fail if SQLite not set up, that's
ok") says an unavailable backend is tolerated. 503 is exactly that case,
and it is already asserted deliberately in ``TestMemoryRustMissing``
directly below.

Add 503 to the tolerated set via a named constant, so the reason is stated
once rather than repeated as a bare literal.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:17:25 -07:00

150 lines
5.1 KiB
Python

"""Tests for extended API routes."""
import pytest
fastapi = pytest.importorskip("fastapi")
from fastapi import FastAPI # noqa: E402
from fastapi.testclient import TestClient # noqa: E402
from openjarvis.server.api_routes import include_all_routes # noqa: E402
def _make_app():
app = FastAPI()
include_all_routes(app)
return app
class TestAgentRoutes:
def test_list_agents(self):
client = TestClient(_make_app())
resp = client.get("/v1/agents")
assert resp.status_code == 200
data = resp.json()
assert "registered" in data
assert "running" in data
def test_create_agent(self):
client = TestClient(_make_app())
resp = client.post("/v1/agents", json={"agent_type": "simple"})
# May succeed or fail depending on agent_tools availability
assert resp.status_code in (200, 501)
def test_kill_nonexistent(self):
client = TestClient(_make_app())
resp = client.delete("/v1/agents/nonexistent")
assert resp.status_code in (404, 501)
class TestMemoryRoutes:
# 503 is the documented response when the native ``openjarvis_rust``
# extension is absent from the venv (see TestMemoryRustMissing below).
# These tests are only asserting "the route is wired up", so a backend
# that cannot be built is tolerated the same way a 500 is.
_BACKEND_OPTIONAL = (200, 500, 503)
def test_search(self):
client = TestClient(_make_app())
resp = client.post("/v1/memory/search", json={"query": "test"})
# May fail if SQLite not set up, that's ok
assert resp.status_code in self._BACKEND_OPTIONAL
def test_stats(self):
client = TestClient(_make_app())
resp = client.get("/v1/memory/stats")
assert resp.status_code in self._BACKEND_OPTIONAL
class TestMemoryRustMissing:
"""Regression for #502: when the native ``openjarvis_rust`` extension is
missing from the serving venv, memory ops must surface a CLEAR, ACTIONABLE
error — never the misleading "Failed to index path" or a 200 silent no-op.
"""
@staticmethod
def _client(monkeypatch):
# Force the same failure mode as a venv without the compiled extension.
def _boom():
raise ImportError("No module named 'openjarvis_rust'")
import openjarvis._rust_bridge as bridge
monkeypatch.setattr(bridge, "get_rust_module", _boom)
return TestClient(_make_app())
def test_store_is_not_a_silent_noop(self, monkeypatch):
client = self._client(monkeypatch)
resp = client.post("/v1/memory/store", json={"content": "hi"})
# Must NOT return the old 200 {"status":"stored","note":"no backend..."}.
assert resp.status_code == 503
detail = resp.json()["detail"]
assert "openjarvis_rust" in detail
assert "maturin develop" in detail
def test_index_surfaces_actionable_detail(self, monkeypatch, tmp_path):
(tmp_path / "note.txt").write_text("hello world some content here")
client = self._client(monkeypatch)
resp = client.post("/v1/memory/index", json={"path": str(tmp_path)})
assert resp.status_code == 503
detail = resp.json()["detail"]
# The frontend reads this `detail`; it must point at the real cause,
# not blame the indexed path.
assert "openjarvis_rust" in detail
assert detail != "Failed to index path"
assert detail != "No memory backend available"
def test_config_reports_unavailable(self, monkeypatch):
client = self._client(monkeypatch)
resp = client.get("/v1/memory/config")
assert resp.status_code == 200
data = resp.json()
# Must not falsely report a healthy backend when none could be built.
assert data["available"] is False
assert "openjarvis_rust" in (data["detail"] or "")
class TestBudgetRoutes:
def test_get_budget(self):
client = TestClient(_make_app())
resp = client.get("/v1/budget")
assert resp.status_code == 200
data = resp.json()
assert "limits" in data
assert "usage" in data
def test_set_limits(self):
client = TestClient(_make_app())
resp = client.put("/v1/budget/limits", json={"max_tokens_per_day": 100000})
assert resp.status_code == 200
assert resp.json()["limits"]["max_tokens_per_day"] == 100000
class TestMetricsRoute:
def test_metrics_endpoint(self):
client = TestClient(_make_app())
resp = client.get("/metrics")
assert resp.status_code == 200
assert "openjarvis" in resp.text or "No metrics" in resp.text
class TestSkillRoutes:
def test_list_skills(self):
client = TestClient(_make_app())
resp = client.get("/v1/skills")
assert resp.status_code == 200
assert "skills" in resp.json()
class TestSessionRoutes:
def test_list_sessions(self):
client = TestClient(_make_app())
resp = client.get("/v1/sessions")
assert resp.status_code == 200
class TestTraceRoutes:
def test_list_traces(self):
client = TestClient(_make_app())
resp = client.get("/v1/traces")
assert resp.status_code == 200