Skip to main content

Testing

Test Strategy

Chaos Cypher uses a multi-level testing approach:

LevelLocationRunnerPurpose
Unitpackages/*/tests/pytestIndividual functions and classes
Integrationtests/pytestCross-package interactions
Dockermake docker-testpytest in DockerIsolated, production-like environment

Running Tests

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:

  1. Lint + format — Ruff linting and formatting checks
  2. Type checking — mypy (Python) + tsc (TypeScript)
  3. Custom architectural ruleslint-claude checks (factory naming, data boundaries, etc.)
  4. Docstring coverage — 100% required for public APIs
  5. Dead code detection — deadcode/vulture scanning
  6. Tests + coverage — 80% coverage gate in Docker
  7. Security scan — Vulnerability scanning

All checks must pass before merging.