Skip to content

Commit bfa6118

Browse files
committed
Add unit tests (with help from claude)
1 parent d159c1b commit bfa6118

7 files changed

Lines changed: 383 additions & 117 deletions

File tree

blog/attr_test.go

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ package blog
33
import (
44
"errors"
55
"log/slog"
6+
"net/netip"
67
"testing"
78

89
"github.com/letsencrypt/boulder/identifier"
9-
"github.com/letsencrypt/boulder/test"
1010
)
1111

1212
func TestAttrHelpers(t *testing.T) {
@@ -53,23 +53,37 @@ func TestAttrHelpers(t *testing.T) {
5353
for _, tc := range testCases {
5454
t.Run(tc.name, func(t *testing.T) {
5555
t.Parallel()
56-
test.AssertEquals(t, tc.got.Key, tc.wantKey)
57-
test.Assert(t, tc.got.Value.Equal(tc.wantVal), "attr value mismatch")
56+
if tc.got.Key != tc.wantKey {
57+
t.Errorf("attr key = %q, want %q", tc.got.Key, tc.wantKey)
58+
}
59+
if !tc.got.Value.Equal(tc.wantVal) {
60+
t.Errorf("attr value = %v, want %v", tc.got.Value, tc.wantVal)
61+
}
5862
})
5963
}
6064
}
6165

6266
func TestIdentsAttr(t *testing.T) {
6367
t.Parallel()
6468

65-
// Idents accepts a variadic list of identifiers and stores them under the
66-
// "idents" key.
67-
attr := Idents(identifier.NewDNS("example.com"), identifier.NewDNS("example.net"))
68-
test.AssertEquals(t, attr.Key, "idents")
69+
// This test is separate from the above because the Idents helper accepts
70+
// a variadic number of arguments.
71+
attr := Idents(identifier.NewDNS("example.com"), identifier.NewIP(netip.MustParseAddr("12.34.56.78")))
72+
if attr.Key != "idents" {
73+
t.Errorf("attr key = %q, want %q", attr.Key, "idents")
74+
}
6975

7076
idents, ok := attr.Value.Any().([]identifier.ACMEIdentifier)
71-
test.Assert(t, ok, "idents attr value should be a slice of ACMEIdentifier")
72-
test.AssertEquals(t, len(idents), 2)
73-
test.AssertEquals(t, idents[0].Value, "example.com")
74-
test.AssertEquals(t, idents[1].Value, "example.net")
77+
if !ok {
78+
t.Fatalf("idents attr value should be a slice of ACMEIdentifier, got %T", attr.Value.Any())
79+
}
80+
if len(idents) != 2 {
81+
t.Fatalf("got %d idents, want 2", len(idents))
82+
}
83+
if idents[0].Value != "example.com" {
84+
t.Errorf("idents[0].Value = %q, want %q", idents[0].Value, "example.com")
85+
}
86+
if idents[1].Value != "12.34.56.78" {
87+
t.Errorf("idents[1].Value = %q, want %q", idents[1].Value, "12.34.56.78")
88+
}
7589
}

blog/audit_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package blog
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestAuditWriter(t *testing.T) {
9+
t.Parallel()
10+
11+
testCases := []struct {
12+
name string
13+
in string
14+
want string
15+
}{
16+
{
17+
name: "empty",
18+
in: "",
19+
want: "[AUDIT] ",
20+
},
21+
{
22+
name: "simple",
23+
in: "hello, world",
24+
want: "[AUDIT] hello, world",
25+
},
26+
}
27+
28+
for _, tc := range testCases {
29+
t.Run(tc.name, func(t *testing.T) {
30+
t.Parallel()
31+
32+
var buf bytes.Buffer
33+
w := &auditWriter{inner: &buf}
34+
n, err := w.Write([]byte(tc.in))
35+
if err != nil {
36+
t.Fatalf("auditWriter.Write returned error: %s", err)
37+
}
38+
if n != len(tc.in) {
39+
t.Errorf("auditWriter.Write returned n=%d, want %d", n, len(tc.in))
40+
}
41+
got := buf.String()
42+
if got != tc.want {
43+
t.Errorf("auditWriter wrote %q, want %q", got, tc.want)
44+
}
45+
})
46+
}
47+
48+
// Each call to Write produces its own [AUDIT]-prefixed line.
49+
t.Run("multiple writes", func(t *testing.T) {
50+
t.Parallel()
51+
52+
var buf bytes.Buffer
53+
w := &auditWriter{inner: &buf}
54+
for _, line := range []string{"foo", "bar"} {
55+
n, err := w.Write([]byte(line))
56+
if err != nil {
57+
t.Fatalf("auditWriter.Write(%q) returned error: %s", line, err)
58+
}
59+
if n != len(line) {
60+
t.Errorf("auditWriter.Write(%q) returned n=%d, want %d", line, n, len(line))
61+
}
62+
}
63+
want := "[AUDIT] foo[AUDIT] bar"
64+
got := buf.String()
65+
if got != want {
66+
t.Errorf("auditWriter wrote %q, want %q", got, want)
67+
}
68+
})
69+
}

blog/checksum_test.go

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package blog
22

33
import (
4+
"bytes"
45
"testing"
5-
6-
"github.com/letsencrypt/boulder/test"
76
)
87

98
func TestLogLineChecksum(t *testing.T) {
@@ -23,21 +22,95 @@ func TestLogLineChecksum(t *testing.T) {
2322
{
2423
name: "simple",
2524
line: "hello, world",
26-
// Deterministic CRC32 of the given input, as base64-url-unpadded.
27-
want: LogLineChecksum("hello, world"),
25+
// Deterministic base64url(CRC32("hello, world"))
26+
want: "OnKr_w",
2827
},
2928
}
3029

3130
for _, tc := range testCases {
3231
t.Run(tc.name, func(t *testing.T) {
3332
t.Parallel()
3433
got := LogLineChecksum(tc.line)
35-
test.AssertEquals(t, got, tc.want)
34+
if got != tc.want {
35+
t.Errorf("LogLineChecksum(%q) = %q, want %q", tc.line, got, tc.want)
36+
}
3637
// Checksum should be deterministic across repeated calls.
37-
test.AssertEquals(t, got, LogLineChecksum(tc.line))
38+
if again := LogLineChecksum(tc.line); again != got {
39+
t.Errorf("LogLineChecksum(%q) not deterministic: got %q then %q", tc.line, got, again)
40+
}
3841
})
3942
}
4043

4144
// Different inputs produce different checksums.
42-
test.AssertNotEquals(t, LogLineChecksum("foo"), LogLineChecksum("bar"))
45+
if LogLineChecksum("foo") == LogLineChecksum("bar") {
46+
t.Errorf("LogLineChecksum(%q) and LogLineChecksum(%q) should differ", "foo", "bar")
47+
}
48+
}
49+
50+
func TestChecksumWriter(t *testing.T) {
51+
t.Parallel()
52+
53+
testCases := []struct {
54+
name string
55+
in string
56+
want string
57+
}{
58+
{
59+
name: "empty",
60+
in: "",
61+
want: "AAAAAA ",
62+
},
63+
{
64+
name: "simple",
65+
in: "hello, world",
66+
want: "OnKr_w hello, world",
67+
},
68+
{
69+
name: "complex",
70+
in: `level=INFO msg="he said \"hi\"\nthen" trail=actual
71+
`,
72+
want: `R3t8pA level=INFO msg="he said \"hi\"\nthen" trail=actual
73+
`,
74+
},
75+
}
76+
77+
for _, tc := range testCases {
78+
t.Run(tc.name, func(t *testing.T) {
79+
t.Parallel()
80+
81+
var buf bytes.Buffer
82+
w := newChecksumWriter(&buf)
83+
n, err := w.Write([]byte(tc.in))
84+
if err != nil {
85+
t.Fatalf("checksumWriter.Write returned error: %s", err)
86+
}
87+
if n != len(tc.in) {
88+
t.Errorf("checksumWriter.Write returned n=%d, want %d", n, len(tc.in))
89+
}
90+
if got := buf.String(); got != tc.want {
91+
t.Errorf("checksumWriter wrote %q, want %q", got, tc.want)
92+
}
93+
})
94+
}
95+
96+
// Each call to Write produces its own checksum-prefixed line.
97+
t.Run("multiple writes", func(t *testing.T) {
98+
t.Parallel()
99+
100+
var buf bytes.Buffer
101+
w := newChecksumWriter(&buf)
102+
for _, line := range []string{"foo", "bar"} {
103+
n, err := w.Write([]byte(line))
104+
if err != nil {
105+
t.Fatalf("checksumWriter.Write(%q) returned error: %s", line, err)
106+
}
107+
if n != len(line) {
108+
t.Errorf("checksumWriter.Write(%q) returned n=%d, want %d", line, n, len(line))
109+
}
110+
}
111+
want := LogLineChecksum("foo") + " foo" + LogLineChecksum("bar") + " bar"
112+
if got := buf.String(); got != want {
113+
t.Errorf("checksumWriter wrote %q, want %q", got, want)
114+
}
115+
})
43116
}

blog/config_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package blog
2+
3+
import (
4+
"log/slog"
5+
"testing"
6+
)
7+
8+
func TestConfigToSlogLevel(t *testing.T) {
9+
t.Parallel()
10+
11+
testCases := []struct {
12+
name string
13+
in int
14+
want slog.Level
15+
}{
16+
{name: "1 -> Error", in: 1, want: slog.LevelError},
17+
{name: "2 -> Error", in: 2, want: slog.LevelError},
18+
{name: "3 -> Error", in: 3, want: slog.LevelError},
19+
{name: "4 -> Warn", in: 4, want: slog.LevelWarn},
20+
{name: "5 -> Warn", in: 5, want: slog.LevelWarn},
21+
{name: "6 -> Info", in: 6, want: slog.LevelInfo},
22+
{name: "7 -> Debug", in: 7, want: slog.LevelDebug},
23+
// Unspecified values fall through to Info.
24+
{name: "0 -> Info (default)", in: 0, want: slog.LevelInfo},
25+
{name: "-1 -> Info (default)", in: -1, want: slog.LevelInfo},
26+
{name: "99 -> Info (default)", in: 99, want: slog.LevelInfo},
27+
}
28+
29+
for _, tc := range testCases {
30+
t.Run(tc.name, func(t *testing.T) {
31+
t.Parallel()
32+
got := configToSlogLevel(tc.in)
33+
if got != tc.want {
34+
t.Errorf("configToSlogLevel(%d) = %s, want %s", tc.in, got, tc.want)
35+
}
36+
})
37+
}
38+
}

blog/context_test.go

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,72 @@ package blog
33
import (
44
"context"
55
"log/slog"
6+
"strings"
67
"testing"
7-
8-
"github.com/letsencrypt/boulder/test"
98
)
109

11-
func TestContextWithNoAttrs(t *testing.T) {
12-
t.Parallel()
13-
14-
// A fresh context has no attrs attached; fromContext should return an empty slice.
15-
test.AssertEquals(t, len(fromContext(context.Background())), 0)
16-
}
17-
18-
func TestContextWithSingleLayer(t *testing.T) {
19-
t.Parallel()
20-
21-
ctx := ContextWith(context.Background(), slog.String("k1", "v1"), slog.Int("k2", 2))
22-
attrs := fromContext(ctx)
23-
test.AssertEquals(t, len(attrs), 2)
24-
test.AssertEquals(t, attrs[0].Key, "k1")
25-
test.AssertEquals(t, attrs[1].Key, "k2")
26-
}
27-
28-
func TestContextWithAccumulatesAcrossLayers(t *testing.T) {
10+
func TestContextWith(t *testing.T) {
2911
t.Parallel()
3012

31-
// ContextWith should append to any existing attrs, not replace them.
32-
ctx := ContextWith(context.Background(), slog.String("k1", "v1"))
33-
ctx = ContextWith(ctx, slog.String("k2", "v2"))
34-
ctx = ContextWith(ctx, slog.String("k3", "v3"))
13+
testCases := []struct {
14+
name string
15+
ctxFn func(context.Context) context.Context
16+
wantKeys []string
17+
wantLog []string
18+
}{
19+
{
20+
name: "empty",
21+
ctxFn: func(ctx context.Context) context.Context { return ctx },
22+
},
23+
{
24+
name: "single layer",
25+
ctxFn: func(ctx context.Context) context.Context {
26+
return ContextWith(ctx, slog.String("k1", "v1"), slog.Int("k2", 2))
27+
},
28+
wantKeys: []string{"k1", "k2"},
29+
wantLog: []string{"k1=v1", "k2=2"},
30+
},
31+
{
32+
name: "multi-layer",
33+
ctxFn: func(ctx context.Context) context.Context {
34+
// ContextWith should append to any existing attrs, not replace them.
35+
ctx = ContextWith(ctx, slog.String("k1", "v1"))
36+
ctx = ContextWith(ctx, slog.String("k2", "v2"))
37+
ctx = ContextWith(ctx, slog.String("k3", "v3"))
38+
return ctx
39+
},
40+
wantKeys: []string{"k1", "k2", "k3"},
41+
wantLog: []string{"k1=v1", "k2=v2", "k3=v3"},
42+
},
43+
}
3544

36-
attrs := fromContext(ctx)
37-
test.AssertEquals(t, len(attrs), 3)
38-
test.AssertEquals(t, attrs[0].Key, "k1")
39-
test.AssertEquals(t, attrs[1].Key, "k2")
40-
test.AssertEquals(t, attrs[2].Key, "k3")
41-
}
42-
43-
func TestContextWithAppliedByLogger(t *testing.T) {
44-
t.Parallel()
45+
for _, tc := range testCases {
46+
t.Run(tc.name, func(t *testing.T) {
47+
t.Parallel()
4548

46-
l := NewMock()
47-
ctx := ContextWith(context.Background(), slog.String("outer", "yes"))
48-
ctx = ContextWith(ctx, slog.String("inner", "also"))
49+
ctx := tc.ctxFn(t.Context())
4950

50-
l.Info(ctx, "hello")
51+
attrs := fromContext(ctx)
52+
if len(attrs) != len(tc.wantKeys) {
53+
t.Fatalf("fromContext returned %d attrs, want %d", len(attrs), len(tc.wantKeys))
54+
}
55+
for i, want := range tc.wantKeys {
56+
if attrs[i].Key != want {
57+
t.Errorf("attrs[%d].Key = %q, want %q", i, attrs[i].Key, want)
58+
}
59+
}
5160

52-
got := l.GetAll()
53-
test.AssertEquals(t, len(got), 1)
54-
test.AssertContains(t, got[0], "outer=yes")
55-
test.AssertContains(t, got[0], "inner=also")
61+
l := NewMock()
62+
l.Info(ctx, "hello")
63+
got := l.GetAll()
64+
if len(got) != 1 {
65+
t.Fatalf("got %d log lines, want 1: %v", len(got), got)
66+
}
67+
for _, want := range tc.wantLog {
68+
if !strings.Contains(got[0], want) {
69+
t.Errorf("log line %q does not contain %q", got[0], want)
70+
}
71+
}
72+
})
73+
}
5674
}

0 commit comments

Comments
 (0)