@@ -73,6 +73,7 @@ func TestRoundTrip(t *testing.T) {
73
73
t .Fatal (err )
74
74
}
75
75
j = convertNullTypeToNullable (j )
76
+ j = stripIntOrStringType (j )
76
77
openAPIJSON , err = json .Marshal (j )
77
78
if err != nil {
78
79
t .Fatal (err )
@@ -139,126 +140,225 @@ func convertNullTypeToNullable(x interface{}) interface{} {
139
140
}
140
141
}
141
142
142
- func TestValidateCustomResource (t * testing.T ) {
143
- type args struct {
144
- schema apiextensions.JSONSchemaProps
145
- object interface {}
143
+ func stripIntOrStringType (x interface {}) interface {} {
144
+ switch x := x .(type ) {
145
+ case map [string ]interface {}:
146
+ if t , found := x ["type" ]; found {
147
+ switch t := t .(type ) {
148
+ case []interface {}:
149
+ if len (t ) == 2 && t [0 ] == "integer" && t [1 ] == "string" && x ["x-kubernetes-int-or-string" ] == true {
150
+ delete (x , "type" )
151
+ }
152
+ }
153
+ }
154
+ for k := range x {
155
+ x [k ] = stripIntOrStringType (x [k ])
156
+ }
157
+ return x
158
+ case []interface {}:
159
+ for i := range x {
160
+ x [i ] = stripIntOrStringType (x [i ])
161
+ }
162
+ return x
163
+ default :
164
+ return x
146
165
}
166
+ }
167
+
168
+ func TestValidateCustomResource (t * testing.T ) {
147
169
tests := []struct {
148
- name string
149
- args args
150
- wantErr bool
170
+ name string
171
+ schema apiextensions.JSONSchemaProps
172
+ objects []interface {}
173
+ failingObjects []interface {}
151
174
}{
152
- // TODO: make more complete
153
- {"!nullable against non-null" , args {
154
- apiextensions.JSONSchemaProps {
175
+ {name : "!nullable" ,
176
+ schema : apiextensions.JSONSchemaProps {
155
177
Properties : map [string ]apiextensions.JSONSchemaProps {
156
178
"field" : {
157
179
Type : "object" ,
158
180
Nullable : false ,
159
181
},
160
182
},
161
183
},
162
- map [string ]interface {}{"field" : map [string ]interface {}{}},
163
- }, false },
164
- {"!nullable against null" , args {
165
- apiextensions.JSONSchemaProps {
166
- Properties : map [string ]apiextensions.JSONSchemaProps {
167
- "field" : {
168
- Type : "object" ,
169
- Nullable : false ,
170
- },
171
- },
184
+ objects : []interface {}{
185
+ map [string ]interface {}{},
186
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
172
187
},
173
- map [string ]interface {}{"field" : nil },
174
- }, true },
175
- {"!nullable against undefined" , args {
176
- apiextensions.JSONSchemaProps {
188
+ failingObjects : []interface {}{
189
+ map [string ]interface {}{"field" : "foo" },
190
+ map [string ]interface {}{"field" : 42 },
191
+ map [string ]interface {}{"field" : true },
192
+ map [string ]interface {}{"field" : 1.2 },
193
+ map [string ]interface {}{"field" : []interface {}{}},
194
+ },
195
+ },
196
+ {name : "nullable" ,
197
+ schema : apiextensions.JSONSchemaProps {
177
198
Properties : map [string ]apiextensions.JSONSchemaProps {
178
199
"field" : {
179
200
Type : "object" ,
180
- Nullable : false ,
201
+ Nullable : true ,
181
202
},
182
203
},
183
204
},
184
- map [string ]interface {}{},
185
- }, false },
186
- {"nullable against non-null" , args {
187
- apiextensions.JSONSchemaProps {
205
+ objects : []interface {}{
206
+ map [string ]interface {}{},
207
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
208
+ map [string ]interface {}{"field" : nil },
209
+ },
210
+ failingObjects : []interface {}{
211
+ map [string ]interface {}{"field" : "foo" },
212
+ map [string ]interface {}{"field" : 42 },
213
+ map [string ]interface {}{"field" : true },
214
+ map [string ]interface {}{"field" : 1.2 },
215
+ map [string ]interface {}{"field" : []interface {}{}},
216
+ },
217
+ },
218
+ {name : "nullable and no type" ,
219
+ schema : apiextensions.JSONSchemaProps {
188
220
Properties : map [string ]apiextensions.JSONSchemaProps {
189
221
"field" : {
190
- Type : "object" ,
191
222
Nullable : true ,
192
223
},
193
224
},
194
225
},
195
- map [string ]interface {}{"field" : map [string ]interface {}{}},
196
- }, false },
197
- {"nullable against null" , args {
198
- apiextensions.JSONSchemaProps {
226
+ objects : []interface {}{
227
+ map [string ]interface {}{},
228
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
229
+ map [string ]interface {}{"field" : nil },
230
+ map [string ]interface {}{"field" : "foo" },
231
+ map [string ]interface {}{"field" : 42 },
232
+ map [string ]interface {}{"field" : true },
233
+ map [string ]interface {}{"field" : 1.2 },
234
+ map [string ]interface {}{"field" : []interface {}{}},
235
+ },
236
+ },
237
+ {name : "x-kubernetes-int-or-string" ,
238
+ schema : apiextensions.JSONSchemaProps {
199
239
Properties : map [string ]apiextensions.JSONSchemaProps {
200
240
"field" : {
201
- Type : "object" ,
202
- Nullable : true ,
241
+ XIntOrString : true ,
203
242
},
204
243
},
205
244
},
206
- map [string ]interface {}{"field" : nil },
207
- }, false },
208
- {"!nullable against undefined" , args {
209
- apiextensions.JSONSchemaProps {
245
+ objects : []interface {}{
246
+ map [string ]interface {}{},
247
+ map [string ]interface {}{"field" : 42 },
248
+ map [string ]interface {}{"field" : "foo" },
249
+ },
250
+ failingObjects : []interface {}{
251
+ map [string ]interface {}{"field" : nil },
252
+ map [string ]interface {}{"field" : true },
253
+ map [string ]interface {}{"field" : 1.2 },
254
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
255
+ map [string ]interface {}{"field" : []interface {}{}},
256
+ },
257
+ },
258
+ {name : "nullable and x-kubernetes-int-or-string" ,
259
+ schema : apiextensions.JSONSchemaProps {
210
260
Properties : map [string ]apiextensions.JSONSchemaProps {
211
261
"field" : {
212
- Type : "object" ,
213
- Nullable : true ,
262
+ Nullable : true ,
263
+ XIntOrString : true ,
214
264
},
215
265
},
216
266
},
217
- map [string ]interface {}{},
218
- }, false },
219
- {"nullable and no type against non-nil" , args {
220
- apiextensions.JSONSchemaProps {
267
+ objects : []interface {}{
268
+ map [string ]interface {}{},
269
+ map [string ]interface {}{"field" : 42 },
270
+ map [string ]interface {}{"field" : "foo" },
271
+ map [string ]interface {}{"field" : nil },
272
+ },
273
+ failingObjects : []interface {}{
274
+ map [string ]interface {}{"field" : true },
275
+ map [string ]interface {}{"field" : 1.2 },
276
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
277
+ map [string ]interface {}{"field" : []interface {}{}},
278
+ },
279
+ },
280
+ {name : "nullable, x-kubernetes-int-or-string and user-provided anyOf" ,
281
+ schema : apiextensions.JSONSchemaProps {
221
282
Properties : map [string ]apiextensions.JSONSchemaProps {
222
283
"field" : {
223
- Nullable : true ,
284
+ Nullable : true ,
285
+ XIntOrString : true ,
286
+ AnyOf : []apiextensions.JSONSchemaProps {
287
+ {Type : "integer" },
288
+ {Type : "string" },
289
+ },
224
290
},
225
291
},
226
292
},
227
- map [string ]interface {}{"field" : 42 },
228
- }, false },
229
- {"nullable and no type against nil" , args {
230
- apiextensions.JSONSchemaProps {
293
+ objects : []interface {}{
294
+ map [string ]interface {}{},
295
+ map [string ]interface {}{"field" : nil },
296
+ map [string ]interface {}{"field" : 42 },
297
+ map [string ]interface {}{"field" : "foo" },
298
+ },
299
+ failingObjects : []interface {}{
300
+ map [string ]interface {}{"field" : true },
301
+ map [string ]interface {}{"field" : 1.2 },
302
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
303
+ map [string ]interface {}{"field" : []interface {}{}},
304
+ },
305
+ },
306
+ {name : "nullable, x-kubernetes-int-or-string and user-provider allOf" ,
307
+ schema : apiextensions.JSONSchemaProps {
231
308
Properties : map [string ]apiextensions.JSONSchemaProps {
232
309
"field" : {
233
- Nullable : true ,
310
+ Nullable : true ,
311
+ XIntOrString : true ,
312
+ AllOf : []apiextensions.JSONSchemaProps {
313
+ {
314
+ AnyOf : []apiextensions.JSONSchemaProps {
315
+ {Type : "integer" },
316
+ {Type : "string" },
317
+ },
318
+ },
319
+ },
234
320
},
235
321
},
236
322
},
237
- map [string ]interface {}{"field" : nil },
238
- }, false },
239
- {"invalid regex" , args {
240
- apiextensions.JSONSchemaProps {
323
+ objects : []interface {}{
324
+ map [string ]interface {}{},
325
+ map [string ]interface {}{"field" : nil },
326
+ map [string ]interface {}{"field" : 42 },
327
+ map [string ]interface {}{"field" : "foo" },
328
+ },
329
+ failingObjects : []interface {}{
330
+ map [string ]interface {}{"field" : true },
331
+ map [string ]interface {}{"field" : 1.2 },
332
+ map [string ]interface {}{"field" : map [string ]interface {}{}},
333
+ map [string ]interface {}{"field" : []interface {}{}},
334
+ },
335
+ },
336
+ {name : "invalid regex" ,
337
+ schema : apiextensions.JSONSchemaProps {
241
338
Properties : map [string ]apiextensions.JSONSchemaProps {
242
339
"field" : {
243
340
Type : "string" ,
244
341
Pattern : "+" ,
245
342
},
246
343
},
247
344
},
248
- map [string ]interface {}{"field" : "foo" },
249
- }, true },
345
+ failingObjects : [] interface {}{ map [string ]interface {}{"field" : "foo" } },
346
+ },
250
347
}
251
348
for _ , tt := range tests {
252
349
t .Run (tt .name , func (t * testing.T ) {
253
- validator , _ , err := NewSchemaValidator (& apiextensions.CustomResourceValidation {OpenAPIV3Schema : & tt .args . schema })
350
+ validator , _ , err := NewSchemaValidator (& apiextensions.CustomResourceValidation {OpenAPIV3Schema : & tt .schema })
254
351
if err != nil {
255
352
t .Fatal (err )
256
353
}
257
- if err := ValidateCustomResource (tt .args .object , validator ); (err != nil ) != tt .wantErr {
258
- if err == nil {
259
- t .Error ("expected error, but didn't get one" )
260
- } else {
261
- t .Errorf ("unexpected validation error: %v" , err )
354
+ for _ , obj := range tt .objects {
355
+ if err := ValidateCustomResource (obj , validator ); err != nil {
356
+ t .Errorf ("unexpected validation error for %v: %v" , obj , err )
357
+ }
358
+ }
359
+ for _ , obj := range tt .failingObjects {
360
+ if err := ValidateCustomResource (obj , validator ); err == nil {
361
+ t .Errorf ("missing error for %v" , obj )
262
362
}
263
363
}
264
364
})
0 commit comments