Skip to content

Commit 22e4e90

Browse files
committed
Add unit tests (with help from claude)
1 parent 7ad1d02 commit 22e4e90

7 files changed

Lines changed: 675 additions & 0 deletions

File tree

blog/attr_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package blog
2+
3+
import (
4+
"errors"
5+
"log/slog"
6+
"net/netip"
7+
"testing"
8+
9+
"github.com/letsencrypt/boulder/identifier"
10+
)
11+
12+
func TestAttrHelpers(t *testing.T) {
13+
t.Parallel()
14+
15+
testCases := []struct {
16+
name string
17+
got slog.Attr
18+
wantKey string
19+
wantVal slog.Value
20+
}{
21+
{
22+
name: "Acct",
23+
got: Acct(42),
24+
wantKey: "acct",
25+
wantVal: slog.Int64Value(42),
26+
},
27+
{
28+
name: "Order",
29+
got: Order(17),
30+
wantKey: "order",
31+
wantVal: slog.Int64Value(17),
32+
},
33+
{
34+
name: "Authz",
35+
got: Authz(99),
36+
wantKey: "authz",
37+
wantVal: slog.Int64Value(99),
38+
},
39+
{
40+
name: "Serial",
41+
got: Serial("deadbeef"),
42+
wantKey: "serial",
43+
wantVal: slog.StringValue("deadbeef"),
44+
},
45+
{
46+
name: "Error",
47+
got: Error(errors.New("boom")),
48+
wantKey: "error",
49+
wantVal: slog.StringValue("boom"),
50+
},
51+
}
52+
53+
for _, tc := range testCases {
54+
t.Run(tc.name, func(t *testing.T) {
55+
t.Parallel()
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+
}
62+
})
63+
}
64+
}
65+
66+
func TestIdentsAttr(t *testing.T) {
67+
t.Parallel()
68+
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+
}
75+
76+
idents, ok := attr.Value.Any().([]identifier.ACMEIdentifier)
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+
}
89+
}

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: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package blog
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestLogLineChecksum(t *testing.T) {
9+
t.Parallel()
10+
11+
testCases := []struct {
12+
name string
13+
line string
14+
want string
15+
}{
16+
{
17+
name: "empty",
18+
line: "",
19+
// CRC32 of the empty string is 0.
20+
want: "AAAAAA",
21+
},
22+
{
23+
name: "simple",
24+
line: "hello, world",
25+
// Deterministic base64url(CRC32("hello, world"))
26+
want: "OnKr_w",
27+
},
28+
}
29+
30+
for _, tc := range testCases {
31+
t.Run(tc.name, func(t *testing.T) {
32+
t.Parallel()
33+
got := LogLineChecksum(tc.line)
34+
if got != tc.want {
35+
t.Errorf("LogLineChecksum(%q) = %q, want %q", tc.line, got, tc.want)
36+
}
37+
// Checksum should be deterministic across repeated calls.
38+
if again := LogLineChecksum(tc.line); again != got {
39+
t.Errorf("LogLineChecksum(%q) not deterministic: got %q then %q", tc.line, got, again)
40+
}
41+
})
42+
}
43+
44+
// Different inputs produce different checksums.
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+
})
116+
}

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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package blog
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestContextWith(t *testing.T) {
11+
t.Parallel()
12+
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+
}
44+
45+
for _, tc := range testCases {
46+
t.Run(tc.name, func(t *testing.T) {
47+
t.Parallel()
48+
49+
ctx := tc.ctxFn(t.Context())
50+
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+
}
60+
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+
}
74+
}

0 commit comments

Comments
 (0)