package testutils import ( "os" "sync" "testing" "github.com/testcontainers/testcontainers-go" ) // SkipIfNoIntegration skips the current test when integration prerequisites // (notably a running Docker daemon for testcontainers-go) are unavailable. // // It honors three conditions, in order: // // 1. `go test -short` — skip for fast dev cycles. // 2. VEZA_SKIP_INTEGRATION=1 — explicit opt-out, used by CI matrices // that only run unit tests. // 3. Runtime Docker probe — if testcontainers-go cannot construct a // Docker provider (daemon down, rootless socket missing, Docker // Desktop not running, etc.), the test is SKIPPED instead of // failing inside testcontainers.GenericContainer. // // The Docker probe is memoized with sync.Once, so it runs at most once // per test process regardless of how many tests call SkipIfNoIntegration. // // Call this at the very top of any test helper that relies on // GetTestContainerDB, GetTestRedisClient, or any other testcontainers // entry point. func SkipIfNoIntegration(t *testing.T) { t.Helper() if testing.Short() { t.Skip("integration test requires Docker; skipped (-short)") } if os.Getenv("VEZA_SKIP_INTEGRATION") == "1" { t.Skip("integration test requires Docker; skipped (VEZA_SKIP_INTEGRATION=1)") } if !dockerAvailable() { t.Skip("integration test requires Docker; skipped (no Docker provider reachable)") } } var ( dockerOnce sync.Once dockerReachable bool ) // dockerAvailable probes the testcontainers Docker provider once per test // process. A successful NewDockerProvider call means the client can reach // the daemon (testcontainers dials the socket at construction). Any error // is treated as "Docker not available" and makes all integration tests // using SkipIfNoIntegration skip cleanly. func dockerAvailable() bool { dockerOnce.Do(func() { provider, err := testcontainers.NewDockerProvider() if err != nil { return } _ = provider.Close() dockerReachable = true }) return dockerReachable }