mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-14 05:37:40 +02:00
Each removal verified by checking actual production callers (frontend,
electron, internal HTTP, MCP-server subprocesses) — not just test
references. Symbols whose only callers were tests are removed along
with those tests.
Production removals (~390 LOC):
- backend/main.py
- websocket_session: drop `agent:edit_message` WS branch. Frontend
only ever uses HTTP `POST /api/agents/sessions/{id}/edit_message`
(frontend/src/shared/state/agentsSlice.ts); nothing on the wire
sends a WS message of this type.
- backend/apps/agents/agent_manager.py
- AgentManager._build_connected_tools_context (~80 LOC): zero call
sites in production; the connected-tools system-prompt context is
built inline in _compose_system_prompt now.
- AgentManager._approx_tokens / _summarize_message_block: pure
helpers whose only callers were tests. The compaction path uses
LLM-driven _maybe_compact instead.
- backend/apps/agents/browser_agent.py
- clear_browser_history: only used by tests. _browser_history is
pruned via the size cap inline.
- MODEL_MAP constant: never read.
- backend/apps/agents/mcp_preflight.py
- DISCOVERY_SCAFFOLDING (~25-line system-prompt block): defined but
never appended anywhere. The header comment described an intended
use that the codebase no longer has.
- backend/apps/agents/providers/registry.py
- thinking_params_for, _is_9router_available, OPENROUTER_BASE_URL,
get_context_window: zero callers in production. Thinking-params
routing is done by the provider classes directly; 9Router presence
is detected at request time; context-window numbers are stamped
onto sessions from BUILTIN_MODELS at launch.
- backend/apps/agents/tools/{base,web}.py
- BaseTool.get_schema (abstract) + WebSearchTool/WebFetchTool
overrides: production code in backend/apps/web/web.py instantiates
these tools and only calls .execute(); the JSON-schema lives in
the HTTP wrapper, not on the tool class.
- backend/apps/outputs/outputs.py
- _resolve_model + MODEL_MAP: tests-only.
- load_output: docstring claimed it was a public helper for "other
modules" but no module imported it.
- backend/apps/service/client.py
- set_user_id, the _user_id module global, and the dead cache short-
circuit in _get_user_id: setter was tests-only. _get_user_id now
reads user_email directly from settings on every call.
- backend/apps/settings/credentials.py
- get_provider_credentials: zero callers. The sibling get_agent_sdk_env
is kept (it has the explicit "Legacy helpers" keep-comment).
Test updates:
- test_agent_manager_unit.py: drop _approx_tokens / _summarize_message_block
cases (5 tests), update module docstring index.
- test_browser_agent_unit.py: drop clear_browser_history cases (2 tests)
and the unused _Boom helper class in the repr-fallback test.
- test_outputs_unit.py: drop _resolve_model / load_output cases
(4 tests), update docstring + import list.
- test_v2_invariants.py: drop get_context_window tests + get_schema
assertions on web tools (kept name + BaseTool inheritance checks).
- test_service.py: rewrite the 4 set_user_id-driven tests to drive
user_id through settings.user_email instead, so _get_user_id's live
envelope-stamping path stays covered.
Verification:
- ruff --select F401,F811,F841 backend/ → clean.
- pytest backend/tests/ → 1167 passed, 1 deselected (pre-existing
sandbox git test, unrelated). No tests dropped silently — every
deletion is paired with the corresponding test removal/rewrite.
- Dead-code scan re-run: dead WS events 1→0, Tier-2 high-confidence
14→11 (residue is SDK-callback `context` params + Pydantic `cls`
validators — both false positives vulture can't see through),
vulture total 165→145.
Total diff: -565 / +34 LOC across 15 files.
Co-authored-by: Cursor <cursoragent@cursor.com>
351 lines
10 KiB
Python
351 lines
10 KiB
Python
"""Tests for the service-sync layer.
|
|
|
|
Public surface is a single `sync(data)` function. The desktop hands off
|
|
opaque dicts; the cloud determines what they are. Tests verify:
|
|
|
|
- Envelope (install_id, user_id) stamped on every submission
|
|
- Opt-out gate works
|
|
- Test sink intercepts every sync
|
|
- Spool round-trip (enqueue/drain/acknowledge)
|
|
- Legacy shims (submit, record, identify) route through sync
|
|
|
|
Run:
|
|
cd backend && python -m pytest tests/test_service.py -v
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
import time
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
_tmpdir = tempfile.mkdtemp()
|
|
os.environ.setdefault("OPENSWARM_DATA_DIR", _tmpdir)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def patch_settings(tmp_path):
|
|
sf = tmp_path / "settings.json"
|
|
sf.write_text(json.dumps({
|
|
"installation_id": "test-install-abc",
|
|
"analytics_opt_in": True,
|
|
}))
|
|
import backend.apps.settings.settings as settings_mod
|
|
old = settings_mod.SETTINGS_FILE
|
|
settings_mod.SETTINGS_FILE = str(sf)
|
|
yield
|
|
settings_mod.SETTINGS_FILE = old
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def fresh_client(tmp_path):
|
|
import backend.apps.service.client as client
|
|
client._install_id = None
|
|
client._test_sink = None
|
|
spool = tmp_path / "spool.db"
|
|
with patch.object(client, "_spool_path", lambda: str(spool)):
|
|
yield
|
|
|
|
|
|
@pytest.fixture
|
|
def sink():
|
|
captured: list[tuple[str, dict]] = []
|
|
import backend.apps.service.client as client
|
|
client.set_test_sink(lambda kind, body: captured.append((kind, body)))
|
|
yield captured
|
|
client.set_test_sink(None)
|
|
|
|
|
|
# --- core sync ---------------------------------------------------------------
|
|
|
|
def test_sync_basic(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({"foo": "bar"})
|
|
assert len(sink) == 1
|
|
_, body = sink[0]
|
|
assert body["d"] == {"foo": "bar"}
|
|
|
|
|
|
def test_sync_carries_install_id(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
assert body["client_state"]["install_id"] == "test-install-abc"
|
|
|
|
|
|
def test_sync_carries_user_id_when_set_in_settings(tmp_path, sink):
|
|
"""User id is read from settings.user_email at envelope-build time."""
|
|
sf = tmp_path / "settings_with_email.json"
|
|
sf.write_text(json.dumps({
|
|
"installation_id": "test-install-abc",
|
|
"analytics_opt_in": True,
|
|
"user_email": "alice@example.com",
|
|
}))
|
|
import backend.apps.settings.settings as settings_mod
|
|
with patch.object(settings_mod, "SETTINGS_FILE", str(sf)):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
assert body["client_state"]["user_id"] == "alice@example.com"
|
|
|
|
|
|
def test_sync_no_user_id_when_email_not_set(sink):
|
|
"""No user_email in settings → user_id absent from envelope."""
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
assert "user_id" not in body["client_state"]
|
|
|
|
|
|
def test_sync_no_user_id_when_email_empty(tmp_path, sink):
|
|
"""Empty-string user_email is treated like missing."""
|
|
sf = tmp_path / "settings_empty_email.json"
|
|
sf.write_text(json.dumps({
|
|
"installation_id": "test-install-abc",
|
|
"analytics_opt_in": True,
|
|
"user_email": "",
|
|
}))
|
|
import backend.apps.settings.settings as settings_mod
|
|
with patch.object(settings_mod, "SETTINGS_FILE", str(sf)):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
assert "user_id" not in body["client_state"]
|
|
|
|
|
|
def test_sync_environment_metadata(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
cs = body["client_state"]
|
|
assert cs.get("device_type") == "desktop"
|
|
assert cs.get("os")
|
|
assert cs.get("os_version")
|
|
|
|
|
|
def test_sync_payload_round_trips(sink):
|
|
from backend.apps.service.client import sync
|
|
data = {"deeply": {"nested": [1, 2]}, "flag": True, "n": 3.14}
|
|
sync(data)
|
|
_, body = sink[0]
|
|
assert body["d"] == data
|
|
|
|
|
|
def test_sync_empty_data(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
assert len(sink) == 1
|
|
|
|
|
|
def test_sync_none_treated_as_empty(sink):
|
|
from backend.apps.service.client import sync
|
|
sync(None)
|
|
_, body = sink[0]
|
|
assert body["d"] == {}
|
|
|
|
|
|
def test_sync_timestamp_present(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
assert isinstance(body["t"], float)
|
|
assert body["t"] > 0
|
|
|
|
|
|
# --- opt-out gating ----------------------------------------------------------
|
|
|
|
def test_opt_out_blocks_sync(sink, tmp_path):
|
|
sf = tmp_path / "minimal.json"
|
|
sf.write_text(json.dumps({
|
|
"installation_id": "test-install-abc",
|
|
"analytics_opt_in": False,
|
|
}))
|
|
import backend.apps.settings.settings as settings_mod
|
|
settings_mod.SETTINGS_FILE = str(sf)
|
|
from backend.apps.service.client import sync
|
|
sync({"x": 1})
|
|
assert sink == []
|
|
|
|
|
|
def test_standard_mode_allows_sync(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
sync({})
|
|
assert len(sink) == 2
|
|
|
|
|
|
def test_settings_load_failure_defaults_to_enabled(sink):
|
|
import backend.apps.settings.settings as settings_mod
|
|
settings_mod.SETTINGS_FILE = "/nonexistent/path/settings.json"
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
assert len(sink) == 1
|
|
|
|
|
|
# --- legacy shims ------------------------------------------------------------
|
|
|
|
def test_legacy_submit_routes_through_sync(sink):
|
|
from backend.apps.service.client import submit
|
|
submit("event", {"test": True})
|
|
assert len(sink) == 1
|
|
_, body = sink[0]
|
|
assert body["d"] == {"test": True}
|
|
|
|
|
|
def test_legacy_record_routes_through_sync(sink):
|
|
from backend.apps.service.client import record
|
|
record("some.event", {"k": "v"})
|
|
assert len(sink) == 1
|
|
|
|
|
|
def test_legacy_identify_routes_through_sync(sink):
|
|
from backend.apps.service.client import identify
|
|
identify({"plan": "pro"})
|
|
assert len(sink) == 1
|
|
|
|
|
|
def test_legacy_submit_session_close(sink):
|
|
from backend.apps.service.client import submit_session_close
|
|
submit_session_close({"id": "s-1", "cost_usd": 0.42})
|
|
assert len(sink) == 1
|
|
|
|
|
|
def test_legacy_submit_diagnostic(sink):
|
|
from backend.apps.service.client import submit_diagnostic
|
|
submit_diagnostic({"kind": "error_caught"})
|
|
assert len(sink) == 1
|
|
|
|
|
|
# --- spool -------------------------------------------------------------------
|
|
|
|
def test_buffer_enqueue_and_drain(tmp_path):
|
|
from backend.apps.service import buffer
|
|
spool = str(tmp_path / "s.db")
|
|
buffer.enqueue(spool, "s:/api/service/sync", {"a": 1}, now=time.time())
|
|
buffer.enqueue(spool, "s:/api/service/sync", {"a": 2}, now=time.time())
|
|
assert buffer.count(spool) == 2
|
|
rows = buffer.drain(spool, batch_size=10)
|
|
assert [r[2]["a"] for r in rows] == [1, 2]
|
|
buffer.acknowledge(spool, [r[0] for r in rows])
|
|
assert buffer.count(spool) == 0
|
|
|
|
|
|
def test_buffer_drain_partial(tmp_path):
|
|
from backend.apps.service import buffer
|
|
spool = str(tmp_path / "s.db")
|
|
for i in range(5):
|
|
buffer.enqueue(spool, "s:/x", {"i": i}, now=time.time())
|
|
rows = buffer.drain(spool, batch_size=2)
|
|
assert len(rows) == 2
|
|
assert buffer.count(spool) == 5
|
|
buffer.acknowledge(spool, [r[0] for r in rows])
|
|
assert buffer.count(spool) == 3
|
|
|
|
|
|
def test_buffer_clear(tmp_path):
|
|
from backend.apps.service import buffer
|
|
spool = str(tmp_path / "s.db")
|
|
buffer.enqueue(spool, "s:/x", {}, now=time.time())
|
|
buffer.clear(spool)
|
|
assert buffer.count(spool) == 0
|
|
|
|
|
|
def test_buffer_missing_file(tmp_path):
|
|
from backend.apps.service import buffer
|
|
assert buffer.count(str(tmp_path / "nope.db")) == 0
|
|
assert buffer.drain(str(tmp_path / "nope.db")) == []
|
|
|
|
|
|
def test_buffer_corrupt_row_dropped(tmp_path):
|
|
from backend.apps.service import buffer
|
|
spool = str(tmp_path / "s.db")
|
|
with buffer._conn(spool) as c:
|
|
c.execute(
|
|
"INSERT INTO spool (kind, payload, created_at) VALUES (?, ?, ?)",
|
|
("s:/x", "{not json", time.time()),
|
|
)
|
|
rows = buffer.drain(spool)
|
|
assert rows == []
|
|
assert buffer.count(spool) == 0
|
|
|
|
|
|
def test_buffer_size_cap(tmp_path):
|
|
from backend.apps.service import buffer
|
|
spool = str(tmp_path / "s.db")
|
|
big = "x" * 1024
|
|
for i in range(200):
|
|
buffer.enqueue(spool, "s:/x", {"i": i, "pad": big}, now=time.time())
|
|
assert buffer.count(spool) == 200
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_drain_spool_empty():
|
|
from backend.apps.service.client import drain_spool
|
|
n = await drain_spool()
|
|
assert n == 0
|
|
|
|
|
|
# --- identity ----------------------------------------------------------------
|
|
|
|
def test_install_id_persisted(sink, tmp_path):
|
|
sf = tmp_path / "fresh.json"
|
|
sf.write_text(json.dumps({"analytics_opt_in": True}))
|
|
import backend.apps.settings.settings as settings_mod
|
|
settings_mod.SETTINGS_FILE = str(sf)
|
|
import backend.apps.service.client as client
|
|
client._install_id = None
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
_, body = sink[0]
|
|
iid = body["client_state"]["install_id"]
|
|
assert iid
|
|
raw = json.loads(sf.read_text())
|
|
assert raw["installation_id"] == iid
|
|
|
|
|
|
def test_install_id_stable(sink):
|
|
from backend.apps.service.client import sync
|
|
sync({})
|
|
sync({})
|
|
iid1 = sink[0][1]["client_state"]["install_id"]
|
|
iid2 = sink[1][1]["client_state"]["install_id"]
|
|
assert iid1 == iid2
|
|
|
|
|
|
# --- SubApp endpoints --------------------------------------------------------
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_endpoint_submit(sink):
|
|
from backend.apps.service.service import post_submit
|
|
res = await post_submit({"kind": "state", "payload": {"x": 1}})
|
|
assert res == {"ok": True}
|
|
assert len(sink) == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_endpoint_submit_missing_payload(sink):
|
|
from backend.apps.service.service import post_submit
|
|
res = await post_submit({"kind": "state"})
|
|
assert res["ok"] is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_endpoint_event_happy(sink):
|
|
from backend.apps.service.service import post_event
|
|
res = await post_event({"surface": "test", "action": "happy"})
|
|
assert res == {"ok": True}
|
|
assert len(sink) == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_endpoint_event_missing_surface(sink):
|
|
from backend.apps.service.service import post_event
|
|
res = await post_event({"action": "x"})
|
|
assert res["ok"] is False
|
|
|
|
|