Testing

Testing guidelines for Copinance OS. Favor unit tests on pure domain and tool logic; integration tests with mocked providers for I/O; use the DI container’s overrides to inject fakes for ResearchOrchestrator / JobRunner when testing orchestration.

Test Structure

Tests are organized to mirror the source code structure:

tests/
├── unit/                    # Unit tests
│   └── copinance_os/
│       ├── domain/
│       ├── research/workflows/
│       ├── data/
│       ├── core/
│       ├── ai/
│       ├── infra/
│       └── interfaces/cli/
└── integration/             # Integration tests
    └── copinance_os/
        ├── data/
        └── core/execution_engine/

Running Tests

pyproject.toml enables coverage in the default pytest invocation (addopts). Use pytest --no-cov for a faster loop when you do not need a coverage report.

# All tests (includes coverage per project defaults)
pytest
 
# Skip coverage
pytest --no-cov
 
# Unit tests only
pytest -m unit
 
# Integration tests only
pytest -m integration
 
# Explicit coverage HTML (also on by default unless --no-cov)
pytest --cov=copinance_os --cov-report=html

Writing Tests

Unit Tests

Test individual components in isolation:

import pytest
from copinance_os.domain.models.job import Job, JobScope, JobStatus, JobTimeframe
from copinance_os.domain.models.market import MarketType
 
def test_job_creation():
    job = Job(
        scope=JobScope.INSTRUMENT,
        market_type=MarketType.EQUITY,
        instrument_symbol="AAPL",
        timeframe=JobTimeframe.MID_TERM,
        execution_type="deterministic_instrument_analysis",
    )
    assert job.status == JobStatus.PENDING

Integration Tests

Test component interactions using fixtures from tests/conftest.py:

import pytest
from copinance_os.data.repositories import StockRepositoryImpl
from copinance_os.domain.models.stock import Stock
 
@pytest.mark.integration
async def test_stock_repository_save_and_get(isolated_storage):
    repo = StockRepositoryImpl(storage=isolated_storage)
    stock = Stock(symbol="AAPL", name="Apple Inc.", exchange="NASDAQ")
 
    saved = await repo.save(stock)
    retrieved = await repo.get_by_symbol(saved.symbol)
 
    assert retrieved is not None
    assert retrieved.symbol == saved.symbol

Test Fixtures

Common fixtures are available in tests/conftest.py:

  • isolated_storage: Isolated storage for each test
  • profile_repository: Profile repository with isolated storage

Test Markers

Use markers to categorize tests:

@pytest.mark.unit
def test_unit_example():
    pass
 
@pytest.mark.integration
async def test_integration_example():
    pass

Best Practices

  1. Isolation: Each test should be independent
  2. Fixtures: Use fixtures for common setup
  3. Async: Use pytest-asyncio for async tests
  4. Coverage: Aim for high test coverage
  5. Naming: Use descriptive test names