From dc482ff169127579d98f0d90370d71b8572c8fa9 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sun, 12 Apr 2026 07:18:01 +0700 Subject: [PATCH] ci: run integration suite against pgvector service container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integration tests were running locally only — CI never exercised the 29 v3_*_test.go files under tests/integration/. This left the session tenant-isolation + vault scan-arity bugs undetected until a manual run this session. Changes: - tests/integration/v3_test_helper.go: testDB() now calls pg.InitSqlx(db) inside sharedDBOnce. Previously the test suite relied on whichever test ran first happening to call InitSqlx, so running with `-run ` could segfault with a nil pkgSqlxDB. Removes the ordering-dependency land mine. - .github/workflows/ci.yaml: add services.pg block running pgvector/pgvector:pg18 with pg_isready healthcheck, set TEST_DATABASE_URL at job level, and add a new "Integration tests" step after unit tests. Uses -timeout=180s (vs 90s unit) because the first test runs migrations from scratch. Local integration suite: 2.9s. CI cold start expected ~30-60s with image pull + migrations. --- .github/workflows/ci.yaml | 31 ++++++++++++++++++++++++++++- tests/integration/v3_test_helper.go | 8 ++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f2e0af46..075f2a6f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,6 +12,28 @@ permissions: jobs: go: runs-on: ubuntu-latest + # Postgres service container for integration tests. pgvector/pgvector:pg18 + # matches the local dev image (Makefile uses the same tag on port 5433). + # Healthcheck blocks the job until PG accepts connections, avoiding + # first-connection flakiness on cold runner starts. + services: + pg: + image: pgvector/pgvector:pg18 + env: + POSTGRES_PASSWORD: test + POSTGRES_DB: goclaw_test + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U postgres" + --health-interval 5s + --health-timeout 3s + --health-retries 10 + env: + # tests/integration/v3_test_helper.go reads TEST_DATABASE_URL and runs + # migrations once via golang-migrate. Uses port 5432 (CI-clean) vs 5433 + # for local dev (avoids clash with desktop Postgres installs). + TEST_DATABASE_URL: postgres://postgres:test@localhost:5432/goclaw_test?sslmode=disable steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -24,7 +46,14 @@ jobs: # -timeout=90s caps each test binary so a deadlocked test fails fast # instead of blocking CI for the 10-minute default. Race-heavy wave C # suites run in <20s, so 90s leaves ample breathing room. - - run: go test -race -timeout=90s -coverpkg=./... -coverprofile=coverage.out ./... + - name: Unit tests + run: go test -race -timeout=90s -coverpkg=./... -coverprofile=coverage.out ./... + # Integration suite runs against the `pg` service container above. + # Longer -timeout=180s because the first test runs migrations from + # scratch (can take 10-30s on a cold CI runner). Local full suite + # completes in <3s against a warm DB. + - name: Integration tests + run: go test -race -timeout=180s -tags integration ./tests/integration/ - name: Coverage summary run: go tool cover -func=coverage.out | tail -1 diff --git a/tests/integration/v3_test_helper.go b/tests/integration/v3_test_helper.go index 0c620787..66062546 100644 --- a/tests/integration/v3_test_helper.go +++ b/tests/integration/v3_test_helper.go @@ -16,6 +16,7 @@ import ( _ "github.com/jackc/pgx/v5/stdlib" "github.com/nextlevelbuilder/goclaw/internal/store" + "github.com/nextlevelbuilder/goclaw/internal/store/pg" ) const defaultTestDSN = "postgres://postgres:test@localhost:5433/goclaw_test?sslmode=disable" @@ -58,6 +59,13 @@ func testDB(t *testing.T) *sql.DB { return } m.Close() + + // Initialize pg package's sqlx wrapper. Without this, any store + // method that uses pkgSqlxDB.SelectContext panics on nil deref if + // the test runs before any other test happens to call InitSqlx. + // Centralizing here removes the ordering-dependency land mine. + pg.InitSqlx(db) + sharedDB = db })