app/agents/shared/ is a sibling of anonymous_chat/podcaster/multi_agent_chat/
video_presentation, so it should only hold code shared across 2+ of those
agents. In practice podcaster and video_presentation import nothing from it,
and anonymous_chat needs only context + compaction + retry_after + web_search.
Everything else was multi_agent_chat-only (the boundary just passes through).
Move the multi_agent_chat-only cluster into multi_agent_chat/shared/ (files
moved verbatim via git rename; ~116 import sites rewritten):
errors, feature_flags, filesystem_selection, path_resolver, prompt_caching,
sandbox, llm_config, mention_resolver
middleware/busy_mutex, middleware/kb_persistence
busy_mutex/llm_config/mention_resolver are boundary-only but import the moved
modules, so they were folded in to avoid a backwards shared -> multi_agent_chat
dependency. main_agent builders now import the impls directly; the shared
middleware barrel keeps only the genuinely-shared compaction + retry_after.
Also delete the dead leftover shared/plugins and shared/skills dirs (live
copies already live under main_agent/).
Remaining in app/agents/shared/: context, system_prompt(+prompts), checkpointer,
middleware/{compaction,retry_after,dedup_tool_calls}, tools/. checkpointer and
system_prompt are boundary-only infra pending a dedicated home decision.
|
||
|---|---|---|
| .. | ||
| fakes | ||
| fixtures | ||
| middleware | ||
| __init__.py | ||
| auth_mint.py | ||
| README.md | ||
| run_backend.py | ||
| run_celery.py | ||
Backend E2E Harness
This directory contains the test-only backend entrypoints and fakes used by
Playwright. They are not part of the production image: .dockerignore excludes
tests/, and the E2E Docker stage copies this directory through a separate
build context.
Files
| Path | Purpose |
|---|---|
run_backend.py |
Starts FastAPI after installing the test fakes into sys.modules. |
run_celery.py |
Starts the Celery worker with the same fake setup. |
middleware/scenario.py |
Reads X-E2E-Scenario into a request-scoped context var. |
fakes/composio_module.py |
Fake composio package used by connector flows. |
fakes/llm.py |
Fake chat model factory. |
fakes/embeddings.py |
Deterministic embedding helpers. |
fakes/fixtures/drive_files.json |
Drive fixture data and canary file contents. |
Why the import hook exists
Some production modules import SDK clients at module load time, for example
from composio import Composio. By the time app.app has been imported, those
bindings are already fixed.
The E2E entrypoints install fake modules in sys.modules before importing any
app.* module. That lets the normal production code run while SDK calls resolve
to local fakes.
The fakes should fail loudly. If production starts using a new SDK method that the fake does not implement, add that method to the fake instead of letting the test call the real service.
Adding a fake
- Add
fakes/<sdk>_module.py. - Register it in both
run_backend.pyandrun_celery.pybefore importingapp.apporapp.celery_app. - If the fake needs per-test behavior, read the current scenario from
tests.e2e.middleware.scenario.current_scenario().
Shared with backend integration tests
Backend integration tests can use the same fakes when they need production route code without the real SDK:
from tests.e2e.fakes import composio_module as _fake_composio
sys.modules["composio"] = _fake_composio
from app.app import app
See surfsense_backend/tests/integration/composio/conftest.py for the current
pattern.
Running locally
The recommended local flow runs only Postgres and Redis in Docker, and the
backend + Celery worker on the host. No .env file is required: both
entrypoints setdefault every variable they need (DB URL, Redis URL,
sentinel API keys, etc.) to values that match docker-compose.deps-only.yml.
One-time setup
From surfsense_web/:
pnpm install
pnpm exec playwright install --with-deps chromium
Each run
1. Bring up Postgres + Redis from the repo root (the other deps-only services (SearXNG, Zero, pgAdmin) are not needed for E2E):
docker compose -f docker/docker-compose.deps-only.yml up -d db redis
2. Start the backend in surfsense_backend/, terminal A:
uv sync
uv run alembic upgrade head
uv run python tests/e2e/run_backend.py
3. Start the Celery worker in surfsense_backend/, terminal B:
uv run python tests/e2e/run_celery.py
4. Register the Playwright user:
curl -X POST http://localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"e2e-test@surfsense.net","password":"E2eTestPassword123!"}'
5. Run Playwright from surfsense_web/, terminal C:
pnpm test:e2e # dev server (fast iteration)
pnpm test:e2e:headed # show the browser
pnpm test:e2e:ui # Playwright UI mode
pnpm test:e2e:prod # build + start (matches CI exactly)
playwright.config.ts and the run scripts share defaults, so this works on a
fresh checkout. Set PLAYWRIGHT_TEST_EMAIL, PLAYWRIGHT_TEST_PASSWORD,
NEXT_PUBLIC_FASTAPI_BACKEND_URL, or any backend env (e.g. DATABASE_URL)
only when pointing tests at a different stack.
Cleanup
docker compose -f docker/docker-compose.deps-only.yml down
Add -v to also wipe the Postgres volume.
Hermetic alternative (matches CI)
To reproduce the CI environment exactly — backend and Celery in containers, network egress denied at L3 — replace steps 1–3 with:
docker compose -f docker/docker-compose.e2e.yml up -d --build --wait
Then run steps 4 (curl register) and 5 (pnpm test:e2e:prod) as above. Tear
down with:
docker compose -f docker/docker-compose.e2e.yml down -v --remove-orphans
This builds the ~9 GB surfsense-e2e-backend:local image, so the deps-only
flow above is faster for day-to-day development.