schema/v1alpha/annotation_schema_test.go
1
package schema_test
3
import (
4
_ "embed"
5
"encoding/json"
6
"os"
7
"path/filepath"
8
"testing"
10
"github.com/google/jsonschema-go/jsonschema"
11
yaml "go.yaml.in/yaml/v3"
12
)
14
//go:embed annotation.schema.json
15
var annotationSchema []byte
17
const validRecord = `{
18
"apiVersion": "koment.dev/v1alpha",
19
"kind": "Annotation",
20
"metadata": {
21
"id": "01JQ8ZK3M4N5P6R7S8T9V0W1X2",
22
"created": "2026-08-03T09:15:00Z"
23
},
24
"spec": {
25
"target": {
26
"file": "internal/session/token.go"
27
},
28
"type": "invariant",
29
"body": "Keep the prior key through the rotation window.",
30
"anchor": {
31
"scope": "excerpt",
32
"excerpt": "if token.Expired(now) {"
33
},
34
"author": {
35
"name": "Fixture Author",
36
"kind": "human",
37
"source": "explicit"
38
}
39
},
40
"status": {
41
"lastSeenLine": 42
42
}
43
}`
45
func resolveAnnotationSchema(t *testing.T) *jsonschema.Resolved {
46
t.Helper()
47
var schema jsonschema.Schema
48
if err := json.Unmarshal(annotationSchema, &schema); err != nil {
49
t.Fatalf("decode schema: %v", err)
50
}
51
resolved, err := schema.Resolve(nil)
52
if err != nil {
53
t.Fatalf("resolve schema: %v", err)
54
}
55
return resolved
56
}
58
func decodeRecord(t *testing.T, record string) map[string]any {
59
t.Helper()
60
var decoded map[string]any
61
if err := json.Unmarshal([]byte(record), &decoded); err != nil {
62
t.Fatalf("decode record: %v", err)
63
}
64
return decoded
65
}
67
func specOf(record map[string]any) map[string]any {
68
spec, _ := record["spec"].(map[string]any)
69
return spec
70
}
72
func TestAnnotationSchemaAcceptsTheCurrentShape(t *testing.T) {
73
if err := resolveAnnotationSchema(t).Validate(decodeRecord(t, validRecord)); err != nil {
74
t.Fatalf("validate record: %v", err)
75
}
76
}
78
func TestAnnotationSchemaAcceptsAPersistedObservation(t *testing.T) {
79
record := decodeRecord(t, validRecord)
80
record["status"] = map[string]any{
81
"lastSeenLine": float64(42),
82
"resolution": "ok",
83
"resolvedAt": "2026-08-05T11:00:00Z",
84
"resolvedCommit": "0123456789abcdef0123456789abcdef01234567",
85
}
86
if err := resolveAnnotationSchema(t).Validate(record); err != nil {
87
t.Fatalf("validate observation: %v", err)
88
}
89
}
91
func TestAnnotationSchemaAcceptsAFileScopedRecordWithNoObservedLine(t *testing.T) {
92
record := decodeRecord(t, validRecord)
93
specOf(record)["anchor"] = map[string]any{"scope": "file"}
94
record["status"] = map[string]any{
95
"resolution": "ok",
96
"resolvedAt": "2026-08-05T11:00:00Z",
97
"resolvedCommit": "0123456789abcdef0123456789abcdef01234567",
98
}
99
if err := resolveAnnotationSchema(t).Validate(record); err != nil {
100
t.Fatalf("validate file-scoped record: %v", err)
101
}
103
delete(record, "status")
104
if err := resolveAnnotationSchema(t).Validate(record); err != nil {
105
t.Fatalf("validate file-scoped record with no status at all: %v", err)
106
}
107
}
109
func TestEveryCommittedAnnotationMatchesThePublishedSchema(t *testing.T) {
110
resolved := resolveAnnotationSchema(t)
111
patterns := []string{
112
filepath.Join("..", "..", ".koment", "annotations", "*.yaml"),
113
filepath.Join("..", "..", "workspace", ".koment", "annotations", "*.yaml"),
114
}
115
total := 0
116
for _, pattern := range patterns {
117
paths, err := filepath.Glob(pattern)
118
if err != nil {
119
t.Fatal(err)
120
}
121
for _, path := range paths {
122
total++
123
content, err := os.ReadFile(path)
124
if err != nil {
125
t.Fatal(err)
126
}
127
var yamlRecord any
128
if err := yaml.Unmarshal(content, &yamlRecord); err != nil {
129
t.Fatalf("decode %s: %v", path, err)
130
}
131
encoded, err := json.Marshal(yamlRecord)
132
if err != nil {
133
t.Fatalf("convert %s to JSON: %v", path, err)
134
}
135
var record map[string]any
136
if err := json.Unmarshal(encoded, &record); err != nil {
137
t.Fatalf("decode converted %s: %v", path, err)
138
}
139
if err := resolved.Validate(record); err != nil {
140
t.Errorf("%s: %v", path, err)
141
}
142
}
143
}
144
if total == 0 {
145
t.Fatal("no committed annotations were validated")
146
}
147
}
149
func TestAnnotationSchemaAcceptsInlineCommentAcknowledgement(t *testing.T) {
150
record := decodeRecord(t, validRecord)
151
spec := specOf(record)
152
spec["type"] = "why"
153
spec["policy"] = map[string]any{
154
"exception": "inline-comment",
155
"acknowledged": true,
156
}
157
if err := resolveAnnotationSchema(t).Validate(record); err != nil {
158
t.Fatalf("validate acknowledgement: %v", err)
159
}
160
}
162
func TestAnnotationSchemaRejectsUnsupportedRecords(t *testing.T) {
163
resolved := resolveAnnotationSchema(t)
164
for _, test := range []struct {
165
name string
166
mutate func(map[string]any)
167
}{
168
{"the retired version field", func(record map[string]any) { record["version"] = float64(1) }},
169
{"a later api version", func(record map[string]any) { record["apiVersion"] = "koment.dev/v1" }},
170
{"another resource kind", func(record map[string]any) { record["kind"] = "Rationale" }},
171
{"unknown field", func(record map[string]any) { record["legacy"] = true }},
172
{"unknown spec field", func(record map[string]any) { specOf(record)["confidence"] = "high" }},
173
{"a bare file outside target", func(record map[string]any) {
174
specOf(record)["file"] = "internal/session/token.go"
175
}},
176
{"a date instead of an instant", func(record map[string]any) {
177
record["metadata"] = map[string]any{"id": "01JQ8ZK3M4N5P6R7S8T9V0W1X2", "created": "2026-08-03"}
178
}},
179
{"absolute path", func(record map[string]any) {
180
specOf(record)["target"] = map[string]any{"file": "/etc/passwd"}
181
}},
182
{"noncanonical path", func(record map[string]any) {
183
specOf(record)["target"] = map[string]any{"file": "./main.go"}
184
}},
185
{"backslash path", func(record map[string]any) {
186
specOf(record)["target"] = map[string]any{"file": `internal\main.go`}
187
}},
188
{"drive-relative path", func(record map[string]any) {
189
specOf(record)["target"] = map[string]any{"file": "C:main.go"}
190
}},
191
{"an excerpt anchor with no observed line", func(record map[string]any) {
192
delete(record, "status")
193
}},
194
{"a line on a file anchor", func(record map[string]any) {
195
specOf(record)["anchor"] = map[string]any{"scope": "file"}
196
}},
197
{"a resolution with no time", func(record map[string]any) {
198
record["status"] = map[string]any{"lastSeenLine": float64(42), "resolution": "ok"}
199
}},
200
{"a retired resolution", func(record map[string]any) {
201
record["status"] = map[string]any{
202
"lastSeenLine": float64(42), "resolution": "moved", "resolvedAt": "2026-08-05T11:00:00Z",
203
}
204
}},
205
{"an abbreviated resolved commit", func(record map[string]any) {
206
record["status"] = map[string]any{
207
"lastSeenLine": float64(42), "resolution": "ok",
208
"resolvedAt": "2026-08-05T11:00:00Z", "resolvedCommit": "0123456",
209
}
210
}},
211
{"human migration source", func(record map[string]any) {
212
specOf(record)["author"] = map[string]any{
213
"name": "Test Human",
214
"kind": "human",
215
"source": "migration",
216
}
217
}},
218
{"unacknowledged exception", func(record map[string]any) {
219
specOf(record)["policy"] = map[string]any{
220
"exception": "inline-comment",
221
"acknowledged": false,
222
}
223
}},
224
{"non-why exception", func(record map[string]any) {
225
spec := specOf(record)
226
spec["type"] = "gotcha"
227
spec["policy"] = map[string]any{
228
"exception": "inline-comment",
229
"acknowledged": true,
230
}
231
}},
232
{"file-wide exception", func(record map[string]any) {
233
spec := specOf(record)
234
spec["type"] = "why"
235
spec["anchor"] = map[string]any{"scope": "file"}
236
spec["policy"] = map[string]any{
237
"exception": "inline-comment",
238
"acknowledged": true,
239
}
240
delete(record, "status")
241
}},
242
} {
243
t.Run(test.name, func(t *testing.T) {
244
record := decodeRecord(t, validRecord)
245
test.mutate(record)
246
if err := resolved.Validate(record); err == nil {
247
t.Fatal("invalid record was accepted")
248
}
249
})
250
}
251
}