SurfSense/surfsense_backend/tests
CREDO23 5b45f78a16 refactor(chat): delete legacy stream_new_chat monolith (cutover complete)
The flows orchestrators (new_chat/resume_chat) are now the sole live path
after the byte-for-byte differential proof, so the monolith and its
monolith-vs-flows parity scaffolding are removed.

- Repoint the last live importer (anonymous_chat_routes) to
  streaming.agent.event_loop.stream_agent_events + shared.stream_result.StreamResult
  (drop-in; the keyword-only fallback-commit params default to inert for anon).
- Repoint e2e launcher patch targets to flows.shared.llm_bundle.
- Repoint helper unit tests (chunk_parts, thinking-step ids, tool-input
  streaming) to their flows homes to preserve coverage.
- Delete the monolith, the contract test, and the parity tests
  (parallel_refactor, stage_1, stage_2, orchestrator_frame) whose sole
  purpose was comparing against the now-removed monolith.

Full suite green (2622 passed, 1 skipped); the two excluded live-app dirs
(document_upload, composio) have a pre-existing, env-gated registration 404
unrelated to this change.
2026-06-04 14:35:45 +02:00
..
e2e refactor(chat): delete legacy stream_new_chat monolith (cutover complete) 2026-06-04 14:35:45 +02:00
fixtures remove stale ElectricSQL references from changelog and test fixtures 2026-03-24 17:07:11 +02:00
integration refactor(agents): move tools package to app/agents/shared (slice 6) 2026-06-04 13:11:56 +02:00
unit refactor(chat): delete legacy stream_new_chat monolith (cutover complete) 2026-06-04 14:35:45 +02:00
utils feat: implement task dispatcher for document processing 2026-02-26 23:55:47 +05:30
__init__.py feat: Add end-to-end tests for document upload pipeline and shared test utilities 2026-02-25 16:39:45 +05:30
conftest.py feat: add should_summarize parameter to task dispatchers 2026-02-26 19:12:37 -08:00
README.md test: add notifications unit tests and conventions doc 2026-06-03 21:53:06 +02:00

Tests

How the backend test suite is organized and the conventions to follow when adding tests.

Layout: type-first, module-mirrored

Tests are split by type at the top level, and each type mirrors the app/ module tree inside:

tests/
├── conftest.py                  # global fixtures + DATABASE_URL pinning
├── unit/                        # pure logic: no DB, no app, no network
│   └── notifications/
│       ├── api/test_transform.py
│       └── service/
│           ├── messages/test_connector_indexing.py
│           └── test_metadata.py
└── integration/                 # real PostgreSQL (pgvector)
    ├── conftest.py              # async engine, transactional db_session, db_user, ...
    └── notifications/
        ├── conftest.py          # module-scoped fixtures (e.g. transactional client)
        └── test_*_handler.py

To find a feature's tests, look under tests/<type>/<same path as app/>.

Unit vs integration

  • @pytest.mark.unit — pure, fast, no I/O. Test behavior through a public function's inputs/outputs.
  • @pytest.mark.integration — requires a real database. Run with AUTH_TYPE=LOCAL.

Maximize logic covered by unit tests; keep integration tests for what genuinely needs the DB (persistence, SQL filters, scoping, HTTP wiring).

Principles

  • Behavior, not implementation. Assert observable outputs (returned values, persisted rows, HTTP responses), never private helpers. Tests should survive a refactor.
  • Functional core / imperative shell. Put pure decision logic in a side-effect-free module (e.g. app/notifications/service/messages/) so it is unit-testable; keep the persistence shell thin and cover it with a few integration tests.
  • One responsibility per test file, mirroring the slice it covers.
  • Mock only at system boundaries (external APIs, brokers), never internal collaborators. Prefer dependency overrides and the transactional db_session over mocks.

Fixtures

conftest.py is scoped to its directory and below. Keep truly global fixtures in tests/conftest.py; put module-specific fixtures in that module's conftest.py so a DB fixture never loads for a pure unit test.

For API integration tests, override get_async_session and current_active_user to ride the test's transactional db_session (see tests/integration/notifications/conftest.py): rows seeded in the test and rows read via the endpoint share one transaction that rolls back automatically.

Import mode

The suite uses --import-mode=importlib with pythonpath = ["."] (see pyproject.toml). This lets test files share basenames across modules (e.g. many test_api.py) without __init__.py boilerplate; new test directories do not need an __init__.py.

Running

# fast unit tests
uv run pytest -m unit

# integration (needs Postgres + pgvector)
AUTH_TYPE=LOCAL uv run pytest -m integration

# a single module's tests
uv run pytest tests/unit/notifications