Skip to main content
Testing is a core part of Strait development process. We use a combination of unit, integration, E2E, and fuzz tests to ensure system reliability.

Test Infrastructure

Shared testing utilities are located in apps/strait/internal/testutil/:
  • testdb.go: Helpers for spinning up ephemeral PostgreSQL instances using testcontainers-go.
  • testredis.go: Helpers for ephemeral Redis instances.
  • factory.go: Data factories for generating test entities (jobs, runs, workflows).
  • assert.go: Custom assertion helpers for common domain checks (uses samber/lo for collection queries).
  • cmp.go: Structural comparison helpers powered by google/go-cmp for rich, human-readable diffs.

Running Tests

Unit Tests

Unit tests are fast and do not require external dependencies.

Integration Tests

Integration tests require Docker to run testcontainers. They are marked with the integration build tag.

End-to-End (E2E) Tests

E2E tests verify the entire system flow, from API request to job execution.

Fuzz Tests

We use Go’s native fuzzing to test the robustness of the FSM transitions.

Benchmarks

Performance benchmarks for critical paths like the job queue and workflow engine.

Event Trigger Benchmarks

Integration-tagged benchmarks for event trigger database operations:
Available benchmarks:
  • BenchmarkListExpiredEventTriggers — Measures reaper query performance with 1000 triggers
  • BenchmarkListByKeyPrefix — Measures prefix matching query performance with 1000 triggers

Load Tests

Strait has two load testing approaches that cover different concerns.

Vegeta Load Tests (HTTP)

Vegeta-based tests in apps/strait/test/loadtest/ exercise every API route at scale using three attack modes: baseline (fixed-rate SLA validation), stress (high-rate ceiling discovery), and spike (linear ramp). Tests require the loadtest and integration build tags and spin up a full API server with testcontainers PostgreSQL.
Test files cover all API surface areas:
  • jobs_test.go — CRUD, listing, filtering, pagination, batch operations
  • triggers_test.go — Single, bulk, idempotent, delayed, priority triggers
  • runs_test.go — Listing, filtering, status transitions, replay, annotations
  • workflows_test.go — CRUD, triggering, step run listing, concurrent operations
  • sdk_test.go — Heartbeat, logging, checkpoints, progress, continuation
  • webhooks_test.go — Subscription management
  • rbac_test.go — API key CRUD, scoped access enforcement
  • events_test.go — Event sending and listing
  • job_groups_test.go — Group CRUD, job assignment
  • environments_test.go — Environment CRUD, variable management
  • mixed_test.go — Cross-resource concurrent operations
  • edge_cases_test.go — Malformed payloads, boundary values, conflict handling

Integration Load Tests (Go)

Go-native load tests in apps/strait/internal/e2e/ test internal subsystem throughput using testcontainers. They exercise queue, worker, webhook, workflow, rate limiting, store, and full pipeline operations at configurable volumes.
Control volume via LOADTEST_VOLUME_TIER:
Available tiers: default (500 items), large (5,000 items), extreme (10,000 items). Test files:
  • load_queue_test.go — Enqueue/dequeue throughput, concurrent operations, FSM transitions
  • load_worker_test.go — Bulk trigger, concurrent triggers, SDK heartbeat flood
  • load_webhook_test.go — Subscription CRUD throughput, delivery listing
  • load_workflow_test.go — Workflow creation, triggering, concurrent operations
  • load_ratelimit_test.go — Burst, sustained, mixed rate limit scenarios
  • load_store_test.go — Direct store throughput for jobs, events, FSM, pagination
  • load_pipeline_test.go — Full trigger-dequeue-complete lifecycle pipelines

Event Trigger Load Tests

Legacy integration-tagged load tests for event trigger throughput:
Available load tests:
  • TestEventTriggerLoadCreate — Sequential create throughput (500 triggers)
  • TestEventTriggerLoadSendConcurrent — 50 concurrent event sends

CI Workflow

Load tests run via the Load Tests GitHub Action (.github/workflows/loadtest.yml). This workflow is manually triggered via workflow_dispatch with configurable volume tier and duration. It runs Vegeta and integration load tests in parallel jobs, generates markdown summaries, and publishes results to the GitHub Actions step summary.

Advanced Testing Tools

Race Detector

Always run tests with the race detector enabled in CI and during local development of concurrent features.

Code Coverage

Generate a coverage report to identify untested paths.

Test Patterns

Table-Driven Tests

We prefer table-driven tests for testing multiple scenarios with the same logic.

Parallel Execution

Use t.Parallel() in unit tests to speed up execution.

Structural Assertions with go-cmp

Use testutil.AssertEqual for comparing complex structs. It produces rich, human-readable diffs on failure instead of opaque assertion messages.
Available helpers in apps/strait/internal/testutil/cmp.go:
  • AssertEqual(t, got, want, ...cmp.Option): Structural comparison with rich diff output.
  • AssertJSONEqual(t, a, b []byte): JSON-aware comparison that ignores key ordering.
  • EquateEmpty(): Treats nil slices/maps as equal to empty ones.
  • IgnoreFields(typ, ...names): Skips specified fields during comparison (useful for timestamps, IDs).

Testcontainers

We use testcontainers-go to manage the lifecycle of PostgreSQL and Redis during integration tests. This ensures a clean state for every test suite.

Mock Patterns

For packages like apps/strait/internal/api and apps/strait/internal/scheduler, we use mock stores with configurable function fields:
Each method delegates to its function field if set, falling back to sensible defaults. This enables precise control over test behavior without implementing the full store interface. For pubsub testing, mockPublisher supports configurable subscribeFn that returns real pubsub.Subscription instances with channels, enabling SSE stream testing without Redis.

Test Fixtures

Static test data and JSON payloads are stored in testdata/ directories within their respective packages.