SurfSense/surfsense_backend/tests
CREDO23 b7ea829371 refactor(agents): relocate boundary-only infra out of shared/
Neither module is imported by any sibling agent package, so neither belongs in
the cross-agent shared kernel:

- checkpointer.py -> app/agents/runtime/checkpointer.py
  LangGraph Postgres checkpoint saver. It's cross-agent *runtime infra* wired by
  the boundary (app lifespan + anonymous_chat & multi_agent_chat flows), not
  agent code. New app/agents/runtime/ layer holds boundary-wired agent infra.

- shared/system_prompt.py + shared/prompts/ -> app/prompts/
  The legacy single-agent prompt composer. The live agents don't use it
  (main_agent has its own system_prompt/ builder; anonymous_chat builds inline);
  its only consumer is new_llm_config_routes for displaying default instructions.
  Moved to the existing non-agent prompt domain:
    system_prompt.py        -> app/prompts/default_system_instructions.py
    prompts/                 -> app/prompts/system_prompt_composer/

app/agents/shared/ now contains only genuinely cross-agent code: context,
middleware/{compaction,retry_after,dedup_tool_calls}, tools/.

NOTE: get_default_system_instructions() (LLM-config UI) composes from the legacy
library, which differs from what the live agents actually run -- pre-existing
latent staleness, not changed here.
2026-06-05 12:36:44 +02:00
..
e2e refactor(agents): move mac-only modules out of the cross-agent shared kernel 2026-06-05 12:30:15 +02:00
fixtures remove stale ElectricSQL references from changelog and test fixtures 2026-03-24 17:07:11 +02:00
integration refactor(agents): move mac-only modules out of the cross-agent shared kernel 2026-06-05 12:30:15 +02:00
unit refactor(agents): relocate boundary-only infra out of shared/ 2026-06-05 12:36:44 +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