> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strait.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Overview of the testing infrastructure and patterns used in Strait.

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.

```bash theme={null}
cd apps/strait && go test ./...
```

### Integration Tests

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

```bash theme={null}
cd apps/strait && go test -tags integration ./...
```

### End-to-End (E2E) Tests

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

```bash theme={null}
cd apps/strait && go test -tags integration ./internal/e2e/...
```

### Fuzz Tests

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

```bash theme={null}
cd apps/strait && go test -fuzz FuzzFSMTransition ./internal/domain/
```

### Benchmarks

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

```bash theme={null}
cd apps/strait && go test -bench . ./internal/...
```

### Event Trigger Benchmarks

Integration-tagged benchmarks for event trigger database operations:

```bash theme={null}
# Requires DATABASE_URL and 'integration' build tag
go test -tags integration -bench . ./internal/e2e/...
```

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.

```bash theme={null}
cd apps/strait && go test -tags "loadtest,integration" -timeout=30m ./test/loadtest/...
```

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.

```bash theme={null}
cd apps/strait && go test -tags integration -run "TestLoad" -timeout=30m ./internal/e2e/...
```

Control volume via `LOADTEST_VOLUME_TIER`:

```bash theme={null}
cd apps/strait && LOADTEST_VOLUME_TIER=large go test -tags integration -run "TestLoad" ./internal/e2e/...
```

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:

```bash theme={null}
cd apps/strait && go test -tags integration -run TestEventTriggerLoad ./internal/e2e/...
```

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.

```bash theme={null}
gh workflow run "Load Tests" --field volume_tier=large
```

## Advanced Testing Tools

### Race Detector

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

```bash theme={null}
cd apps/strait && go test -race ./...
```

### Code Coverage

Generate a coverage report to identify untested paths.

```bash theme={null}
cd apps/strait
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```

## Test Patterns

### Table-Driven Tests

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

```go theme={null}
func TestCalculateBackoff(t *testing.T) {
    tests := []struct {
        name     string
        attempt  int
        expected time.Duration
    }{
        {"first attempt", 1, 1 * time.Second},
        {"second attempt", 2, 2 * time.Second},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // ...
        })
    }
}
```

### 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.

```go theme={null}
func TestCreateJob(t *testing.T) {
    got := store.GetJob(ctx, id)
    testutil.AssertEqual(t, got, want)
}
```

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:

```go theme={null}
type mockAPIStore struct {
    getEventTriggerByEventKeyFn func(ctx context.Context, key string) (*domain.EventTrigger, error)
    // ... other store methods
}
```

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.
