@@ -16,10 +16,14 @@ import (
16
16
"strings"
17
17
)
18
18
19
+ var (
20
+ schemaDirectory = flag .String ("schemas-dir" , "schemas" , "directory containing schema files" )
21
+ vectorsDirectories = flag .String ("vectors-dir" , "testvectors_v1,testvectors" , "comma separated directories containing vector files" )
22
+ vectorFilter = flag .String ("vector-filter" , "" , "only validate vector files matching the provided pattern" )
23
+ vectorRegex * regexp.Regexp
24
+ )
25
+
19
26
func main () {
20
- schemaDirectory := flag .String ("schemas-dir" , "schemas" , "directory containing schema files" )
21
- vectorsDirectories := flag .String ("vectors-dir" , "testvectors_v1,testvectors" , "comma separated directories containing vector files" )
22
- vectorFilter := flag .String ("vector-filter" , "" , "only validate vector files matching the provided pattern" )
23
27
24
28
flag .Parse ()
25
29
@@ -28,7 +32,6 @@ func main() {
28
32
log .Printf ("reading schemas from %q\n " , * schemaDirectory )
29
33
log .Printf ("reading vectors from %q\n " , vectorDirectoryParts )
30
34
31
- var vectorRegex * regexp.Regexp
32
35
if * vectorFilter != "" {
33
36
vectorRegex = regexp .MustCompile (* vectorFilter )
34
37
log .Printf ("filtering vectors with %q\n " , * vectorFilter )
@@ -41,94 +44,21 @@ func main() {
41
44
}
42
45
schemaCompiler .AssertFormat () // Opt in to format validation.
43
46
44
- var total , valid , invalid , noSchema , ignored int
45
- for _ , vectorDir := range vectorDirectoryParts {
46
- err := filepath .WalkDir (vectorDir , func (path string , d fs.DirEntry , err error ) error {
47
- if err != nil {
48
- return err
49
- }
50
-
51
- if d .IsDir () || ! strings .HasSuffix (d .Name (), ".json" ) {
52
- return nil
53
- }
47
+ var results schemaLintResults
54
48
55
- if vectorRegex != nil && ! vectorRegex .MatchString (d .Name ()) {
56
- return nil
57
- }
58
-
59
- vectorData , err := os .ReadFile (path )
60
- if err != nil {
61
- return fmt .Errorf ("failed to read %s: %w" , path , err )
62
- }
63
-
64
- total ++
65
-
66
- var vector struct {
67
- Schema string `json:"schema"`
68
- }
69
-
70
- if err := json .Unmarshal (vectorData , & vector ); err != nil {
71
- log .Printf ("❌ %q: invalid vector JSON data: %s\n " , path , err )
72
- invalid ++
73
- return nil
74
- }
75
-
76
- if vector .Schema == "" {
77
- log .Printf ("❌ %q: no schema specified\n " , path )
78
- noSchema ++
79
- return nil
80
- }
81
-
82
- if missingSchemas [vector .Schema ] {
83
- log .Printf ("⚠️ %q: ignoring missing schema %q\n " , path , vector .Schema )
84
- ignored ++
85
- return nil
86
- }
87
-
88
- schemaPath := filepath .Join (* schemaDirectory , vector .Schema )
89
- if _ , err := os .Stat (schemaPath ); os .IsNotExist (err ) {
90
- log .Printf ("❌ %q: referenced schema %q not found\n " , path , vector .Schema )
91
- invalid ++
92
- return nil
93
- }
94
-
95
- schema , err := schemaCompiler .Compile (schemaPath )
96
- if err != nil {
97
- log .Printf ("❌ %q: invalid schema %q: %s\n " , path , vector .Schema , err )
98
- invalid ++
99
- return nil
100
- }
101
-
102
- var instance any
103
- if err := json .Unmarshal (vectorData , & instance ); err != nil {
104
- log .Printf ("❌ %q: invalid vector JSON data: %s\n " , path , err )
105
- invalid ++
106
- return nil
107
- }
108
-
109
- if err := schema .Validate (instance ); err != nil {
110
- log .Printf ("❌ %q: vector doesn't validate with schema: %s\n " , path , err )
111
- invalid ++
112
- return nil
113
- }
114
-
115
- log .Printf ("✅ %q: validates with %q\n " , path , vector .Schema )
116
- valid ++
117
- return nil
118
- })
119
- if err != nil {
120
- fmt .Printf ("Error walking directory: %v\n " , err )
121
- os .Exit (1 )
49
+ for _ , vectorDir := range vectorDirectoryParts {
50
+ if err := lintVectorDir (schemaCompiler , & results , vectorDir ); err != nil {
51
+ log .Fatalf ("error linting schemas: %v\n " , err )
122
52
}
123
53
}
124
54
125
- log .Printf ("linted %d vector files\n " , total )
126
- log .Printf ("valid: %d\n " , valid )
127
- log .Printf ("invalid: %d\n " , invalid )
128
- log .Printf ("no schema: %d\n " , noSchema )
129
- log .Printf ("ignored: %d\n " , ignored )
55
+ log .Printf ("linted %d vector files\n " , results . total )
56
+ log .Printf ("valid: %d\n " , results . valid )
57
+ log .Printf ("invalid: %d\n " , results . invalid )
58
+ log .Printf ("no schema: %d\n " , results . noSchema )
59
+ log .Printf ("ignored: %d\n " , results . ignored )
130
60
131
- os .Exit (invalid )
61
+ os .Exit (results . invalid )
132
62
}
133
63
134
64
var (
@@ -232,3 +162,133 @@ func validateCurve(value any) error {
232
162
return fmt .Errorf ("invalid EcCurve: unknown curve name: %v" , value )
233
163
}
234
164
}
165
+
166
+ func lintVectorDir (schemaCompiler * jsonschema.Compiler , results * schemaLintResults , vectorDir string ) error {
167
+ err := filepath .WalkDir (vectorDir , func (path string , d fs.DirEntry , err error ) error {
168
+ if err != nil {
169
+ return err
170
+ }
171
+
172
+ if d .IsDir () || ! strings .HasSuffix (d .Name (), ".json" ) {
173
+ return nil
174
+ }
175
+
176
+ if vectorRegex != nil && ! vectorRegex .MatchString (d .Name ()) {
177
+ return nil
178
+ }
179
+
180
+ results .total ++
181
+
182
+ vectorData , err := os .ReadFile (path )
183
+ if err != nil {
184
+ return fmt .Errorf ("failed to read %s: %w" , path , err )
185
+ }
186
+
187
+ if err := lintVectorTestGroups (vectorData , path ); err != nil {
188
+ log .Printf ("❌ %q: %s\n " , path , err )
189
+ results .invalid ++
190
+ return nil
191
+ }
192
+
193
+ if err := lintVectorToSchema (schemaCompiler , vectorData , path , results ); err != nil {
194
+ return err
195
+ }
196
+
197
+ return nil
198
+ })
199
+ if err != nil {
200
+ return fmt .Errorf ("error walking directory: %w" , err )
201
+ }
202
+
203
+ return nil
204
+ }
205
+
206
+ func lintVectorTestGroups (vectorData []byte , path string ) error {
207
+ var vector struct {
208
+ NumberOfTests int `json:"numberOfTests"`
209
+ TestGroups []struct {
210
+ Tests []struct {
211
+ TcId int `json:"tcId"`
212
+ } `json:"tests"`
213
+ } `json:"testGroups"`
214
+ }
215
+ if err := json .Unmarshal (vectorData , & vector ); err != nil {
216
+ return fmt .Errorf ("error decoding vector JSON data for test groups: %w" , err )
217
+ }
218
+
219
+ // Within a vector file, test case IDs must be unique.
220
+ testCaseIds := make (map [int ]struct {})
221
+ for _ , tg := range vector .TestGroups {
222
+ for _ , test := range tg .Tests {
223
+ if _ , ok := testCaseIds [test .TcId ]; ok {
224
+ return fmt .Errorf ("vector %q has duplicate tcId %d" , path , test .TcId )
225
+ }
226
+ testCaseIds [test .TcId ] = struct {}{}
227
+ }
228
+ }
229
+
230
+ if testCount := len (testCaseIds ); testCount != vector .NumberOfTests {
231
+ return fmt .Errorf ("vector %q declared %d tests in group, had %d" , path , vector .NumberOfTests , testCount )
232
+ }
233
+
234
+ return nil
235
+ }
236
+
237
+ func lintVectorToSchema (schemaCompiler * jsonschema.Compiler , vectorData []byte , path string , results * schemaLintResults ) error {
238
+ var vector struct {
239
+ Schema string `json:"schema"`
240
+ }
241
+
242
+ if err := json .Unmarshal (vectorData , & vector ); err != nil {
243
+ log .Printf ("❌ %q: invalid vector JSON data: %s\n " , path , err )
244
+ results .invalid ++
245
+ return nil
246
+ }
247
+
248
+ if vector .Schema == "" {
249
+ log .Printf ("❌ %q: no schema specified\n " , path )
250
+ results .noSchema ++
251
+ return nil
252
+ }
253
+
254
+ if missingSchemas [vector .Schema ] {
255
+ log .Printf ("⚠️ %q: ignoring missing schema %q\n " , path , vector .Schema )
256
+ results .ignored ++
257
+ return nil
258
+ }
259
+
260
+ schemaPath := filepath .Join (* schemaDirectory , vector .Schema )
261
+ if _ , err := os .Stat (schemaPath ); os .IsNotExist (err ) {
262
+ log .Printf ("❌ %q: referenced schema %q not found\n " , path , vector .Schema )
263
+ results .invalid ++
264
+ return nil
265
+ }
266
+
267
+ schema , err := schemaCompiler .Compile (schemaPath )
268
+ if err != nil {
269
+ log .Printf ("❌ %q: invalid schema %q: %s\n " , path , vector .Schema , err )
270
+ results .invalid ++
271
+ return nil
272
+ }
273
+
274
+ var instance any
275
+ if err := json .Unmarshal (vectorData , & instance ); err != nil {
276
+ log .Printf ("❌ %q: invalid vector JSON data: %s\n " , path , err )
277
+ results .invalid ++
278
+ return nil
279
+ }
280
+
281
+ if err := schema .Validate (instance ); err != nil {
282
+ log .Printf ("❌ %q: vector doesn't validate with schema: %s\n " , path , err )
283
+ results .invalid ++
284
+ return nil
285
+ }
286
+
287
+ log .Printf ("✅ %q: validates with %q\n " , path , vector .Schema )
288
+ results .valid ++
289
+ return nil
290
+ }
291
+
292
+ type schemaLintResults struct {
293
+ total , valid , invalid , noSchema , ignored int
294
+ }
0 commit comments