Skip to main content
Tests live under src/voicegateway/tests/, mirroring the package layout (tests/middleware/ for middleware/, tests/core/ for core/, and so on). The coverage gate is fail_under under [tool.coverage.report] in pyproject.toml.

Running tests

pytest configuration

pyproject.toml sets:
asyncio_mode = "auto" means async def test_... functions run without a @pytest.mark.asyncio decorator.

Shared fixtures

src/voicegateway/tests/conftest.py defines:

_test_env (autouse)

Sets fake API keys (OPENAI_API_KEY, DEEPGRAM_API_KEY, CARTESIA_API_KEY, ANTHROPIC_API_KEY, GROQ_API_KEY, ELEVENLABS_API_KEY, ASSEMBLYAI_API_KEY) via monkeypatch so provider constructors never fail on a missing key. Runs for every test automatically.

_isolate_db_path_env (autouse)

Snapshots and restores VOICEGW_DB_PATH around each test. VOICEGW_DB_PATH beats every other DB path source, so a test that sets it and forgets to unset it redirects every later StorageService in the run into one shared file. If you see no such table on a test’s own tmp database, or a UNIQUE-constraint collision between unrelated tests, this is usually the cause: check for a leaked monkeypatch.setenv("VOICEGW_DB_PATH", ...) outside this fixture’s protection.

example_config_path

Writes the bundled starter config (src/voicegateway/data/voicegw.example.yaml) to a tmp file and returns its path:

temp_config

Writes a minimal voicegw.yaml (OpenAI + Deepgram providers, one STT model, one LLM model, a test-project and blocked-project, cost tracking on) to a tmp directory, points VOICEGW_DB_PATH at an isolated file, and returns the config path:

seeded_storage

Async fixture. Creates a StorageService (src/voicegateway/services/storage_service.py) pre-loaded with three sample RequestRecord rows:

Writing tests

File-name pattern: test_<module>.py. Function-name pattern: test_<behaviour>.

Async tests

Write plain async def test_... functions; asyncio_mode = "auto" picks them up without a decorator. The next section has a full example.

Mocking a provider’s health check

BaseProvider.health_check() is the only method a provider subclass exercises in production (the health-check surface: dashboard Test Connection, voicegw doctor, the MCP server’s admin test_provider tool). Follow src/voicegateway/tests/providers/test_cartesia_health_check.py: mock httpx.AsyncClient, not the provider method itself.

Resolving a provider/model string

Testing cost calculations

voicegateway.inference.pricing.catalog.calculate_cost(modality, model, **units) is the modality-dispatching entry point; stt.py / llm.py / tts.py do the actual voice-prices lookup per modality. See Refreshing Pricing.

Testing storage directly

monkeypatch.setenv for environment variables

Coverage expectations

  • New features must include tests.
  • Bug fixes should include a regression test.
  • The suite must stay at or above fail_under in pyproject.toml’s [tool.coverage.report].