2026-05-11 03:29:32 +05:30
|
|
|
|
# Backend E2E Harness
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
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.
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
|
|
|
|
|
## Files
|
|
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
1. Add `fakes/<sdk>_module.py`.
|
|
|
|
|
|
2. Register it in both `run_backend.py` and `run_celery.py` before importing
|
|
|
|
|
|
`app.app` or `app.celery_app`.
|
|
|
|
|
|
3. If the fake needs per-test behavior, read the current scenario from
|
2026-05-06 17:17:42 +05:30
|
|
|
|
`tests.e2e.middleware.scenario.current_scenario()`.
|
|
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
## Shared with backend integration tests
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
Backend integration tests can use the same fakes when they need production route
|
|
|
|
|
|
code without the real SDK:
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from tests.e2e.fakes import composio_module as _fake_composio
|
|
|
|
|
|
sys.modules["composio"] = _fake_composio
|
|
|
|
|
|
from app.app import app
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
See `surfsense_backend/tests/integration/composio/conftest.py` for the current
|
|
|
|
|
|
pattern.
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
|
|
|
|
|
## Running locally
|
|
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
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/`:
|
|
|
|
|
|
|
2026-05-06 17:17:42 +05:30
|
|
|
|
```bash
|
2026-05-11 03:29:32 +05:30
|
|
|
|
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):
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker compose -f docker/docker-compose.deps-only.yml up -d db redis
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**2. Start the backend** in `surfsense_backend/`, terminal A:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
uv sync
|
|
|
|
|
|
uv run alembic upgrade head
|
2026-05-06 17:17:42 +05:30
|
|
|
|
uv run python tests/e2e/run_backend.py
|
2026-05-11 03:29:32 +05:30
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**3. Start the Celery worker** in `surfsense_backend/`, terminal B:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-06 17:17:42 +05:30
|
|
|
|
uv run python tests/e2e/run_celery.py
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-11 03:29:32 +05:30
|
|
|
|
**4. Register the Playwright user**:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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:
|
2026-05-06 17:17:42 +05:30
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-11 03:29:32 +05:30
|
|
|
|
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)
|
2026-05-06 17:17:42 +05:30
|
|
|
|
```
|
2026-05-11 03:29:32 +05:30
|
|
|
|
|
|
|
|
|
|
`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
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
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.
|