Testing
Test Strategy
Chaos Cypher uses a multi-level testing approach:
| Level | Location | Runner | Purpose |
|---|---|---|---|
| Unit | packages/*/tests/ | pytest | Individual functions and classes |
| Integration | tests/ | pytest | Cross-package interactions |
| Docker | make docker-test | pytest in Docker | Isolated, production-like environment |
Running Tests
Docker Tests (Recommended)
Run tests in an isolated Docker container that mirrors the production environment:
make docker-test
This builds a test image and runs all tests with coverage reporting. Use this before pushing to ensure tests pass in a clean environment.
Local Tests
Run tests directly on your machine (faster, but may be affected by local state):
make test
Individual Package Tests
# Core
cd packages/core && pytest
# Cortex
cd packages/cortex && pytest
# CLI
cd packages/cli && pytest
Coverage Requirements
The CI pipeline enforces 80% code coverage. Check coverage locally:
make docker-test
Coverage reports are generated in coverage_html/ for detailed analysis.
Writing Tests
Test File Structure
Tests are organized across multiple locations:
tests/ # Repo root — integration and cross-package tests
└── unit/
├── cortex/ # Cortex unit tests
└── core/ # Core unit tests
packages/core/tests/ # Core package tests
packages/cortex/tests/ # Cortex package tests
packages/cli/tests/ # CLI package tests
Testing Services
Service tests should mock the repository layer:
def test_list_entities(mock_repository):
"""Test listing entities returns expected results."""
mock_repository.list_entities.return_value = [
MyEntity(id="1", name="Test", database_name="default")
]
service = MyService(mock_repository)
result = service.list_entities()
assert len(result) == 1
assert result[0]["name"] == "Test"
Testing API Endpoints
Use FastAPI's TestClient:
from fastapi.testclient import TestClient
def test_list_endpoint(client: TestClient):
"""Test list endpoint returns 200."""
response = client.get("/api/v1/myentities")
assert response.status_code == 200
Testing Core Services
Core services use storage protocol mocks:
def test_core_service(mock_storage):
"""Test core service with mocked storage."""
mock_storage.get_source.return_value = {
"id": "source-1",
"name": "test.pdf",
"status": "indexed",
}
service = SourceService(mock_storage)
result = service.get_source("source-1")
assert result["status"] == "indexed"
CI Pipeline
The full CI pipeline (make ci) runs:
- Lint + format — Ruff linting and formatting checks
- Type checking — mypy (Python) + tsc (TypeScript)
- Custom architectural rules —
lint-claudechecks (factory naming, data boundaries, etc.) - Docstring coverage — 100% required for public APIs
- Dead code detection — deadcode/vulture scanning
- Tests + coverage — 80% coverage gate in Docker
- Security scan — Vulnerability scanning
All checks must pass before merging.