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