Skip to content

Commit dbe9569

Browse files
test(coverage): worker logsafe/migrations/root ≥95%
Drive three coverage gaps to ≥95% (all hit 100%) via test seams, no waivers: - internal/logsafe (88.9% → 100%): cover itoa's n==0 and defensive n<0 branches with a direct unit test. - internal/migrations (93.9% → 100%): cover NewReader's ttl<=0 default-clamp branch and queryState's COUNT-query error path with sqlmock. - root package main.go (0% → 100%): introduce a run(ctx, deps) seam plus realMain/main wrapper indirection. Extract setupLogger, resolvePlansPath, loadPlanRegistry, newHealthzHandler, buildMux, serveLiveness/ startLivenessServer/shutdownLivenessServer, awaitShutdown, setupTelemetry/ telemetryCleanup, connectProvisioner, deployK8sInitOK, prodStartWorkers, newSignalContext. Inject infra constructors (deps struct + package vars osExit/signalCtxFn/realMainFn/newDeployK8sClients) so the full boot/shutdown path is exercised with sqlmock + miniredis + a fake workerSet and a cancelled context — no real Postgres/Redis/gRPC/River. - cmd/smoke-buildinfo (0% → 50%): extract render(io.Writer) seam + test; main() is the irreducible one-line wrapper. Full suite `go test ./...` builds; target packages pass under -race; go build ./... && go vet ./... clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 182ef89 commit dbe9569

6 files changed

Lines changed: 1274 additions & 129 deletions

File tree

cmd/smoke-buildinfo/main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ package main
77

88
import (
99
"fmt"
10+
"io"
11+
"os"
1012

1113
"instant.dev/common/buildinfo"
1214
)
1315

14-
func main() {
15-
fmt.Printf("GitSHA=%s BuildTime=%s Version=%s\n",
16+
// render writes the buildinfo smoke line to w. Extracted from main() so the
17+
// output shape is unit-testable without spawning the binary.
18+
func render(w io.Writer) {
19+
fmt.Fprintf(w, "GitSHA=%s BuildTime=%s Version=%s\n",
1620
buildinfo.GitSHA, buildinfo.BuildTime, buildinfo.Version)
1721
}
22+
23+
func main() {
24+
render(os.Stdout)
25+
}

cmd/smoke-buildinfo/main_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"strings"
6+
"testing"
7+
8+
"instant.dev/common/buildinfo"
9+
)
10+
11+
// TestRender pins the smoke-buildinfo output shape: a single line carrying
12+
// the three linked-in buildinfo fields. The format is what `make
13+
// smoke-buildinfo` greps to confirm the -ldflags -X override landed.
14+
func TestRender(t *testing.T) {
15+
var buf bytes.Buffer
16+
render(&buf)
17+
18+
out := buf.String()
19+
if !strings.HasPrefix(out, "GitSHA=") {
20+
t.Fatalf("output must start with GitSHA=; got %q", out)
21+
}
22+
if !strings.HasSuffix(out, "\n") {
23+
t.Errorf("output must end with newline; got %q", out)
24+
}
25+
for _, want := range []string{
26+
"GitSHA=" + buildinfo.GitSHA,
27+
"BuildTime=" + buildinfo.BuildTime,
28+
"Version=" + buildinfo.Version,
29+
} {
30+
if !strings.Contains(out, want) {
31+
t.Errorf("output %q missing %q", out, want)
32+
}
33+
}
34+
}

internal/logsafe/logsafe_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@ func TestToken_BasicShapes(t *testing.T) {
3737
}
3838
}
3939

