@@ -237,3 +237,116 @@ def test_oneof_required(self):
237
237
validator = OAS30Validator (schema , format_checker = oas30_format_checker )
238
238
result = validator .validate (instance )
239
239
assert result is None
240
+
241
+ @pytest .mark .parametrize ('schema_type' , [
242
+ 'oneOf' , 'anyOf' , 'allOf' ,
243
+ ])
244
+ def test_oneof_discriminator (self , schema_type ):
245
+ # We define a few components schemas
246
+ components = {
247
+ "MountainHiking" : {
248
+ "type" : "object" ,
249
+ "properties" : {
250
+ "discipline" : {
251
+ "type" : "string" ,
252
+ # we allow both the explicitely matched mountain_hiking discipline
253
+ # and the implicitely matched MoutainHiking discipline
254
+ "enum" : ["mountain_hiking" , "MountainHiking" ]
255
+ },
256
+ "length" : {
257
+ "type" : "integer" ,
258
+ }
259
+ },
260
+ "required" : ["discipline" , "length" ]
261
+ },
262
+ "AlpineClimbing" : {
263
+ "type" : "object" ,
264
+ "properties" : {
265
+ "discipline" : {
266
+ "type" : "string" ,
267
+ "enum" : ["alpine_climbing" ]
268
+ },
269
+ "height" : {
270
+ "type" : "integer" ,
271
+ },
272
+ },
273
+ "required" : ["discipline" , "height" ]
274
+ },
275
+ "Route" : {
276
+ # defined later
277
+ }
278
+ }
279
+ components ['Route' ][schema_type ] = [
280
+ {"$ref" : "#/components/schemas/MountainHiking" },
281
+ {"$ref" : "#/components/schemas/AlpineClimbing" },
282
+ ]
283
+
284
+ # Add the compoments in a minimalis schema
285
+ schema = {
286
+ "$ref" : "#/components/schemas/Route" ,
287
+ "components" : {
288
+ "schemas" : components
289
+ }
290
+ }
291
+
292
+ if schema_type != 'allOf' :
293
+ # use jsonschema validator when no discriminator is defined
294
+ validator = OAS30Validator (schema , format_checker = oas30_format_checker )
295
+ with pytest .raises (ValidationError , match = "is not valid under any of the given schemas" ):
296
+ validator .validate ({
297
+ "something" : "matching_none_of_the_schemas"
298
+ })
299
+ assert False
300
+
301
+ if schema_type == 'anyOf' :
302
+ # use jsonschema validator when no discriminator is defined
303
+ validator = OAS30Validator (schema , format_checker = oas30_format_checker )
304
+ with pytest .raises (ValidationError , match = "is not valid under any of the given schemas" ):
305
+ validator .validate ({
306
+ "something" : "matching_none_of_the_schemas"
307
+ })
308
+ assert False
309
+
310
+ discriminator = {
311
+ "propertyName" : "discipline" ,
312
+ "mapping" : {
313
+ "mountain_hiking" : "#/components/schemas/MountainHiking" ,
314
+ "alpine_climbing" : "#/components/schemas/AlpineClimbing" ,
315
+ }
316
+ }
317
+ schema ['components' ]['schemas' ]['Route' ]['discriminator' ] = discriminator
318
+
319
+ # Optional: check we return useful result when the schema is wrong
320
+ validator = OAS30Validator (schema , format_checker = oas30_format_checker )
321
+ with pytest .raises (ValidationError , match = "does not contain discriminating property" ):
322
+ validator .validate ({
323
+ "something" : "missing"
324
+ })
325
+ assert False
326
+
327
+ # Check we get a non-generic, somehow usable, error message when a discriminated schema is failing
328
+ with pytest .raises (ValidationError , match = "'bad_string' is not of type integer" ):
329
+ validator .validate ({
330
+ "discipline" : "mountain_hiking" ,
331
+ "length" : "bad_string"
332
+ })
333
+ assert False
334
+
335
+ # Check explicit MountainHiking resolution
336
+ validator .validate ({
337
+ "discipline" : "mountain_hiking" ,
338
+ "length" : 10
339
+ })
340
+
341
+ # Check implicit MountainHiking resolution
342
+ validator .validate ({
343
+ "discipline" : "MountainHiking" ,
344
+ "length" : 10
345
+ })
346
+
347
+ # Check non resolvable implicit schema
348
+ with pytest .raises (ValidationError , match = "reference '#/components/schemas/other' could not be resolved" ):
349
+ result = validator .validate ({
350
+ "discipline" : "other"
351
+ })
352
+ assert False
0 commit comments