Skip to content

Testing Guide

Quick Start

# Install dependencies (includes pytest, httpx, pytest-cov)
pip install -r requirements.txt

# Run all tests
pytest tests/ -v

# Run with coverage report
pytest tests/ --cov=app --cov-report=term-missing

# Run a single test file
pytest tests/services/test_pdf_processor.py -v

# Run a specific test
pytest tests/routers/test_auth.py::test_signup_success -v

Test Suite Overview

148 tests across 19 test files, covering every endpoint, service, and the LangGraph teacher agent.

Category Files Tests What's Tested
Services (pure) 3 25 pdf_processor, tier_config, cache
Services (mocked) 5 47 quality_checker, deduplication, text_normalizer, wallet_reservation, llm
Routers 10 66 health, auth, me, curriculum, wallet, admin (users/ingestion/documents), scraping, chat
Agents 1 10 teacher_agent node functions
Total 19 148

Test Structure

tests/
├── conftest.py                    # Shared fixtures, mocks, TestClient
├── test_health.py                 # GET /health
├── services/
│   ├── test_pdf_processor.py      # extract_pages, chunk_text, build_chunks
│   ├── test_tier_config.py        # get_limits, top_k, rerank_n, costs
│   ├── test_cache.py              # LRU: get, set, TTL, eviction, invalidation
│   ├── test_quality_checker.py    # text length, OCR confidence, encoding, file size
│   ├── test_deduplication.py      # SimHash fingerprint, hamming distance
│   ├── test_text_normalizer.py    # Arabic normalization, whitespace, boilerplate
│   ├── test_wallet_reservation.py # reserve, finalize, topup, expire, cost calc
│   └── test_llm.py               # detect_exercise, translate_query, generate_answer
├── routers/
│   ├── test_auth.py               # signup, signin, logout, reset-password
│   ├── test_me.py                 # get/update profile
│   ├── test_chat.py               # POST /chat JSON mode
│   ├── test_curriculum.py         # subjects, textbooks, detail
│   ├── test_wallet.py             # balance, reservations, topup, transactions
│   ├── test_admin_users.py        # test-role, stats, CRUD, role, hint, reset-pw
│   ├── test_admin_ingestion.py    # ingest, dispatch, jobs, requeue
│   ├── test_admin_documents.py    # references, scrape-runs, update/delete doc
│   └── test_scraping.py           # sources, sync, runs, references
└── agents/
    └── test_teacher_agent.py      # check_wallet, decide_path, retrieve, clarify, finalize

Mocking Strategy

All external services are mocked at the library level so tests run fast with no credentials needed:

  • Supabasesupabase.create_client returns a MagicMock with chainable .table().select().eq().execute() pattern
  • OpenAIopenai.OpenAI returns a MagicMock
  • Pineconepinecone.Pinecone returns a MagicMock
  • LangGraphbuild_teacher_graph() is patched before the chat router loads

The conftest.py starts these patches at module level (before any app.* import) because app/core/dependencies.py creates real clients at import time.

Writing New Tests

  1. Service tests — Import the class/function directly, mock only what's needed (supabase client for DB-dependent services).
  2. Router tests — Use the client fixture (TestClient with auth overrides), patch app.api.routers.<module>.supabase_service at the router level.
  3. Agent tests — Import node functions directly, patch app.agents.teacher_agent.supabase_service / wallet_service / retrieval_pipeline.

Dependencies

Added to requirements.txt:

pytest
httpx>=0.25,<0.28
pytest-cov

httpx is pinned to <0.28 for compatibility with starlette==0.27 (bundled with fastapi==0.104.1).