Skip to content

Alkiviades — Testing Guide

Test Structure

backend/tests/
├── __init__.py              # Package marker
├── conftest.py              # Shared fixtures
├── test_auth.py             # Auth unit tests (JWT, bcrypt, cookies)
├── test_auth_endpoints.py   # Auth API integration tests (FastAPI TestClient)
├── test_database.py         # Database integration tests
├── test_files.py            # File I/O operations
├── test_httpx_streaming.py  # httpx streaming compatibility
├── test_prompt.py           # Prompt assembly
├── test_rag.py              # RAG pipeline (chunking, embedding, retrieval)
├── test_session.py          # (future) Session persistence
├── test_stream_events.py    # SSE event mapping
├── test_streaming_agent.py  # E2E agent streaming
├── test_tools.py            # (future) Tool execution
├── test_user_store.py       # Agent registry LRU/TTL
└── test_utils.py            # Utility functions

Running Tests

All Tests

python -m pytest backend/tests/ -v

Unit Tests Only (no API keys needed)

python -m pytest backend/tests/ \
  --ignore=backend/tests/test_auth_endpoints.py \
  --ignore=backend/tests/test_database.py \
  --ignore=backend/tests/test_streaming_agent.py \
  --ignore=backend/tests/test_httpx_streaming.py \
  --ignore=backend/tests/test_user_store.py \
  -v
Currently 50 tests pass.

Single File

python -m pytest backend/tests/test_rag.py -v

Single Test

python -m pytest backend/tests/test_rag.py::test_cosine_similarity_same_text_near_1 -v

With Coverage

pip install pytest-cov
python -m pytest backend/tests/ --cov=backend/app --cov-report=html

Test Categories

Unit Tests

Tests that don't require external services (no API keys, no network):

File Tests What It Tests
test_auth.py 12 JWT creation/decoding/expiry, bcrypt hash/verify, cookie helpers
test_rag.py 10 Markdown chunking, TF-IDF fitting/embedding, cosine similarity, manifest diff
test_prompt.py 5 User message assembly, axis interpolation
test_utils.py 10 Token counting, slugify, sanitize, JSON safety
test_files.py 7 Read/write/edit file operations, path traversal prevention
test_stream_events.py 4 SSE event mapping, heartbeat generator

Integration Tests

Tests that use the FastAPI TestClient with a temporary SQLite database:

File Tests What It Tests
test_auth_endpoints.py 12 Login, refresh, logout, protected endpoints
test_database.py 7 Schema init, admin bootstrap, WAL mode
test_user_store.py 7 AgentRegistry LRU/TTL eviction, concurrent access

E2E Tests

Tests that call the real DeepSeek API (require DEEPSEEK_API_KEY):

File Tests What It Tests
test_streaming_agent.py 1 Full agent streaming flow
test_httpx_streaming.py 2 httpx streaming compatibility

Markers

Use pytest markers to filter tests:

# Run only integration tests
python -m pytest backend/tests/ -m integration -v

# Skip slow tests
python -m pytest backend/tests/ -m "not slow" -v

Available markers (defined in conftest.py): - integration: Requires API keys or external services - slow: Takes >1 second

Fixtures

Shared fixtures in conftest.py:

Fixture Type Description
temp_dir Path Temporary directory (auto-cleaned)
mock_embedder Embedder Pre-fitted embedder with sample texts
sample_chunks list[Chunk] 5 sample chunks for testing
temp_kb KnowledgeBase KB with temporary vectors file
test_db str Temporary SQLite database path
app_with_db (TestClient, str) FastAPI TestClient with test DB
auth_headers dict Pre-authenticated JWT headers

Writing New Tests

  1. Use the shared fixtures from conftest.py
  2. Mock external APIs (DeepSeek, Tavily) with unittest.mock or pytest-mock
  3. Use FastAPI's TestClient for endpoint tests
  4. Tag integration/slow tests with appropriate marks
  5. Follow existing patterns in matching test files

Example: Unit Test

from app.embed import Embedder

def test_embedder_fit_builds_vocab():
    embedder = Embedder(dtype="float32")
    embedder.fit(["hello world", "hello python"])
    assert "hello" in embedder.vocab
    assert len(embedder.vocab) >= 3  # hello, world, python

Example: API Test

def test_health(client):
    response = client.get("/health")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}

CI/CD

For GitHub Actions or similar, add a test workflow:

name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r backend/requirements.txt pytest
      - run: python -m pytest backend/tests/ -v \
          --ignore=backend/tests/test_auth_endpoints.py \
          --ignore=backend/tests/test_streaming_agent.py \
          --ignore=backend/tests/test_httpx_streaming.py