From aba64cceffc9f3fbfa7247596083e1b3f5ebe972 Mon Sep 17 00:00:00 2001 From: Tayeb Mokni Date: Tue, 26 May 2026 20:26:26 +0200 Subject: [PATCH 1/2] fix(migrate): use max-version instead of file-count in integration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TestRun_IntegrationApplyAndRollback assertion required count(*.up.sql) == max_applied_version. That breaks when parallel branches each pick a non-contiguous migration slot (every PR's CI fails until you renumber every other PR's migration). Use the lexically-greatest filename's numeric prefix instead — the migration runner uses the prefix as the version anyway, so the assertion is still meaningful but no longer over-constrained. Signed-off-by: Tayeb Mokni --- packages/go/migrate/migrate_test.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/go/migrate/migrate_test.go b/packages/go/migrate/migrate_test.go index d2df7973..e498c1b8 100644 --- a/packages/go/migrate/migrate_test.go +++ b/packages/go/migrate/migrate_test.go @@ -6,6 +6,8 @@ import ( "log/slog" "os" "path/filepath" + "sort" + "strconv" "strings" "testing" @@ -112,9 +114,13 @@ func TestSourceURLForDir_Relative(t *testing.T) { // --- Integration tests below: require a real Postgres + migrations dir --- -// countMigrations returns the number of up-migration files in dir. The -// canonical migration runner picks them up in lexical order, so this -// is the version we expect Status() to report after a successful Run(). +// countMigrations returns the highest version number among the +// up-migration files in dir. The canonical migration runner uses the +// numeric prefix as the version; we parse the prefix off the last +// (lexically-greatest) file and report that. Using the max version +// rather than len() makes the test robust against gaps (parallel +// branches can each take their own slot without forcing every PR to +// rebase on every other). func countMigrations(t *testing.T, dir string) uint { t.Helper() matches, err := filepath.Glob(filepath.Join(dir, "*.up.sql")) @@ -124,7 +130,16 @@ func countMigrations(t *testing.T, dir string) uint { if len(matches) == 0 { t.Fatalf("no *.up.sql files found in %s", dir) } - return uint(len(matches)) //nolint:gosec // file count, can't overflow + // Lexical sort puts the highest 000NNN_ prefix last. + sort.Strings(matches) + last := filepath.Base(matches[len(matches)-1]) + // "000033_foo.up.sql" → "000033" + prefix := strings.SplitN(last, "_", 2)[0] + n, err := strconv.ParseUint(prefix, 10, 64) + if err != nil { + t.Fatalf("parse version prefix %q from %q: %v", prefix, last, err) + } + return uint(n) //nolint:gosec } func TestRun_IntegrationApplyAndRollback(t *testing.T) { From 8af0031dcabd4b6406f274a6a11499e6119806e1 Mon Sep 17 00:00:00 2001 From: Tayeb Mokni Date: Tue, 26 May 2026 20:49:50 +0200 Subject: [PATCH 2/2] fix(testutil/containers): skip Postgres/Redis/MinIO helpers under -short Pushes the testing.Short() skip up into the three container helpers themselves rather than scattering it across 20+ call sites. Every integration test that calls containers.Postgres(), .Redis(), or .MinIO() now skips automatically under -short. The nightly-full-tests workflow runs without -short so these paths are still exercised against tip-of-main. Signed-off-by: Tayeb Mokni --- cli/gonext/cmd/init/setup_test.go | 3 +++ packages/go/testutil/containers/minio.go | 3 +++ packages/go/testutil/containers/postgres.go | 8 ++++++++ packages/go/testutil/containers/redis.go | 3 +++ 4 files changed, 17 insertions(+) diff --git a/cli/gonext/cmd/init/setup_test.go b/cli/gonext/cmd/init/setup_test.go index a8e5be0f..16c0dd1d 100644 --- a/cli/gonext/cmd/init/setup_test.go +++ b/cli/gonext/cmd/init/setup_test.go @@ -83,6 +83,9 @@ func TestSetup_RequiresPepper(t *testing.T) { // seed, then assert on the resulting rows. func TestSetup_HappyPath_FullFlow(t *testing.T) { t.Parallel() + if testing.Short() { + t.Skip("integration test: testcontainers spin-up flakes on shared CI runners; runs in nightly") + } dsn := containers.Postgres(t) if dsn == "" { t.Skip("docker not available") diff --git a/packages/go/testutil/containers/minio.go b/packages/go/testutil/containers/minio.go index e6e97cba..74a16b3d 100644 --- a/packages/go/testutil/containers/minio.go +++ b/packages/go/testutil/containers/minio.go @@ -29,6 +29,9 @@ import ( // just add a layer of dereferencing for no gain. func MinIO(t testing.TB, opts ...MinIOOption) (endpoint, accessKey, secretKey string) { t.Helper() + if testing.Short() { + t.Skip("integration test: skip under -short (covered by nightly-full-tests workflow)") + } if skipIfNoDocker(t) { return "", "", "" } diff --git a/packages/go/testutil/containers/postgres.go b/packages/go/testutil/containers/postgres.go index 5ac41a07..3ca518a3 100644 --- a/packages/go/testutil/containers/postgres.go +++ b/packages/go/testutil/containers/postgres.go @@ -26,6 +26,14 @@ import ( // to accept SQL connections before returning. func Postgres(t testing.TB, opts ...PGOption) (dsn string) { t.Helper() + // Integration tests are skipped under `go test -short`. PR CI uses + // -short for stability (testcontainers spin-up jitter on shared + // GitHub runners causes flakes that block the merge queue); the + // nightly-full-tests workflow runs without -short to keep these + // paths exercised against tip-of-main. + if testing.Short() { + t.Skip("integration test: skip under -short (covered by nightly-full-tests workflow)") + } if skipIfNoDocker(t) { return "" } diff --git a/packages/go/testutil/containers/redis.go b/packages/go/testutil/containers/redis.go index 78cafea1..557d26c2 100644 --- a/packages/go/testutil/containers/redis.go +++ b/packages/go/testutil/containers/redis.go @@ -21,6 +21,9 @@ import ( // via the URL path). func Redis(t testing.TB, opts ...RedisOption) (url string) { t.Helper() + if testing.Short() { + t.Skip("integration test: skip under -short (covered by nightly-full-tests workflow)") + } if skipIfNoDocker(t) { return "" }