-
Notifications
You must be signed in to change notification settings - Fork 0
282 lines (267 loc) · 13.4 KB
/
Copy pathcoverage.yml
File metadata and controls
282 lines (267 loc) · 13.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
name: coverage
# Coverage CI for the provisioner.
#
# This job runs the FULL test suite against real Postgres / Redis / Mongo /
# NATS service containers so the env-gated integration tests actually execute
# and contribute coverage — instead of skipping silently on a DB-less runner.
#
# Why the service containers matter (2026-05-22):
# A large slice of the provisioner's executable surface is exercised by tests
# that t.Skip() unless a real backend is reachable:
# - internal/pool/manager_db_test.go → TEST_PROVISIONER_DATABASE_URL
# - internal/backend/postgres/local_live_test → TEST_POSTGRES_ADMIN_DSN /
# CUSTOMER_POSTGRES_DSN /
# TEST_POSTGRES_CUSTOMERS_URL
# - internal/backend/postgres/k8s_provision_test → TEST_REDIS_ADDR
# - internal/backend/redis/coverage_test → CUSTOMER_REDIS_URL
# (+ hardcoded :6379 path)
# - internal/backend/mongo/*_test → CUSTOMER_MONGO_URL,
# CUSTOMER_MONGO_AUTH_URL,
# REDIS_URL_TEST
# With no service containers those files skipped and the pool package sat at
# 47% while the suite still reported "green". Wiring the containers + the
# exact TEST_* env var names below moved pool 47%→96%, redis 84%→95%,
# mongo 91%→96% and pushed the full-suite total over the 95% floor.
#
# Note: the suite is NOT gated on `testing.Short()` (grep -rn "testing.Short()"
# internal/ → no hits) — the gate is purely env-var reachability, so `-short`
# is intentionally NOT passed here.
#
# Container ports use the standard host ports (5432/6379/27017/4222) because a
# few tests hardcode them (e.g. redis StorageBytes connects to the Service
# ClusterIP on :6379, and coverage_test's liveRedisAddr falls back to
# localhost:6379). On the Linux runner the service-container network has no
# IPv6/loopback flakiness, so 127.0.0.1 host addresses connect cleanly.
on:
pull_request:
branches: [master, main]
push:
branches: [master, main]
permissions:
contents: read
jobs:
coverage:
runs-on: ubuntu-latest
timeout-minutes: 15
# Real backends for the env-gated integration tests. Health-check options
# gate the job's steps until each container is accepting connections.
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 5s
--health-retries 10
mongo:
image: mongo:6
ports:
- 27017:27017
options: >-
--health-cmd "mongosh --quiet --eval 'db.runCommand({ ping: 1 }).ok' || mongo --quiet --eval 'db.runCommand({ ping: 1 }).ok'"
--health-interval 5s
--health-timeout 5s
--health-retries 10
# Authenticated Mongo for the CUSTOMER_MONGO_AUTH_URL auth-fail branches
# (TestLocalProvision_AuthFailReturnsError et al.). Runs on a second host
# port so it never collides with the no-auth mongo above.
mongo-auth:
image: mongo:6
env:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: rootpass
ports:
- 27018:27017
options: >-
--health-cmd "mongosh --quiet -u root -p rootpass --eval 'db.runCommand({ ping: 1 }).ok' || mongo --quiet -u root -p rootpass --eval 'db.runCommand({ ping: 1 }).ok'"
--health-interval 5s
--health-timeout 5s
--health-retries 10
# NATS (JetStream + HTTP monitor :8222) for the server-layer queue
# round-trip (internal/server/server_live_roundtrip_mqs_test.go::
# TestServer_Queue_*) is started as a `docker run` step below (see
# "Start NATS for queue round-trip"), NOT a `services:` container: the
# minimal nats:2 image has no wget/curl/nc, so a service-container
# `--health-cmd` can NEVER pass — GitHub Actions then marks the container
# unhealthy and aborts the whole job even though the server logged
# "Server is ready". docker-run + a runner-side curl wait avoids that.
# NOTE: MinIO is NOT a `services:` container — GitHub Actions service
# containers cannot supply the required `server /data` command (services
# accept image/env/ports/options only, not args). MinIO is started as a
# `docker run` step below (see "Start MinIO for storage round-trip"), which
# the server-layer storage test (TestServer_Storage_GetStorageBytes_*)
# reaches at 127.0.0.1:9100.
# TEST_* env vars the pool/backend test helpers read via os.Getenv. The
# exact names were grepped from the test files (os.Getenv("TEST_…") and the
# liveXxx() helper fallbacks) — wrong names = silent skip = lost coverage.
env:
# internal/pool/manager_db_test.go::testDSN
TEST_PROVISIONER_DATABASE_URL: postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
# internal/backend/postgres/local_live_test.go::testAdminDSN (any of three)
TEST_POSTGRES_ADMIN_DSN: postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
CUSTOMER_POSTGRES_DSN: postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
TEST_POSTGRES_CUSTOMERS_URL: postgres://postgres:postgres@127.0.0.1:5432/postgres?sslmode=disable
# internal/backend/postgres/k8s_provision_test.go
TEST_REDIS_ADDR: 127.0.0.1:6379
# internal/backend/redis/coverage_test.go::liveRedisAddr
CUSTOMER_REDIS_URL: redis://127.0.0.1:6379
# internal/backend/mongo/coverage_extra_test.go route-registry arm
REDIS_URL_TEST: redis://127.0.0.1:6379
# internal/backend/mongo/local_test.go::liveMongoURI
CUSTOMER_MONGO_URL: mongodb://127.0.0.1:27017
# internal/backend/mongo/coverage_extra_test.go auth-fail branches
# ALSO: internal/server/server_live_roundtrip_mqs_test.go::liveMongoAdminURI
# (the gRPC-layer mongo Provision/StorageBytes/Deprovision round-trip uses
# the authenticated instance — the realistic prod createUser path).
CUSTOMER_MONGO_AUTH_URL: mongodb://root:rootpass@127.0.0.1:27018
# internal/server/server_live_roundtrip_mqs_test.go::liveNATSHost — the
# queue LocalBackend health-checks http://$NATS_HOST:8222/healthz.
TEST_NATS_HOST: 127.0.0.1
# internal/server/server_live_roundtrip_mqs_test.go::liveStorageEndpoint —
# the storage GetStorageBytes round-trip against the MinIO started below.
TEST_MINIO_ENDPOINT: 127.0.0.1:9100
TEST_MINIO_ROOT_USER: minioadmin
TEST_MINIO_ROOT_PASSWORD: minioadmin
TEST_MINIO_BUCKET: itest-bucket
steps:
- uses: actions/checkout@v6
with:
path: provisioner
# Full history so diff-cover can resolve origin/<base_ref>.
fetch-depth: 0
- uses: actions/checkout@v6
with:
repository: InstaNode-dev/common
path: common
continue-on-error: true
- uses: actions/checkout@v6
with:
repository: InstaNode-dev/proto
path: proto
continue-on-error: true
- uses: actions/setup-go@v6
with:
go-version-file: provisioner/go.mod
# Drop the `|| true` from the previous revision — it masked real test
# failures and let coverage drift unnoticed (CLAUDE.md rule 12: shipped
# ≠ verified). A red test must fail the job; codecov upload below is
# still soft-failed via fail_ci_if_error: false so a missing token
# doesn't take CI down.
# MinIO can't run as a `services:` container (no way to pass `server /data`).
# Start it here so TestServer_Storage_GetStorageBytes_LiveRoundTrip has a
# real S3-compatible endpoint at 127.0.0.1:9100. Env (MINIO_ROOT_USER/
# PASSWORD) are static literals — no untrusted workflow input is interpolated.
- name: Start MinIO for storage round-trip
run: |
docker run -d --name itest-minio \
-p 9100:9000 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio:latest server /data
# Wait for MinIO to report healthy before the test run.
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:9100/minio/health/live >/dev/null 2>&1; then
echo "minio healthy after ${i} tries"; break
fi
sleep 1
done
curl -fsS http://127.0.0.1:9100/minio/health/live >/dev/null
# NATS can't be a `services:` container (minimal nats:2 image has no
# wget/curl for a passing --health-cmd). Start it here with explicit
# -js (JetStream) + -m 8222 (HTTP monitor) and wait on /healthz from the
# runner, which DOES have curl. Reached by the queue round-trip at
# 127.0.0.1:4222 / :8222 (TEST_NATS_HOST=127.0.0.1).
- name: Start NATS for queue round-trip
run: |
docker run -d --name itest-nats \
-p 4222:4222 \
-p 8222:8222 \
nats:2 -js -m 8222
# Wait for the NATS HTTP monitor to report healthy before the tests.
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8222/healthz >/dev/null 2>&1; then
echo "nats healthy after ${i} tries"; break
fi
sleep 1
done
curl -fsS http://127.0.0.1:8222/healthz >/dev/null
- name: Generate coverage
working-directory: provisioner
# No `-short`: the integration tests are gated on TEST_* env-var
# reachability (set above), NOT on testing.Short(). `-p 1` serialises
# package execution so the many DB-touching packages don't open
# connections concurrently against the shared service containers.
run: go test ./... -p 1 -coverprofile=coverage.out -covermode=atomic
- uses: codecov/codecov-action@v7
with:
files: provisioner/coverage.out
flags: provisioner
fail_ci_if_error: false
# ------------------------------------------------------------------
# Org patch-coverage mandate: every changed line in a PR diff must be
# covered by a test (100%), and the project floor stays >=95%.
# Tool: diff-cover (https://github.com/Bachmann1234/diff-cover).
# ------------------------------------------------------------------
- uses: actions/setup-python@v6
if: github.event_name == 'pull_request'
with:
python-version: '3.12'
- name: Install diff-cover + cobertura converter
if: github.event_name == 'pull_request'
run: |
pip install diff-cover
go install github.com/boumenot/gocover-cobertura@latest
- name: Convert coverage to Cobertura
if: github.event_name == 'pull_request'
working-directory: provisioner
run: $(go env GOPATH)/bin/gocover-cobertura < coverage.out > coverage.xml
- name: Patch coverage gate (100% of changed lines)
if: github.event_name == 'pull_request'
working-directory: provisioner
run: |
# Full base history (NOT --depth=1): a shallow base fetch shares no merge-base
# with a PR branch that is behind base → diff-cover "no merge base" crash.
git fetch origin "${{ github.base_ref }}" || true
diff-cover coverage.xml \
--compare-branch="origin/${{ github.base_ref }}" \
--fail-under=100
- name: Project coverage floor (>=95% production code)
if: github.event_name == 'pull_request'
working-directory: provisioner
# The >=95% floor is measured over PRODUCTION code only. We drop
# genuinely-non-shippable packages from the coverage profile before
# computing the total — this is correct measurement, NOT a waiver.
# No internal/<domain> production package is ever excluded here.
#
# Excluded (and why):
# cmd/smoke-buildinfo — diagnostic/smoke binary, not shipped logic.
# cmd/* — pure diagnostic/smoke binaries.
# internal/testhelpers — test-DB/setup harness (none today; future-proof).
# e2e/ — black-box E2E suite (//go:build e2e; none today).
# proto/gen, *_pb.go — generated protobuf code.
# Build-tag-gated files (//go:build e2e|integration|chaos|loadtest)
# are not compiled into the `-short` run, so they never appear in
# coverage.out — the path filter below is belt-and-suspenders.
run: |
# Keep the `mode:` header line; drop excluded package paths.
grep -vE '(/internal/testhelpers/|/cmd/|/e2e/|/proto/gen/|_pb\.go:)' \
coverage.out > coverage.prod.out
total=$(go tool cover -func=coverage.prod.out | tail -1 | awk '{print $3}' | tr -d '%')
echo "Total project coverage: ${total}%"
awk -v t="$total" 'BEGIN { exit (t+0 >= 95) ? 0 : 1 }' \
|| { echo "::error::Production coverage ${total}% is below the 95% floor"; exit 1; }