mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[arnav] remove 6 dead HTTP endpoints
Verified each had no production caller (frontend, electron, or internal backend) — only test references kept them looking alive to coverage tools. Drops ~140 LOC of route handlers + helpers. - POST /api/outputs/vibe-code: never wired up; frontend has no vibe-code UI. Also drops VibeCodeRequest model and VIBE_CODE_SYSTEM_PROMPT. - GET /api/service/cost-breakdown: frontend Usage page reads /usage-summary, which already returns by_model/by_provider. - GET /api/service/status: zero callers; was placeholder. - GET /api/service/spool/count: debug-only, no UI surface. - GET /api/settings/default-system-prompt: frontend defines its own DEFAULT_SYSTEM_PROMPT in settingsSlice.ts and never fetches the backend constant. - POST /api/browser/command: sole caller (browser_mcp_server.py subprocess) was deleted in 8286cc1; browser_agent.py now calls ws_manager.send_browser_command directly in-process. Tests covering the removed endpoints are dropped along with the now-unused mock-anthropic helpers in test_api_outputs.py.
This commit is contained in:
@@ -153,16 +153,6 @@ async def update_session(session_id: str, body: dict):
|
||||
await agent_manager.update_session(session_id, **body)
|
||||
return {"ok": True}
|
||||
|
||||
@agents.router.get("/sessions/{session_id}/branches")
|
||||
async def get_branches(session_id: str):
|
||||
session = agent_manager.get_session(session_id)
|
||||
if not session:
|
||||
raise HTTPException(status_code=404, detail="Session not found")
|
||||
return {
|
||||
"branches": {k: v.model_dump(mode="json") for k, v in session.branches.items()},
|
||||
"active_branch_id": session.active_branch_id,
|
||||
}
|
||||
|
||||
@agents.router.post("/sessions/{session_id}/duplicate")
|
||||
async def duplicate_session(session_id: str, body: dict = {}):
|
||||
try:
|
||||
@@ -319,16 +309,6 @@ async def subscriptions_exchange(body: dict):
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@agents.router.get("/subscriptions/models")
|
||||
async def subscriptions_models():
|
||||
"""List all models available through connected subscriptions."""
|
||||
from backend.apps.nine_router import is_running, get_models
|
||||
if not is_running():
|
||||
return {"models": []}
|
||||
models = await get_models()
|
||||
return {"models": models}
|
||||
|
||||
|
||||
@agents.router.get("/models")
|
||||
async def list_models():
|
||||
"""Return the chat-picker model list grouped by provider.
|
||||
|
||||
@@ -202,10 +202,3 @@ class WorkspaceSeedRequest(BaseModel):
|
||||
return data
|
||||
|
||||
|
||||
class VibeCodeRequest(BaseModel):
|
||||
prompt: str
|
||||
current_frontend_code: str = ""
|
||||
current_backend_code: str = ""
|
||||
current_schema: str = ""
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
|
||||
@@ -13,7 +13,7 @@ from jsonschema import validate as schema_validate, ValidationError as SchemaVal
|
||||
from backend.config.Apps import SubApp
|
||||
from backend.apps.outputs.models import (
|
||||
Output, OutputCreate, OutputUpdate, OutputExecute, OutputExecuteResult,
|
||||
VibeCodeRequest, AutoRunRequest, AutoRunConfig, AutoRunAgentRequest,
|
||||
AutoRunRequest, AutoRunConfig, AutoRunAgentRequest,
|
||||
WorkspaceSeedRequest,
|
||||
)
|
||||
from backend.apps.outputs.executor import execute_backend_code
|
||||
@@ -398,111 +398,6 @@ async def delete_output(output_id: str):
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
VIBE_CODE_SYSTEM_PROMPT = """\
|
||||
You are an expert at building self-contained HTML/JS/CSS applications that run in an iframe.
|
||||
|
||||
The user will describe what they want, and you will generate:
|
||||
1. **frontend_code**: A complete HTML document. React 18 is available via esm.sh CDN.
|
||||
- Use: <script type="importmap">{"imports":{"react":"https://esm.sh/react@18","react-dom/client":"https://esm.sh/react-dom@18/client"}}</script>
|
||||
- Input data is at window.OUTPUT_INPUT (object), backend result at window.OUTPUT_BACKEND_RESULT.
|
||||
2. **input_schema**: A JSON Schema object defining the structured input.
|
||||
3. **backend_code** (optional): Python code where input_data is a global dict and result is a global dict to assign to.
|
||||
4. **name**: A short name for the view.
|
||||
5. **description**: A one-sentence description.
|
||||
6. **message**: A brief explanation of what you did/changed.
|
||||
|
||||
Return ONLY valid JSON with these keys. No markdown fences, no extra text.\
|
||||
"""
|
||||
|
||||
|
||||
@outputs.router.post("/vibe-code")
|
||||
async def vibe_code(body: VibeCodeRequest):
|
||||
"""Use an LLM to generate or iterate on Output code from a natural language prompt."""
|
||||
try:
|
||||
import anthropic
|
||||
except ImportError:
|
||||
return {
|
||||
"message": "anthropic SDK not installed. Install with: pip install anthropic",
|
||||
"frontend_code": body.current_frontend_code,
|
||||
"backend_code": body.current_backend_code,
|
||||
"input_schema": body.current_schema,
|
||||
}
|
||||
|
||||
context_parts = []
|
||||
if body.current_frontend_code:
|
||||
context_parts.append(f"Current frontend code:\n```html\n{body.current_frontend_code}\n```")
|
||||
if body.current_backend_code:
|
||||
context_parts.append(f"Current backend code:\n```python\n{body.current_backend_code}\n```")
|
||||
if body.current_schema:
|
||||
context_parts.append(f"Current input schema:\n```json\n{body.current_schema}\n```")
|
||||
if body.name:
|
||||
context_parts.append(f"Current name: {body.name}")
|
||||
if body.description:
|
||||
context_parts.append(f"Current description: {body.description}")
|
||||
|
||||
user_message = body.prompt
|
||||
if context_parts:
|
||||
user_message = "\n\n".join(context_parts) + "\n\nUser request: " + body.prompt
|
||||
|
||||
from backend.apps.agents.providers.registry import resolve_aux_model
|
||||
try:
|
||||
aux_model, _aux_base = await resolve_aux_model(load_settings(), preferred_tier="sonnet")
|
||||
except ValueError as e:
|
||||
return {
|
||||
"message": f"Error: {str(e)}",
|
||||
"frontend_code": body.current_frontend_code,
|
||||
"backend_code": body.current_backend_code,
|
||||
"input_schema": body.current_schema,
|
||||
}
|
||||
client = _get_anthropic_client(aux_model)
|
||||
try:
|
||||
resp = await client.messages.create(
|
||||
model=aux_model,
|
||||
max_tokens=8000,
|
||||
system=VIBE_CODE_SYSTEM_PROMPT,
|
||||
messages=[{"role": "user", "content": user_message}],
|
||||
)
|
||||
from backend.apps.agents.agent_manager import _safe_resp_text
|
||||
raw = _safe_resp_text(resp).strip()
|
||||
if not raw:
|
||||
return {
|
||||
"message": "Aux model returned no content. Please try again.",
|
||||
"frontend_code": body.current_frontend_code,
|
||||
"backend_code": body.current_backend_code,
|
||||
"input_schema": body.current_schema,
|
||||
}
|
||||
if raw.startswith("```"):
|
||||
raw = raw.split("\n", 1)[1] if "\n" in raw else raw[3:]
|
||||
if raw.endswith("```"):
|
||||
raw = raw[:-3]
|
||||
|
||||
result = json.loads(raw)
|
||||
pass
|
||||
return {
|
||||
"message": result.get("message", "View updated."),
|
||||
"frontend_code": result.get("frontend_code", body.current_frontend_code),
|
||||
"backend_code": result.get("backend_code", body.current_backend_code),
|
||||
"input_schema": result.get("input_schema", body.current_schema),
|
||||
"name": result.get("name", body.name),
|
||||
"description": result.get("description", body.description),
|
||||
}
|
||||
except json.JSONDecodeError:
|
||||
return {
|
||||
"message": "I generated code but couldn't parse the response. Please try again.",
|
||||
"frontend_code": body.current_frontend_code,
|
||||
"backend_code": body.current_backend_code,
|
||||
"input_schema": body.current_schema,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.exception("Vibe code generation failed")
|
||||
return {
|
||||
"message": f"Error: {str(e)}",
|
||||
"frontend_code": body.current_frontend_code,
|
||||
"backend_code": body.current_backend_code,
|
||||
"input_schema": body.current_schema,
|
||||
}
|
||||
|
||||
|
||||
AUTO_RUN_SYSTEM_PROMPT = """\
|
||||
You generate structured JSON data matching a given schema.
|
||||
The user provides a prompt describing what data to generate and a JSON Schema.
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
Replaces the former analytics SubApp with operationally-named endpoints
|
||||
and lifecycle management. Responsibilities:
|
||||
|
||||
- Usage-summary and cost-breakdown endpoints (user-facing, for the
|
||||
Settings / Usage page)
|
||||
- Usage-summary endpoint (user-facing, for the Settings / Usage page)
|
||||
- Background heartbeat that reports operational state to the cloud
|
||||
- 9Router auto-start for OpenSwarm Pro users
|
||||
- Frontend event endpoint (`POST /api/service/event`)
|
||||
@@ -364,31 +363,6 @@ async def usage_summary():
|
||||
}
|
||||
|
||||
|
||||
@service.router.get("/cost-breakdown")
|
||||
async def cost_breakdown(period: str = "7d"):
|
||||
from backend.apps.nine_router import get_usage_stats, is_running as _9r_running
|
||||
if not _9r_running():
|
||||
return {"available": False, "by_model": {}, "by_provider": {}}
|
||||
stats = await get_usage_stats(period)
|
||||
if not stats:
|
||||
return {"available": False, "by_model": {}, "by_provider": {}}
|
||||
return {
|
||||
"available": True,
|
||||
"period": period,
|
||||
"total_cost": stats.get("totalCost", 0),
|
||||
"total_requests": stats.get("totalRequests", 0),
|
||||
"total_prompt_tokens": stats.get("totalPromptTokens", 0),
|
||||
"total_completion_tokens": stats.get("totalCompletionTokens", 0),
|
||||
"by_model": stats.get("byModel", {}),
|
||||
"by_provider": stats.get("byProvider", {}),
|
||||
}
|
||||
|
||||
|
||||
@service.router.get("/status")
|
||||
async def service_status():
|
||||
return {"status": "ok", "enabled": True}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Frontend event endpoints
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -422,9 +396,3 @@ async def post_event(body: dict):
|
||||
"p": body.get("props") or body.get("properties") or {},
|
||||
})
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@service.router.get("/spool/count")
|
||||
async def spool_count():
|
||||
from backend.apps.service import buffer
|
||||
return {"pending": buffer.count(svc._spool_path())}
|
||||
|
||||
@@ -200,11 +200,6 @@ async def update_settings(body: AppSettings):
|
||||
return {"ok": True, "settings": body.model_dump()}
|
||||
|
||||
|
||||
@settings.router.get("/default-system-prompt")
|
||||
async def get_default_system_prompt():
|
||||
return {"default_system_prompt": DEFAULT_SYSTEM_PROMPT}
|
||||
|
||||
|
||||
@settings.router.post("/reset-system-prompt")
|
||||
async def reset_system_prompt():
|
||||
current = load_settings()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import logging
|
||||
import os
|
||||
from uuid import uuid4
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -273,24 +272,6 @@ async def websocket_dashboard(websocket: WebSocket):
|
||||
ws_manager.disconnect_global(websocket)
|
||||
|
||||
|
||||
@app.post("/api/browser/command")
|
||||
async def browser_command(request: Request):
|
||||
"""HTTP endpoint called by the browser MCP server subprocess.
|
||||
Proxies commands to the frontend via WebSocket and waits for results."""
|
||||
body = await request.json()
|
||||
action = body.get("action", "")
|
||||
browser_id = body.get("browser_id", "")
|
||||
tab_id = body.get("tab_id", "")
|
||||
params = body.get("params", {})
|
||||
|
||||
if not action or not browser_id:
|
||||
return JSONResponse({"error": "action and browser_id are required"}, status_code=400)
|
||||
|
||||
request_id = uuid4().hex
|
||||
result = await ws_manager.send_browser_command(request_id, action, browser_id, params, tab_id=tab_id)
|
||||
return JSONResponse(result)
|
||||
|
||||
|
||||
@app.get("/api/subscriptions/pending/{state}")
|
||||
async def subscriptions_pending(state: str):
|
||||
"""Return pending OAuth data for a state param. Called by 9Router's callback page."""
|
||||
|
||||
@@ -8,7 +8,6 @@ Tests:
|
||||
succeeds; DELETE removes it.
|
||||
- POST /sessions/{id}/edit_message validates input.
|
||||
- PATCH /sessions/{id} updates allowed fields (name, system_prompt).
|
||||
- GET /sessions/{id}/branches returns the default 'main' branch.
|
||||
- GET /history returns paginated results.
|
||||
"""
|
||||
|
||||
@@ -127,20 +126,6 @@ def test_patch_session_updates_name(client, stub_agent_loop):
|
||||
assert fetched["model"] == "sonnet" # not changed by the PATCH
|
||||
|
||||
|
||||
def test_get_branches_returns_main(client, stub_agent_loop):
|
||||
launch = client.post(
|
||||
"/api/agents/launch",
|
||||
json={"name": "B", "model": "sonnet", "mode": "agent"},
|
||||
)
|
||||
session_id = launch.json()["session_id"]
|
||||
|
||||
resp = client.get(f"/api/agents/sessions/{session_id}/branches")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["active_branch_id"] == "main"
|
||||
assert "main" in body["branches"]
|
||||
|
||||
|
||||
def test_history_endpoint_returns_paginated_shape(client):
|
||||
"""`/history` is the search-and-resume endpoint. Even with no
|
||||
saved sessions it should return the paginated wrapper without
|
||||
|
||||
@@ -15,7 +15,6 @@ chatter from these tests. Two fixtures here:
|
||||
Coverage:
|
||||
|
||||
- GET /subscriptions/status — both down + up shapes.
|
||||
- GET /subscriptions/models — both down + up shapes.
|
||||
- POST /subscriptions/connect — missing provider → 400; 503 when
|
||||
9Router is unavailable; happy path
|
||||
round-trips the device_code flow.
|
||||
@@ -126,25 +125,6 @@ def test_status_when_9router_up(client, nine_router_up):
|
||||
assert isinstance(body["models"], list)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /subscriptions/models
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_models_when_9router_down(client, nine_router_down):
|
||||
resp = client.get("/api/agents/subscriptions/models")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"models": []}
|
||||
|
||||
|
||||
def test_models_when_9router_up(client, nine_router_up):
|
||||
resp = client.get("/api/agents/subscriptions/models")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert isinstance(body["models"], list)
|
||||
assert len(body["models"]) == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST /subscriptions/connect
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -57,8 +57,14 @@ def test_create_get_update_delete_dashboard(client):
|
||||
assert gone.status_code == 404
|
||||
|
||||
|
||||
def test_dashboard_layout_round_trip(client):
|
||||
"""PUT layout with a sticky note and assert it survives a re-fetch."""
|
||||
def test_dashboard_layout_field_round_trip(client):
|
||||
"""PUT the `layout` field on a dashboard with a sticky note and
|
||||
assert it survives a re-fetch via /api/dashboards/{id}.
|
||||
|
||||
Named `_field_` (not `_dashboard_layout_`) to make clear this exercises
|
||||
the `layout` attribute of the live `Dashboard` model — not the legacy
|
||||
`backend.apps.dashboard_layout` package, which was removed.
|
||||
"""
|
||||
create = client.post("/api/dashboards/create", json={"name": "L"})
|
||||
dashboard_id = create.json()["id"]
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Layout mirrors `outputs.py`:
|
||||
- File serve (workspace + saved output, with token rewrite + _d
|
||||
payload injection)
|
||||
- Backend execute
|
||||
- vibe-code + auto-run (LLM-mocked)
|
||||
- auto-run (LLM-mocked)
|
||||
- auto-run-agent (stub_agent_loop + AgentConfig spy)
|
||||
- Auth control mirroring test_api_agents.test_protected_route_requires_auth
|
||||
"""
|
||||
@@ -24,7 +24,6 @@ import base64
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -52,43 +51,6 @@ def _create_output(client, **overrides) -> dict:
|
||||
return resp.json()["output"]
|
||||
|
||||
|
||||
def _make_mock_anthropic(text_response: str) -> MagicMock:
|
||||
"""Build a mock Anthropic client.
|
||||
|
||||
The route does `await client.messages.create(...)` then reads
|
||||
`resp.content[0].text`. Mirror that shape.
|
||||
"""
|
||||
fake_resp = MagicMock()
|
||||
fake_resp.content = [MagicMock(text=text_response)]
|
||||
client_mock = MagicMock()
|
||||
client_mock.messages.create = AsyncMock(return_value=fake_resp)
|
||||
return client_mock
|
||||
|
||||
|
||||
def _patch_anthropic(monkeypatch, text_response: str) -> MagicMock:
|
||||
"""Hook `_get_anthropic_client` in outputs.py to return a mock that
|
||||
responds with `text_response` on every messages.create call.
|
||||
|
||||
Returns the inner mock client so tests can assert call args.
|
||||
"""
|
||||
from backend.apps.outputs import outputs as outputs_mod
|
||||
|
||||
mock_client = _make_mock_anthropic(text_response)
|
||||
monkeypatch.setattr(outputs_mod, "_get_anthropic_client", lambda: mock_client)
|
||||
return mock_client
|
||||
|
||||
|
||||
def _patch_aux_model(monkeypatch, model_id: str = "claude-haiku-fake") -> None:
|
||||
"""Stub `resolve_aux_model` on the registry so vibe-code/auto-run never
|
||||
try to inspect the user's actual model connections."""
|
||||
from backend.apps.agents.providers import registry
|
||||
|
||||
async def _fake(_settings, preferred_tier="haiku"):
|
||||
return (model_id, None)
|
||||
|
||||
monkeypatch.setattr(registry, "resolve_aux_model", _fake)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CRUD + legacy migration
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -554,45 +516,6 @@ def test_execute_no_backend_code_returns_none_result(client):
|
||||
assert body["error"] is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /vibe-code (Anthropic mocked)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_vibe_code_resolver_raises_value_error_returns_graceful(client, monkeypatch):
|
||||
"""resolve_aux_model raises ValueError when no model is connected.
|
||||
The route catches and returns a graceful error response without
|
||||
touching the Anthropic client."""
|
||||
from backend.apps.agents.providers import registry
|
||||
|
||||
async def _raise(_settings, preferred_tier="haiku"):
|
||||
raise ValueError("no aux model")
|
||||
|
||||
monkeypatch.setattr(registry, "resolve_aux_model", _raise)
|
||||
|
||||
resp = client.post(
|
||||
"/api/outputs/vibe-code",
|
||||
json={"prompt": "x", "current_frontend_code": "<keep/>"},
|
||||
)
|
||||
body = resp.json()
|
||||
assert "no aux model" in body["message"]
|
||||
assert body["frontend_code"] == "<keep/>"
|
||||
|
||||
|
||||
def test_vibe_code_anthropic_import_error(client, monkeypatch):
|
||||
"""Forcing `import anthropic` to fail returns the install hint without
|
||||
touching the model registry at all."""
|
||||
monkeypatch.setitem(sys.modules, "anthropic", None)
|
||||
|
||||
resp = client.post(
|
||||
"/api/outputs/vibe-code",
|
||||
json={"prompt": "x", "current_frontend_code": "<keep/>"},
|
||||
)
|
||||
body = resp.json()
|
||||
assert "anthropic SDK not installed" in body["message"]
|
||||
assert body["frontend_code"] == "<keep/>"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# /auto-run (Anthropic mocked)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -8,7 +8,7 @@ which we exercise here.
|
||||
Tests:
|
||||
- GET /api/settings returns defaults including the canned system prompt
|
||||
- PUT /api/settings round-trips a value and survives `load_settings()` reload
|
||||
- default-system-prompt + reset-system-prompt
|
||||
- reset-system-prompt
|
||||
- browse-directories on a tmp dir
|
||||
"""
|
||||
|
||||
@@ -46,14 +46,6 @@ def test_put_round_trips_value(client):
|
||||
assert load_settings().theme == "light"
|
||||
|
||||
|
||||
def test_default_system_prompt_returns_constant(client):
|
||||
from backend.apps.settings.models import DEFAULT_SYSTEM_PROMPT
|
||||
|
||||
resp = client.get("/api/settings/default-system-prompt")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["default_system_prompt"] == DEFAULT_SYSTEM_PROMPT
|
||||
|
||||
|
||||
def test_reset_system_prompt(client):
|
||||
"""Mutate then reset; the prompt must snap back to DEFAULT_SYSTEM_PROMPT."""
|
||||
from backend.apps.settings.models import DEFAULT_SYSTEM_PROMPT
|
||||
|
||||
@@ -200,7 +200,10 @@ def test_reconcile_idempotent():
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_dashboard_layout_notes_round_trip():
|
||||
def test_dashboard_layout_model_notes_round_trip():
|
||||
"""`DashboardLayout` (Pydantic) with notes round-trips through model_dump
|
||||
and model_validate. Named `_model_` to disambiguate from the long-removed
|
||||
`backend.apps.dashboard_layout` package."""
|
||||
from backend.apps.dashboards.models import DashboardLayout, NotePosition
|
||||
|
||||
n = NotePosition(note_id="n1", x=100, y=200, content="todo: ship",
|
||||
@@ -215,8 +218,9 @@ def test_dashboard_layout_notes_round_trip():
|
||||
assert rehydrated.notes["n1"].color == "yellow"
|
||||
|
||||
|
||||
def test_dashboard_layout_legacy_no_notes():
|
||||
"""Older dashboard JSON without 'notes' must still load cleanly."""
|
||||
def test_dashboard_layout_model_legacy_no_notes():
|
||||
"""Older dashboard JSON without 'notes' must still load cleanly into the
|
||||
live `DashboardLayout` Pydantic model from `backend.apps.dashboards.models`."""
|
||||
from backend.apps.dashboards.models import DashboardLayout
|
||||
|
||||
legacy = {
|
||||
|
||||
@@ -342,12 +342,3 @@ async def test_endpoint_event_missing_surface(sink):
|
||||
assert res["ok"] is False
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_endpoint_spool_count(tmp_path):
|
||||
from backend.apps.service import client as svc, buffer
|
||||
from backend.apps.service.service import spool_count
|
||||
spool = str(tmp_path / "spool.db")
|
||||
with patch.object(svc, "_spool_path", lambda: spool):
|
||||
buffer.enqueue(spool, "s:/x", {}, now=time.time())
|
||||
result = await spool_count()
|
||||
assert result == {"pending": 1}
|
||||
|
||||
Reference in New Issue
Block a user