Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,117 @@ jobs:
env:
INSTANT_API_REPO: ${{ github.workspace }}/../api
run: go test ./... -v -race -count=1

# ------------------------------------------------------------------
# GATING integration job (W2-T1).
#
# WHY: the worker's real-DB integration tests
# (internal/jobs/*_integration_test.go) skip under `-short` AND when
# TEST_DATABASE_URL is unset. The fast `build-and-test` job above runs
# `-race` with NO DB service and the deploy.yml test step runs
# `-short` — so in BOTH the integration tests SKIP and gate NOTHING.
# This job runs them WITHOUT `-short` against a real Postgres so the
# trigger→DB round-trip is actually asserted in CI. It mirrors
# coverage.yml's proven Postgres+Redis service block. Per the two-gate
# rule (root CLAUDE.md rule 15 / project_api_two_test_gates_ci_and_deploy),
# the same job is mirrored in deploy.yml — a worker test-infra change
# must land in BOTH or the deploy wedges while PR CI stays green.
integration:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: instant_dev_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
# Integration tests read TEST_DATABASE_URL and run the real-DB path
# (they SKIP when it's unset). TEST_REDIS_URL is wired for parity
# with coverage.yml and for the redis-touching jobs as they land.
TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/instant_dev_test?sslmode=disable
TEST_REDIS_URL: redis://localhost:6379/15
steps:
- uses: actions/checkout@v6

- name: Checkout proto sibling (replace ../proto)
uses: actions/checkout@v6
with:
repository: ${{ vars.PROTO_REPO || format('{0}/proto', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _proto_ci
- run: mv _proto_ci ../proto

- name: Checkout common sibling (replace ../common)
uses: actions/checkout@v6
with:
repository: ${{ vars.COMMON_REPO || format('{0}/common', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _common_ci
- run: mv _common_ci ../common

- name: Checkout api sibling (for migrations + cross-repo tests)
uses: actions/checkout@v6
with:
repository: ${{ vars.API_REPO || format('{0}/api', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _api_ci
fetch-depth: 1
- run: mv _api_ci ../api

- uses: actions/setup-go@v6
with:
go-version: '1.25'

- name: Apply DB migrations to the test database
# Mirrors coverage.yml: api owns the platform_db schema
# (teams / resources / deployments / pending_propagations /
# audit_log) the integration tests round-trip against; apply those
# first, then any worker-local SQL on top (IF NOT EXISTS-guarded).
env:
PGPASSWORD: postgres
run: |
if [ -d ../api/internal/db/migrations ]; then
for f in $(ls ../api/internal/db/migrations/*.sql | sort); do
echo "→ applying api migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null
done
echo "all api migrations applied to instant_dev_test"
else
echo "::error::no api migrations directory found at ../api/internal/db/migrations — integration tests would skip"
exit 1
fi
if [ -d sql ]; then
for f in $(ls sql/*.sql 2>/dev/null | sort); do
echo "→ applying worker migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null || echo "::warning::worker migration $(basename "$f") failed — likely overlap with api schema; continuing"
done
fi

- name: Run real-DB integration tests (NO -short, so they do not skip)
# `-run Integration` scopes the run to the _integration_test.go
# funcs (all carry "Integration" in their name); `-p 1` because
# every package shares the single instant_dev_test DB and default
# parallelism corrupts shared state. Dropping `-short` is what
# makes testhelpers.SetupTestDB's testing.Short() guard pass and
# the real-DB path run instead of skipping.
env:
INSTANT_API_REPO: ${{ github.workspace }}/../api
run: go test ./internal/... -run Integration -count=1 -p 1 -v
101 changes: 101 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,109 @@ env:
K8S_CONTAINER: worker

jobs:
# ------------------------------------------------------------------
# GATING integration job (W2-T1) — two-gate rule mirror of ci.yml.
#
# The deploy.yml test step ("Run unit tests") runs `go test ./...
# -short`, under which every worker real-DB integration test SKIPS
# (testhelpers.SetupTestDB / *_integration_test.go guard on
# testing.Short()). So before this job, a broken integration
# round-trip could merge to master and DEPLOY — the deploy gate never
# ran the integration tests. Per project_api_two_test_gates_ci_and_deploy
# (root CLAUDE.md rule 15), a worker test-infra change must land in
# BOTH ci.yml and deploy.yml or the deploy wedges while PR CI stays
# green. `deploy` now `needs: integration`, so a red integration run
# blocks the build/rollout.
integration:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: instant_dev_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/instant_dev_test?sslmode=disable
TEST_REDIS_URL: redis://localhost:6379/15
steps:
- uses: actions/checkout@v6

- name: Checkout proto sibling (replace ../proto)
uses: actions/checkout@v6
with:
repository: ${{ vars.PROTO_REPO || format('{0}/proto', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _proto_ci
- run: mv _proto_ci ../proto

- name: Checkout common sibling (replace ../common)
uses: actions/checkout@v6
with:
repository: ${{ vars.COMMON_REPO || format('{0}/common', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _common_ci
- run: mv _common_ci ../common

- name: Checkout api sibling (for migrations + cross-repo tests)
uses: actions/checkout@v6
with:
repository: ${{ vars.API_REPO || format('{0}/api', github.repository_owner) }}
token: ${{ secrets.REPO_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
path: _api_ci
fetch-depth: 1
- run: mv _api_ci ../api

- uses: actions/setup-go@v6
with:
go-version: '1.25'

- name: Apply DB migrations to the test database
env:
PGPASSWORD: postgres
run: |
if [ -d ../api/internal/db/migrations ]; then
for f in $(ls ../api/internal/db/migrations/*.sql | sort); do
echo "→ applying api migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null
done
echo "all api migrations applied to instant_dev_test"
else
echo "::error::no api migrations directory found at ../api/internal/db/migrations — integration tests would skip"
exit 1
fi
if [ -d sql ]; then
for f in $(ls sql/*.sql 2>/dev/null | sort); do
echo "→ applying worker migration $(basename "$f")"
psql -h localhost -U postgres -d instant_dev_test -f "$f" >/dev/null || echo "::warning::worker migration $(basename "$f") failed — likely overlap with api schema; continuing"
done
fi

- name: Run real-DB integration tests (NO -short, so they do not skip)
env:
INSTANT_API_REPO: ${{ github.workspace }}/../api
run: go test ./internal/... -run Integration -count=1 -p 1 -v

deploy:
runs-on: ubuntu-latest
needs: integration
steps:
- name: Checkout worker (this repo) into ./worker
uses: actions/checkout@v6
Expand Down
13 changes: 13 additions & 0 deletions internal/jobs/propagation_runner_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ func TestPropagation_ForUpdateSkipLockedIntegration(t *testing.T) {
ctx := context.Background()
teamID := uuid.New()
propID := uuid.New()
// pending_propagations.team_id carries a real FK to teams(id) in the api
// migration set (ON DELETE CASCADE). Against a fully-migrated DB (the
// non-short integration job's environment) the seed must reference a real
// team row or the INSERT fails the FK. Seed the parent team first; the FK
// cascade tidies the propagation row when the team is deleted in cleanup.
if _, err := db.ExecContext(ctx,
`INSERT INTO teams (id) VALUES ($1)`, teamID,
); err != nil {
t.Fatalf("seed team: %v", err)
}
t.Cleanup(func() {
_, _ = db.ExecContext(context.Background(), `DELETE FROM teams WHERE id = $1`, teamID)
})
if _, err := db.ExecContext(ctx, `
INSERT INTO pending_propagations
(id, kind, team_id, target_tier, payload, attempts, next_attempt_at, created_at)
Expand Down
Loading