40+
// TestItoa covers the internal base-10 itoa helper directly, including
41+
// the n==0 and (defensive) n<0 branches that Token() can never reach via
42+
// a real len() argument. Pinning these keeps the helper safe to reuse.
43+
func TestItoa(t *testing.T) {
44+
cases := []struct {
45+
in int
46+
want string
47+
}{
48+
{0, "0"},
49+
{1, "1"},
50+
{7, "7"},
51+
{42, "42"},
52+
{1000, "1000"},
53+
// Defensive negative path — len() can't produce this, but the
54+
// helper must not crash and must render a leading minus sign.
55+
{-1, "-1"},
56+
{-42, "-42"},
57+
}
58+
for _, c := range cases {
59+
if got := itoa(c.in); got != c.want {
60+
t.Errorf("itoa(%d) = %q; want %q", c.in, got, c.want)
61+
}
62+
}
63+
}
64+
4065
// TestToken_NoLeakBeyondPrefix is the substantive regression guard:
4166
// for any token longer than 8 chars, the redacted output must NOT
4267
// contain any character from position [8:] of the original. Catches

internal/migrations/state_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,65 @@ func TestReader_NilDB(t *testing.T) {
9999
}
100100
}
101101

102+
// TestNewReader_Defaults exercises both fallback branches: ttl <= 0 must
103+
// clamp to defaultTTL, and a nil clock must default to time.Now. We can't
104+
// read the unexported fields from outside, so we verify behaviourally:
105+
// with the defaulted clock + TTL, a fresh Get refreshes from the DB and a
106+
// second immediate Get serves from cache (proving the TTL is the positive
107+
// defaultTTL, not the passed-in 0).
108+
func TestNewReader_Defaults(t *testing.T) {
109+
db, mock, err := sqlmock.New()
110+
if err != nil {
111+
t.Fatalf("sqlmock.New: %v", err)
112+
}
113+
defer db.Close()
114+
115+
// Exactly one query pair — the second Get must hit the cache, which
116+
// only happens if ttl was clamped to a positive defaultTTL (not 0).
117+
mock.ExpectQuery(`SELECT filename FROM schema_migrations`).
118+
WillReturnRows(sqlmock.NewRows([]string{"filename"}).AddRow("001_initial.sql"))
119+
mock.ExpectQuery(`SELECT COUNT\(\*\)`).
120+
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
121+
122+
// ttl=0 -> defaultTTL; clock=nil -> time.Now.
123+
r := NewReader(db, 0, nil)
124+
a := r.Get(context.Background())
125+
b := r.Get(context.Background())
126+
if a != b || a.Status != StatusOK || a.Count != 1 {
127+
t.Fatalf("expected cached StatusOK after defaulting: %+v vs %+v", a, b)
128+
}
129+
if err := mock.ExpectationsWereMet(); err != nil {
130+
t.Fatalf("unmet: %v", err)
131+
}
132+
}
133+
134+
// TestQueryState_CountError covers the COUNT query failure path: the
135+
// filename query succeeds but the count query errors, so queryState must
136+
// return StatusUnknown + the error.
137+
func TestQueryState_CountError(t *testing.T) {
138+
db, mock, err := sqlmock.New()
139+
if err != nil {
140+
t.Fatalf("sqlmock.New: %v", err)
141+
}
142+
defer db.Close()
143+
144+
mock.ExpectQuery(`SELECT filename FROM schema_migrations`).
145+
WillReturnRows(sqlmock.NewRows([]string{"filename"}).AddRow("042_x.sql"))
146+
mock.ExpectQuery(`SELECT COUNT\(\*\)`).
147+
WillReturnError(errors.New("count query failed"))
148+
149+
s, err := queryState(context.Background(), db)
150+
if err == nil {
151+
t.Fatalf("expected error from count query")
152+
}
153+
if s.Status != StatusUnknown {
154+
t.Fatalf("Status: got %q want %q", s.Status, StatusUnknown)
155+
}
156+
if err := mock.ExpectationsWereMet(); err != nil {
157+
t.Fatalf("unmet: %v", err)
158+
}
159+
}
160+
102161
// TestQueryState_NoRows surfaces StatusOK with empty filename. A fresh DB
103162
// where schema_migrations exists but is empty (boot-time race) is valid.
104163
func TestQueryState_NoRows(t *testing.T) {

0 commit comments

Comments
 (0)