Skip to main content

Testing

Test Commands​

# All unit tests with race detector
make test

# Single package
go test ./internal/supervisor/...

# Single test
go test -run TestHooksIntegration ./cmd/runix/...

# E2E tests (no race flag, longer timeout)
go test -v -count=1 -timeout 120s ./internal/e2e/...

Full CI Reproduction​

make test && go test -v -count=1 -timeout 120s ./internal/e2e/...

Test Conventions​

Framework​

  • Standard library testing only — no testify, ginkgo, or other frameworks
  • Use t.Fatal, t.Fatalf, t.Error, t.Errorf for assertions
  • Use t.TempDir() for temporary directories (auto-cleaned)

File Placement​

Test TypeLocation
Unit tests<package>/<name>_test.go (same package)
Integration tests<package>/integration_test.go or internal/e2e/
CLI testscmd/runix/<name>_test.go

Table-Driven Tests​

func TestValidateRuntime(t *testing.T) {
tests := []struct {
name string
runtime string
wantErr bool
}{
{"go", "go", false},
{"python", "python", false},
{"invalid", "ruby", true},
{"empty", "", true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateRuntime(tt.runtime)
if (err != nil) != tt.wantErr {
t.Errorf("validateRuntime(%q) error = %v, wantErr %v", tt.runtime, err, tt.wantErr)
}
})
}
}

Hook Integration Tests​

Hook tests use marker files to verify execution:

func TestHooksIntegration(t *testing.T) {
dir := t.TempDir()
markerFile := dir + "/hook_markers.txt"

// Configure hooks that write to marker file
cfg := types.ProcessConfig{
Hooks: &types.ProcessHooks{
PreStart: &types.HookConfig{
Command: "echo pre_start >> " + markerFile,
Timeout: 5 * time.Second,
},
},
}

// Start and stop process, then verify markers
// ...
}

E2E Tests​

E2E tests in internal/e2e/ use a real supervisor with actual sleep commands:

func TestE2EStartStop(t *testing.T) {
dir := t.TempDir()
sup := supervisor.New(supervisor.Options{
LogDir: dir + "/logs",
Defaults: types.DefaultsConfig{
RestartPolicy: types.RestartNever,
},
})

cfg := types.ProcessConfig{
Name: "sleeper",
Runtime: "unknown",
Entrypoint: "sleep",
Args: []string{"60"},
Cwd: dir,
}

proc, err := sup.AddProcess(context.Background(), cfg)
if err != nil {
t.Fatalf("AddProcess: %v", err)
}

// Verify running, then stop
// ...
}

Test Packages with Tests​

PackageTest Files
cmd/runixhooks_test.go
internal/supervisor*_test.go
internal/config*_test.go
internal/hooks*_test.go
internal/healthcheck*_test.go
internal/secrets*_test.go
internal/cgroups*_test.go
internal/logrot*_test.go
internal/events*_test.go
internal/updater*_test.go
internal/e2eE2E suite

What's Next​