pkg/, package-level tests under tests/pkg/, and integration and end-to-end tests under tests/integration/ and tests/e2e/. All layers are discoverable by a single go test ./... invocation, so there is no separate test runner to learn.
Test Categories
| Category | Location | Requirements | Run command |
|---|---|---|---|
| Unit | pkg/**/*_test.go and tests/pkg/... | None — fully offline | go test ./pkg/... ./tests/pkg/... |
| Integration | tests/integration/... | Docker services (DB, Redis) running; real API keys for provider tests | go test ./tests/integration/... |
| End-to-end | tests/e2e/... | Full stack running (server + external services) | go test ./tests/e2e/... |
| Evals | cmd/evals/ | Real API keys; runs against live providers | make evals |
Provider-specific tests (e.g. Sarvam, Groq, ElevenLabs) are automatically skipped when the corresponding
*_API_KEY environment variable is not set. You do not need to comment them out locally.Running Tests
All tests
Unit tests only
Integration tests
End-to-end tests
tests/README.md for which services and config keys each suite requires.
Evals
cmd/evals/.
Race Detection
Always run the suite with-race before opening a pull request. The Voxray pipeline is heavily concurrent and race conditions are easy to introduce in new processors or service clients:
-timeout 5m guard prevents a goroutine leak or deadlock from hanging CI indefinitely.
Integration Test Requirements
Provider integration tests intests/pkg/services/<provider>/ follow a uniform skip pattern:
<PROVIDER>_API_KEY. Exceptions (AWS, Google Vertex, Qwen) are documented in CONTRIBUTING.md.
For full provider coverage in a local integration run, set the keys for the providers you want to test:
docker-compose up -d to start them.
Test Discovery and File Conventions
- Test files use the
_test.gosuffix and live either alongside the code (pkg/audio/audio_test.go) or in the centralizedtests/pkg/tree mirroring thepkg/layout (e.g.tests/pkg/pipeline/pipeline_test.goforpkg/pipeline). - External test packages (e.g.
package pipeline_test) are preferred over internal (package pipeline) for tests intests/pkg/— they test the public API as a consumer would. - Shared test fixtures and golden files go under
tests/testdata/. - Because
tests/is part of the Go module tree,go test ./...automatically discovers everything. No build tags or manual registration is needed.
Writing Good Tests
Testing processors
Processors are the most common thing contributors add or modify. The key pattern is to construct a minimal processor chain, push a frame into it, and assert on the frames that emerge at the sink end.pkg/frames/ directly — they are lightweight value types and safe to construct in tests without any mocking library.
Test frame routing. Verify that error frames travel upstream and data frames travel downstream. A common bug is accidentally reversing the direction when forwarding from a base processor.
Context cancellation. For processors that own goroutines (e.g. streaming STT or TTS), cancel the context and assert that the goroutine exits (e.g. by waiting on a done channel or checking that the output channel is closed).
Testing services (STT, LLM, TTS)
Usenet/http/httptest to mock provider HTTP endpoints. This keeps tests offline and deterministic:
Table-driven tests
Prefer table-driven tests for anything with multiple input/output combinations:Makefile Targets
| Target | Command | Description |
|---|---|---|
make test | go test ./... | Run all tests (unit + integration + e2e) |
make build | go build -o voxray ./cmd/voxray | Build the main binary |
make evals | go run ./cmd/evals | Run provider quality evaluations |
The
Makefile does not yet have separate test-unit, test-integration, and test-e2e targets, but you can run those scopes directly with go test as shown in the commands above. Contributions that add these focused targets are welcome.CI
The CI workflow runsgo test ./... on every push and pull request. A failing test in any package blocks merge. The race detector (-race) is strongly recommended locally but may not be enabled in all CI configurations — check the workflow file for the current state and feel free to propose enabling it.