Skip to content

Testing Guide

This guide covers testing practices, patterns, and tools used in Routstr Core development.

Testing Philosophy

We follow these principles:

  • Test behavior, not implementation
  • Fast feedback
  • Reliable tests
  • Clear failures

Test Structure

tests/
├── integration/
│   ├── conftest.py
│   ├── utils.py
│   ├── test_wallet_topup.py
│   ├── test_wallet_refund.py
│   ├── test_wallet_information.py
│   ├── test_proxy_get_endpoints.py
│   ├── test_proxy_post_endpoints.py
│   └── ... more integration tests
├── unit/
│   ├── test_algorithm.py
│   ├── test_fee_consistency.py
│   ├── test_image_tokens.py
│   ├── test_logging_securityfilter.py
│   ├── test_payment_helpers.py
│   ├── test_settings.py
│   ├── test_wallet.py
│   └── ... more unit tests
└── run_integration.py

Running Tests

Make Targets

# Run all tests (unit + integration with mocks)
make test

# Unit tests only
make test-unit

# Integration tests with mocks (fast)
make test-integration

# Integration tests with Docker services
make test-integration-docker

# Fast tests only (skip slow and Docker tests)
make test-fast

# Performance tests
make test-performance

# Coverage
make test-coverage

Direct pytest Commands

# Run all tests
pytest

# Run a specific test file
pytest tests/unit/test_wallet.py -v

# Run a specific test
pytest tests/unit/test_wallet.py::test_get_balance -v

# Run tests matching a pattern
pytest -k "wallet" -v

Test Modes (Integration)

Integration tests support two execution modes:

  • Mock mode (default): uses in-memory mocks, no Docker required
  • Docker mode: uses real Docker services (Cashu mint, mock OpenAI, Nostr relay)

Use the runner script for Docker mode:

./tests/run_integration.py

Or manually:

docker-compose -f compose.testing.yml up -d
USE_LOCAL_SERVICES=1 pytest tests/integration/ -v
docker-compose -f compose.testing.yml down -v

Test Markers

Markers are defined in pyproject.toml:

  • integration
  • unit
  • slow
  • requires_docker
  • requires_real_mint
  • performance
  • asyncio

Examples:

# Skip slow tests
pytest -m "not slow" -v

# Run only integration tests
pytest -m "integration" -v

# Run performance tests
pytest -m "performance" -v

Fixtures and Utilities

Core Integration Fixtures

Defined in tests/integration/conftest.py:

  • integration_client - Async HTTP client for the FastAPI app
  • authenticated_client - Client with a pre-created API key
  • testmint_wallet - Test wallet for generating Cashu tokens
  • db_snapshot - Database state snapshot/diff helper
  • create_api_key - Helper to create API keys for tests
  • integration_engine, integration_session - Async DB engine/session
  • background_tasks_controller - Control background tasks in tests
  • mock_upstream_server - Mock upstream API responses

Integration Utilities

Defined in tests/integration/utils.py:

  • CashuTokenGenerator
  • ResponseValidator
  • PerformanceValidator
  • ConcurrencyTester
  • DatabaseStateValidator
  • MockServiceBuilder
  • TestDataBuilder

Writing Tests

Unit Test Example

from routstr.algorithm import calculate_model_cost_score
from routstr.payment.models import Architecture, Model, Pricing


def test_calculate_model_cost_score_basic() -> None:
    model = Model(
        id="test-model",
        name="Test test-model",
        created=1234567890,
        description="Test model",
        context_length=8192,
        architecture=Architecture(
            modality="text",
            input_modalities=["text"],
            output_modalities=["text"],
            tokenizer="gpt",
            instruct_type=None,
        ),
        pricing=Pricing(
            prompt=0.001,
            completion=0.002,
            request=0.0,
            image=0.0,
            web_search=0.0,
            internal_reasoning=0.0,
        ),
    )
    assert calculate_model_cost_score(model) == 0.002

Integration Test Example

import pytest
from httpx import AsyncClient


@pytest.mark.integration
@pytest.mark.asyncio
async def test_wallet_topup(
    authenticated_client: AsyncClient,
    testmint_wallet: object,
    db_snapshot: object,
) -> None:
    await db_snapshot.capture()
    token = await testmint_wallet.mint_tokens(1000)
    response = await authenticated_client.post(
        "/v1/wallet/topup", params={"cashu_token": token}
    )
    assert response.status_code == 200
    diff = await db_snapshot.diff()
    assert len(diff["api_keys"]["modified"]) == 1

Debugging Tips

# Show print output
pytest -s tests/unit/test_wallet.py

# Drop into debugger on failure
pytest --pdb

Troubleshooting

  • Docker mode failures: check docker ps and docker-compose -f compose.testing.yml logs
  • Connection errors: make sure ports 3338, 3000, 8000, and 8088 are free
  • Slow tests: use pytest -m "not slow" or make test-fast

Next Steps