1import json 2 3import pytest 4 5from marshmallow.fields import Field, DateTime, Dict, String, Nested, List, TimeDelta 6from marshmallow import Schema 7 8from apispec import APISpec 9from apispec.ext.marshmallow import MarshmallowPlugin 10from apispec.ext.marshmallow import common 11from apispec.exceptions import APISpecError 12from .schemas import ( 13 PetSchema, 14 SampleSchema, 15 AnalysisSchema, 16 RunSchema, 17 SelfReferencingSchema, 18 OrderedSchema, 19 PatternedObjectSchema, 20 DefaultValuesSchema, 21 AnalysisWithListSchema, 22) 23 24from .utils import ( 25 get_schemas, 26 get_parameters, 27 get_responses, 28 get_headers, 29 get_paths, 30 build_ref, 31) 32 33 34class TestDefinitionHelper: 35 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 36 def test_can_use_schema_as_definition(self, spec, schema): 37 spec.components.schema("Pet", schema=schema) 38 definitions = get_schemas(spec) 39 props = definitions["Pet"]["properties"] 40 41 assert props["id"]["type"] == "integer" 42 assert props["name"]["type"] == "string" 43 44 def test_schema_helper_without_schema(self, spec): 45 spec.components.schema("Pet", {"properties": {"key": {"type": "integer"}}}) 46 definitions = get_schemas(spec) 47 assert definitions["Pet"]["properties"] == {"key": {"type": "integer"}} 48 49 @pytest.mark.parametrize("schema", [AnalysisSchema, AnalysisSchema()]) 50 def test_resolve_schema_dict_auto_reference(self, schema): 51 def resolver(schema): 52 schema_cls = common.resolve_schema_cls(schema) 53 return schema_cls.__name__ 54 55 spec = APISpec( 56 title="Test auto-reference", 57 version="0.1", 58 openapi_version="2.0", 59 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 60 ) 61 with pytest.raises(KeyError): 62 get_schemas(spec) 63 64 spec.components.schema("analysis", schema=schema) 65 spec.path( 66 "/test", 67 operations={ 68 "get": { 69 "responses": { 70 "200": {"schema": build_ref(spec, "schema", "analysis")} 71 } 72 } 73 }, 74 ) 75 definitions = get_schemas(spec) 76 assert 3 == len(definitions) 77 78 assert "analysis" in definitions 79 assert "SampleSchema" in definitions 80 assert "RunSchema" in definitions 81 82 @pytest.mark.parametrize( 83 "schema", [AnalysisWithListSchema, AnalysisWithListSchema()] 84 ) 85 def test_resolve_schema_dict_auto_reference_in_list(self, schema): 86 def resolver(schema): 87 schema_cls = common.resolve_schema_cls(schema) 88 return schema_cls.__name__ 89 90 spec = APISpec( 91 title="Test auto-reference", 92 version="0.1", 93 openapi_version="2.0", 94 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 95 ) 96 with pytest.raises(KeyError): 97 get_schemas(spec) 98 99 spec.components.schema("analysis", schema=schema) 100 spec.path( 101 "/test", 102 operations={ 103 "get": { 104 "responses": { 105 "200": {"schema": build_ref(spec, "schema", "analysis")} 106 } 107 } 108 }, 109 ) 110 definitions = get_schemas(spec) 111 assert 3 == len(definitions) 112 113 assert "analysis" in definitions 114 assert "SampleSchema" in definitions 115 assert "RunSchema" in definitions 116 117 @pytest.mark.parametrize("schema", [AnalysisSchema, AnalysisSchema()]) 118 def test_resolve_schema_dict_auto_reference_return_none(self, schema): 119 def resolver(schema): 120 return None 121 122 spec = APISpec( 123 title="Test auto-reference", 124 version="0.1", 125 openapi_version="2.0", 126 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 127 ) 128 with pytest.raises(KeyError): 129 get_schemas(spec) 130 131 with pytest.raises( 132 APISpecError, match="Name resolver returned None for schema" 133 ): 134 spec.components.schema("analysis", schema=schema) 135 136 @pytest.mark.parametrize("schema", [AnalysisSchema, AnalysisSchema()]) 137 def test_warning_when_schema_added_twice(self, spec, schema): 138 spec.components.schema("Analysis", schema=schema) 139 with pytest.warns(UserWarning, match="has already been added to the spec"): 140 spec.components.schema("DuplicateAnalysis", schema=schema) 141 142 def test_schema_instances_with_different_modifiers_added(self, spec): 143 class MultiModifierSchema(Schema): 144 pet_unmodified = Nested(PetSchema) 145 pet_exclude = Nested(PetSchema, exclude=("name",)) 146 147 spec.components.schema("Pet", schema=PetSchema()) 148 spec.components.schema("Pet_Exclude", schema=PetSchema(exclude=("name",))) 149 150 spec.components.schema("MultiModifierSchema", schema=MultiModifierSchema) 151 152 definitions = get_schemas(spec) 153 pet_unmodified_ref = definitions["MultiModifierSchema"]["properties"][ 154 "pet_unmodified" 155 ] 156 assert pet_unmodified_ref == build_ref(spec, "schema", "Pet") 157 158 pet_exclude = definitions["MultiModifierSchema"]["properties"]["pet_exclude"] 159 assert pet_exclude == build_ref(spec, "schema", "Pet_Exclude") 160 161 def test_schema_instance_with_different_modifers_custom_resolver(self): 162 class MultiModifierSchema(Schema): 163 pet_unmodified = Nested(PetSchema) 164 pet_exclude = Nested(PetSchema(partial=True)) 165 166 def resolver(schema): 167 schema_instance = common.resolve_schema_instance(schema) 168 prefix = "Partial-" if schema_instance.partial else "" 169 schema_cls = common.resolve_schema_cls(schema) 170 name = prefix + schema_cls.__name__ 171 if name.endswith("Schema"): 172 return name[:-6] or name 173 return name 174 175 spec = APISpec( 176 title="Test Custom Resolver for Partial", 177 version="0.1", 178 openapi_version="2.0", 179 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 180 ) 181 182 with pytest.warns(None) as record: 183 spec.components.schema("NameClashSchema", schema=MultiModifierSchema) 184 185 assert len(record) == 0 186 187 def test_schema_with_clashing_names(self, spec): 188 class Pet(PetSchema): 189 another_field = String() 190 191 class NameClashSchema(Schema): 192 pet_1 = Nested(PetSchema) 193 pet_2 = Nested(Pet) 194 195 with pytest.warns( 196 UserWarning, match="Multiple schemas resolved to the name Pet" 197 ): 198 spec.components.schema("NameClashSchema", schema=NameClashSchema) 199 200 definitions = get_schemas(spec) 201 202 assert "Pet" in definitions 203 assert "Pet1" in definitions 204 205 def test_resolve_nested_schema_many_true_resolver_return_none(self): 206 def resolver(schema): 207 return None 208 209 class PetFamilySchema(Schema): 210 pets_1 = Nested(PetSchema, many=True) 211 pets_2 = List(Nested(PetSchema)) 212 213 spec = APISpec( 214 title="Test auto-reference", 215 version="0.1", 216 openapi_version="2.0", 217 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 218 ) 219 220 spec.components.schema("PetFamily", schema=PetFamilySchema) 221 props = get_schemas(spec)["PetFamily"]["properties"] 222 pets_1 = props["pets_1"] 223 pets_2 = props["pets_2"] 224 assert pets_1["type"] == pets_2["type"] == "array" 225 226 227class TestComponentParameterHelper: 228 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 229 def test_can_use_schema_in_parameter(self, spec, schema): 230 param = {"schema": schema} 231 spec.components.parameter("Pet", "body", param) 232 parameter = get_parameters(spec)["Pet"] 233 assert parameter["in"] == "body" 234 reference = parameter["schema"] 235 assert reference == build_ref(spec, "schema", "Pet") 236 237 resolved_schema = spec.components.schemas["Pet"] 238 assert resolved_schema["properties"]["name"]["type"] == "string" 239 assert resolved_schema["properties"]["password"]["type"] == "string" 240 assert resolved_schema["properties"]["id"]["type"] == "integer" 241 242 @pytest.mark.parametrize("spec", ("3.0.0",), indirect=True) 243 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 244 def test_can_use_schema_in_parameter_with_content(self, spec, schema): 245 param = {"content": {"application/json": {"schema": schema}}} 246 spec.components.parameter("Pet", "body", param) 247 parameter = get_parameters(spec)["Pet"] 248 assert parameter["in"] == "body" 249 reference = parameter["content"]["application/json"]["schema"] 250 assert reference == build_ref(spec, "schema", "Pet") 251 252 resolved_schema = spec.components.schemas["Pet"] 253 assert resolved_schema["properties"]["name"]["type"] == "string" 254 assert resolved_schema["properties"]["password"]["type"] == "string" 255 assert resolved_schema["properties"]["id"]["type"] == "integer" 256 257 258class TestComponentResponseHelper: 259 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 260 def test_can_use_schema_in_response(self, spec, schema): 261 if spec.openapi_version.major < 3: 262 resp = {"schema": schema} 263 else: 264 resp = {"content": {"application/json": {"schema": schema}}} 265 spec.components.response("GetPetOk", resp) 266 response = get_responses(spec)["GetPetOk"] 267 if spec.openapi_version.major < 3: 268 reference = response["schema"] 269 else: 270 reference = response["content"]["application/json"]["schema"] 271 assert reference == build_ref(spec, "schema", "Pet") 272 273 resolved_schema = spec.components.schemas["Pet"] 274 assert resolved_schema["properties"]["id"]["type"] == "integer" 275 assert resolved_schema["properties"]["name"]["type"] == "string" 276 assert resolved_schema["properties"]["password"]["type"] == "string" 277 278 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 279 def test_can_use_schema_in_response_header(self, spec, schema): 280 resp = {"headers": {"PetHeader": {"schema": schema}}} 281 spec.components.response("GetPetOk", resp) 282 response = get_responses(spec)["GetPetOk"] 283 reference = response["headers"]["PetHeader"]["schema"] 284 assert reference == build_ref(spec, "schema", "Pet") 285 286 resolved_schema = spec.components.schemas["Pet"] 287 assert resolved_schema["properties"]["id"]["type"] == "integer" 288 assert resolved_schema["properties"]["name"]["type"] == "string" 289 assert resolved_schema["properties"]["password"]["type"] == "string" 290 291 @pytest.mark.parametrize("spec", ("3.0.0",), indirect=True) 292 def test_content_without_schema(self, spec): 293 resp = {"content": {"application/json": {"example": {"name": "Example"}}}} 294 spec.components.response("GetPetOk", resp) 295 response = get_responses(spec)["GetPetOk"] 296 assert response == resp 297 298 299class TestComponentHeaderHelper: 300 @pytest.mark.parametrize("spec", ("3.0.0",), indirect=True) 301 @pytest.mark.parametrize("schema", [PetSchema, PetSchema()]) 302 def test_can_use_schema_in_header(self, spec, schema): 303 param = {"schema": schema} 304 spec.components.header("Pet", param) 305 header = get_headers(spec)["Pet"] 306 reference = header["schema"] 307 assert reference == build_ref(spec, "schema", "Pet") 308 309 resolved_schema = spec.components.schemas["Pet"] 310 assert resolved_schema["properties"]["name"]["type"] == "string" 311 assert resolved_schema["properties"]["password"]["type"] == "string" 312 assert resolved_schema["properties"]["id"]["type"] == "integer" 313 314 315class TestCustomField: 316 def test_can_use_custom_field_decorator(self, spec_fixture): 317 @spec_fixture.marshmallow_plugin.map_to_openapi_type(DateTime) 318 class CustomNameA(Field): 319 pass 320 321 @spec_fixture.marshmallow_plugin.map_to_openapi_type("integer", "int32") 322 class CustomNameB(Field): 323 pass 324 325 with pytest.raises(TypeError): 326 327 @spec_fixture.marshmallow_plugin.map_to_openapi_type("integer") 328 class BadCustomField(Field): 329 pass 330 331 class CustomPetASchema(PetSchema): 332 name = CustomNameA() 333 334 class CustomPetBSchema(PetSchema): 335 name = CustomNameB() 336 337 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 338 spec_fixture.spec.components.schema("CustomPetA", schema=CustomPetASchema) 339 spec_fixture.spec.components.schema("CustomPetB", schema=CustomPetBSchema) 340 341 props_0 = get_schemas(spec_fixture.spec)["Pet"]["properties"] 342 props_a = get_schemas(spec_fixture.spec)["CustomPetA"]["properties"] 343 props_b = get_schemas(spec_fixture.spec)["CustomPetB"]["properties"] 344 345 assert props_0["name"]["type"] == "string" 346 assert "format" not in props_0["name"] 347 348 assert props_a["name"]["type"] == "string" 349 assert props_a["name"]["format"] == "date-time" 350 351 assert props_b["name"]["type"] == "integer" 352 assert props_b["name"]["format"] == "int32" 353 354 355def get_nested_schema(schema, field_name): 356 try: 357 return schema._declared_fields[field_name]._schema 358 except AttributeError: 359 return schema._declared_fields[field_name]._Nested__schema 360 361 362class TestOperationHelper: 363 @pytest.fixture 364 def make_pet_callback_spec(self, spec_fixture): 365 def _make_pet_spec(operations): 366 spec_fixture.spec.path( 367 path="/pet", 368 operations={ 369 "post": {"callbacks": {"petEvent": {"petCallbackUrl": operations}}} 370 }, 371 ) 372 return spec_fixture 373 374 return _make_pet_spec 375 376 @pytest.mark.parametrize( 377 "pet_schema", 378 (PetSchema, PetSchema(), PetSchema(many=True), "tests.schemas.PetSchema"), 379 ) 380 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 381 def test_schema_v2(self, spec_fixture, pet_schema): 382 spec_fixture.spec.path( 383 path="/pet", 384 operations={ 385 "get": { 386 "responses": { 387 200: { 388 "schema": pet_schema, 389 "description": "successful operation", 390 "headers": {"PetHeader": {"schema": pet_schema}}, 391 } 392 } 393 } 394 }, 395 ) 396 get = get_paths(spec_fixture.spec)["/pet"]["get"] 397 if isinstance(pet_schema, Schema) and pet_schema.many is True: 398 assert get["responses"]["200"]["schema"]["type"] == "array" 399 schema_reference = get["responses"]["200"]["schema"]["items"] 400 assert ( 401 get["responses"]["200"]["headers"]["PetHeader"]["schema"]["type"] 402 == "array" 403 ) 404 header_reference = get["responses"]["200"]["headers"]["PetHeader"][ 405 "schema" 406 ]["items"] 407 else: 408 schema_reference = get["responses"]["200"]["schema"] 409 header_reference = get["responses"]["200"]["headers"]["PetHeader"]["schema"] 410 assert schema_reference == build_ref(spec_fixture.spec, "schema", "Pet") 411 assert header_reference == build_ref(spec_fixture.spec, "schema", "Pet") 412 assert len(spec_fixture.spec.components.schemas) == 1 413 resolved_schema = spec_fixture.spec.components.schemas["Pet"] 414 assert resolved_schema == spec_fixture.openapi.schema2jsonschema(PetSchema) 415 assert get["responses"]["200"]["description"] == "successful operation" 416 417 @pytest.mark.parametrize( 418 "pet_schema", 419 (PetSchema, PetSchema(), PetSchema(many=True), "tests.schemas.PetSchema"), 420 ) 421 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 422 def test_schema_v3(self, spec_fixture, pet_schema): 423 spec_fixture.spec.path( 424 path="/pet", 425 operations={ 426 "get": { 427 "responses": { 428 200: { 429 "content": {"application/json": {"schema": pet_schema}}, 430 "description": "successful operation", 431 "headers": {"PetHeader": {"schema": pet_schema}}, 432 } 433 } 434 } 435 }, 436 ) 437 get = get_paths(spec_fixture.spec)["/pet"]["get"] 438 if isinstance(pet_schema, Schema) and pet_schema.many is True: 439 assert ( 440 get["responses"]["200"]["content"]["application/json"]["schema"]["type"] 441 == "array" 442 ) 443 schema_reference = get["responses"]["200"]["content"]["application/json"][ 444 "schema" 445 ]["items"] 446 assert ( 447 get["responses"]["200"]["headers"]["PetHeader"]["schema"]["type"] 448 == "array" 449 ) 450 header_reference = get["responses"]["200"]["headers"]["PetHeader"][ 451 "schema" 452 ]["items"] 453 else: 454 schema_reference = get["responses"]["200"]["content"]["application/json"][ 455 "schema" 456 ] 457 header_reference = get["responses"]["200"]["headers"]["PetHeader"]["schema"] 458 459 assert schema_reference == build_ref(spec_fixture.spec, "schema", "Pet") 460 assert header_reference == build_ref(spec_fixture.spec, "schema", "Pet") 461 assert len(spec_fixture.spec.components.schemas) == 1 462 resolved_schema = spec_fixture.spec.components.schemas["Pet"] 463 assert resolved_schema == spec_fixture.openapi.schema2jsonschema(PetSchema) 464 assert get["responses"]["200"]["description"] == "successful operation" 465 466 @pytest.mark.parametrize( 467 "pet_schema", 468 (PetSchema, PetSchema(), PetSchema(many=True), "tests.schemas.PetSchema"), 469 ) 470 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 471 def test_callback_schema_v3(self, make_pet_callback_spec, pet_schema): 472 spec_fixture = make_pet_callback_spec( 473 { 474 "get": { 475 "responses": { 476 "200": { 477 "content": {"application/json": {"schema": pet_schema}}, 478 "description": "successful operation", 479 "headers": {"PetHeader": {"schema": pet_schema}}, 480 } 481 } 482 } 483 } 484 ) 485 p = get_paths(spec_fixture.spec)["/pet"] 486 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 487 get = c["get"] 488 if isinstance(pet_schema, Schema) and pet_schema.many is True: 489 assert ( 490 get["responses"]["200"]["content"]["application/json"]["schema"]["type"] 491 == "array" 492 ) 493 schema_reference = get["responses"]["200"]["content"]["application/json"][ 494 "schema" 495 ]["items"] 496 assert ( 497 get["responses"]["200"]["headers"]["PetHeader"]["schema"]["type"] 498 == "array" 499 ) 500 header_reference = get["responses"]["200"]["headers"]["PetHeader"][ 501 "schema" 502 ]["items"] 503 else: 504 schema_reference = get["responses"]["200"]["content"]["application/json"][ 505 "schema" 506 ] 507 header_reference = get["responses"]["200"]["headers"]["PetHeader"]["schema"] 508 509 assert schema_reference == build_ref(spec_fixture.spec, "schema", "Pet") 510 assert header_reference == build_ref(spec_fixture.spec, "schema", "Pet") 511 assert len(spec_fixture.spec.components.schemas) == 1 512 resolved_schema = spec_fixture.spec.components.schemas["Pet"] 513 assert resolved_schema == spec_fixture.openapi.schema2jsonschema(PetSchema) 514 assert get["responses"]["200"]["description"] == "successful operation" 515 516 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 517 def test_schema_expand_parameters_v2(self, spec_fixture): 518 spec_fixture.spec.path( 519 path="/pet", 520 operations={ 521 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 522 "post": { 523 "parameters": [ 524 { 525 "in": "body", 526 "description": "a pet schema", 527 "required": True, 528 "name": "pet", 529 "schema": PetSchema, 530 } 531 ] 532 }, 533 }, 534 ) 535 p = get_paths(spec_fixture.spec)["/pet"] 536 get = p["get"] 537 assert get["parameters"] == spec_fixture.openapi.schema2parameters( 538 PetSchema(), location="query" 539 ) 540 post = p["post"] 541 assert post["parameters"] == spec_fixture.openapi.schema2parameters( 542 PetSchema, 543 location="body", 544 required=True, 545 name="pet", 546 description="a pet schema", 547 ) 548 549 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 550 def test_schema_expand_parameters_v3(self, spec_fixture): 551 spec_fixture.spec.path( 552 path="/pet", 553 operations={ 554 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 555 "post": { 556 "requestBody": { 557 "description": "a pet schema", 558 "required": True, 559 "content": {"application/json": {"schema": PetSchema}}, 560 } 561 }, 562 }, 563 ) 564 p = get_paths(spec_fixture.spec)["/pet"] 565 get = p["get"] 566 assert get["parameters"] == spec_fixture.openapi.schema2parameters( 567 PetSchema(), location="query" 568 ) 569 for parameter in get["parameters"]: 570 description = parameter.get("description", False) 571 assert description 572 name = parameter["name"] 573 assert description == PetSchema.description[name] 574 post = p["post"] 575 post_schema = spec_fixture.marshmallow_plugin.resolver.resolve_schema_dict( 576 PetSchema 577 ) 578 assert ( 579 post["requestBody"]["content"]["application/json"]["schema"] == post_schema 580 ) 581 assert post["requestBody"]["description"] == "a pet schema" 582 assert post["requestBody"]["required"] 583 584 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 585 def test_callback_schema_expand_parameters_v3(self, make_pet_callback_spec): 586 spec_fixture = make_pet_callback_spec( 587 { 588 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 589 "post": { 590 "requestBody": { 591 "description": "a pet schema", 592 "required": True, 593 "content": {"application/json": {"schema": PetSchema}}, 594 } 595 }, 596 } 597 ) 598 p = get_paths(spec_fixture.spec)["/pet"] 599 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 600 get = c["get"] 601 assert get["parameters"] == spec_fixture.openapi.schema2parameters( 602 PetSchema(), location="query" 603 ) 604 for parameter in get["parameters"]: 605 description = parameter.get("description", False) 606 assert description 607 name = parameter["name"] 608 assert description == PetSchema.description[name] 609 post = c["post"] 610 post_schema = spec_fixture.marshmallow_plugin.resolver.resolve_schema_dict( 611 PetSchema 612 ) 613 assert ( 614 post["requestBody"]["content"]["application/json"]["schema"] == post_schema 615 ) 616 assert post["requestBody"]["description"] == "a pet schema" 617 assert post["requestBody"]["required"] 618 619 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 620 def test_schema_uses_ref_if_available_v2(self, spec_fixture): 621 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 622 spec_fixture.spec.path( 623 path="/pet", operations={"get": {"responses": {200: {"schema": PetSchema}}}} 624 ) 625 get = get_paths(spec_fixture.spec)["/pet"]["get"] 626 assert get["responses"]["200"]["schema"] == build_ref( 627 spec_fixture.spec, "schema", "Pet" 628 ) 629 630 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 631 def test_schema_uses_ref_if_available_v3(self, spec_fixture): 632 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 633 spec_fixture.spec.path( 634 path="/pet", 635 operations={ 636 "get": { 637 "responses": { 638 200: {"content": {"application/json": {"schema": PetSchema}}} 639 } 640 } 641 }, 642 ) 643 get = get_paths(spec_fixture.spec)["/pet"]["get"] 644 assert get["responses"]["200"]["content"]["application/json"][ 645 "schema" 646 ] == build_ref(spec_fixture.spec, "schema", "Pet") 647 648 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 649 def test_callback_schema_uses_ref_if_available_v3(self, make_pet_callback_spec): 650 spec_fixture = make_pet_callback_spec( 651 { 652 "get": { 653 "responses": { 654 "200": {"content": {"application/json": {"schema": PetSchema}}} 655 } 656 } 657 } 658 ) 659 p = get_paths(spec_fixture.spec)["/pet"] 660 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 661 get = c["get"] 662 assert get["responses"]["200"]["content"]["application/json"][ 663 "schema" 664 ] == build_ref(spec_fixture.spec, "schema", "Pet") 665 666 def test_schema_uses_ref_if_available_name_resolver_returns_none_v2(self): 667 def resolver(schema): 668 return None 669 670 spec = APISpec( 671 title="Test auto-reference", 672 version="0.1", 673 openapi_version="2.0", 674 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 675 ) 676 spec.components.schema("Pet", schema=PetSchema) 677 spec.path( 678 path="/pet", operations={"get": {"responses": {200: {"schema": PetSchema}}}} 679 ) 680 get = get_paths(spec)["/pet"]["get"] 681 assert get["responses"]["200"]["schema"] == build_ref(spec, "schema", "Pet") 682 683 def test_schema_uses_ref_if_available_name_resolver_returns_none_v3(self): 684 def resolver(schema): 685 return None 686 687 spec = APISpec( 688 title="Test auto-reference", 689 version="0.1", 690 openapi_version="3.0.0", 691 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 692 ) 693 spec.components.schema("Pet", schema=PetSchema) 694 spec.path( 695 path="/pet", 696 operations={ 697 "get": { 698 "responses": { 699 200: {"content": {"application/json": {"schema": PetSchema}}} 700 } 701 } 702 }, 703 ) 704 get = get_paths(spec)["/pet"]["get"] 705 assert get["responses"]["200"]["content"]["application/json"][ 706 "schema" 707 ] == build_ref(spec, "schema", "Pet") 708 709 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 710 def test_schema_resolver_allof_v2(self, spec_fixture): 711 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 712 spec_fixture.spec.components.schema("Sample", schema=SampleSchema) 713 spec_fixture.spec.path( 714 path="/pet", 715 operations={ 716 "get": { 717 "responses": {200: {"schema": {"allOf": [PetSchema, SampleSchema]}}} 718 } 719 }, 720 ) 721 get = get_paths(spec_fixture.spec)["/pet"]["get"] 722 assert get["responses"]["200"]["schema"] == { 723 "allOf": [ 724 build_ref(spec_fixture.spec, "schema", "Pet"), 725 build_ref(spec_fixture.spec, "schema", "Sample"), 726 ] 727 } 728 729 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 730 @pytest.mark.parametrize("combinator", ["oneOf", "anyOf", "allOf"]) 731 def test_schema_resolver_oneof_anyof_allof_v3(self, spec_fixture, combinator): 732 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 733 spec_fixture.spec.path( 734 path="/pet", 735 operations={ 736 "get": { 737 "responses": { 738 200: { 739 "content": { 740 "application/json": { 741 "schema": {combinator: [PetSchema, SampleSchema]} 742 } 743 } 744 } 745 } 746 } 747 }, 748 ) 749 get = get_paths(spec_fixture.spec)["/pet"]["get"] 750 assert get["responses"]["200"]["content"]["application/json"]["schema"] == { 751 combinator: [ 752 build_ref(spec_fixture.spec, "schema", "Pet"), 753 build_ref(spec_fixture.spec, "schema", "Sample"), 754 ] 755 } 756 757 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 758 def test_schema_resolver_not_v2(self, spec_fixture): 759 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 760 spec_fixture.spec.path( 761 path="/pet", 762 operations={"get": {"responses": {200: {"schema": {"not": PetSchema}}}}}, 763 ) 764 get = get_paths(spec_fixture.spec)["/pet"]["get"] 765 assert get["responses"]["200"]["schema"] == { 766 "not": build_ref(spec_fixture.spec, "schema", "Pet"), 767 } 768 769 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 770 def test_schema_resolver_not_v3(self, spec_fixture): 771 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 772 spec_fixture.spec.path( 773 path="/pet", 774 operations={ 775 "get": { 776 "responses": { 777 200: { 778 "content": { 779 "application/json": {"schema": {"not": PetSchema}} 780 } 781 } 782 } 783 } 784 }, 785 ) 786 get = get_paths(spec_fixture.spec)["/pet"]["get"] 787 assert get["responses"]["200"]["content"]["application/json"]["schema"] == { 788 "not": build_ref(spec_fixture.spec, "schema", "Pet"), 789 } 790 791 @pytest.mark.parametrize( 792 "pet_schema", 793 (PetSchema, PetSchema(), "tests.schemas.PetSchema"), 794 ) 795 def test_schema_name_resolver_returns_none_v2(self, pet_schema): 796 def resolver(schema): 797 return None 798 799 spec = APISpec( 800 title="Test resolver returns None", 801 version="0.1", 802 openapi_version="2.0", 803 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 804 ) 805 spec.path( 806 path="/pet", 807 operations={"get": {"responses": {200: {"schema": pet_schema}}}}, 808 ) 809 get = get_paths(spec)["/pet"]["get"] 810 assert "properties" in get["responses"]["200"]["schema"] 811 812 @pytest.mark.parametrize( 813 "pet_schema", 814 (PetSchema, PetSchema(), "tests.schemas.PetSchema"), 815 ) 816 def test_schema_name_resolver_returns_none_v3(self, pet_schema): 817 def resolver(schema): 818 return None 819 820 spec = APISpec( 821 title="Test resolver returns None", 822 version="0.1", 823 openapi_version="3.0.0", 824 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 825 ) 826 spec.path( 827 path="/pet", 828 operations={ 829 "get": { 830 "responses": { 831 200: {"content": {"application/json": {"schema": pet_schema}}} 832 } 833 } 834 }, 835 ) 836 get = get_paths(spec)["/pet"]["get"] 837 assert ( 838 "properties" 839 in get["responses"]["200"]["content"]["application/json"]["schema"] 840 ) 841 842 def test_callback_schema_uses_ref_if_available_name_resolver_returns_none_v3(self): 843 def resolver(schema): 844 return None 845 846 spec = APISpec( 847 title="Test auto-reference", 848 version="0.1", 849 openapi_version="3.0.0", 850 plugins=(MarshmallowPlugin(schema_name_resolver=resolver),), 851 ) 852 spec.components.schema("Pet", schema=PetSchema) 853 spec.path( 854 path="/pet", 855 operations={ 856 "post": { 857 "callbacks": { 858 "petEvent": { 859 "petCallbackUrl": { 860 "get": { 861 "responses": { 862 "200": { 863 "content": { 864 "application/json": { 865 "schema": PetSchema 866 } 867 } 868 } 869 } 870 } 871 } 872 } 873 } 874 } 875 }, 876 ) 877 p = get_paths(spec)["/pet"] 878 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 879 get = c["get"] 880 assert get["responses"]["200"]["content"]["application/json"][ 881 "schema" 882 ] == build_ref(spec, "schema", "Pet") 883 884 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 885 def test_schema_uses_ref_in_parameters_and_request_body_if_available_v2( 886 self, spec_fixture 887 ): 888 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 889 spec_fixture.spec.path( 890 path="/pet", 891 operations={ 892 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 893 "post": {"parameters": [{"in": "body", "schema": PetSchema}]}, 894 }, 895 ) 896 p = get_paths(spec_fixture.spec)["/pet"] 897 assert "schema" not in p["get"]["parameters"][0] 898 post = p["post"] 899 assert len(post["parameters"]) == 1 900 assert post["parameters"][0]["schema"] == build_ref( 901 spec_fixture.spec, "schema", "Pet" 902 ) 903 904 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 905 def test_schema_uses_ref_in_parameters_and_request_body_if_available_v3( 906 self, spec_fixture 907 ): 908 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 909 spec_fixture.spec.path( 910 path="/pet", 911 operations={ 912 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 913 "post": { 914 "requestBody": { 915 "content": {"application/json": {"schema": PetSchema}} 916 } 917 }, 918 }, 919 ) 920 p = get_paths(spec_fixture.spec)["/pet"] 921 assert "schema" in p["get"]["parameters"][0] 922 post = p["post"] 923 schema_ref = post["requestBody"]["content"]["application/json"]["schema"] 924 assert schema_ref == build_ref(spec_fixture.spec, "schema", "Pet") 925 926 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 927 def test_callback_schema_uses_ref_in_parameters_and_request_body_if_available_v3( 928 self, make_pet_callback_spec 929 ): 930 spec_fixture = make_pet_callback_spec( 931 { 932 "get": {"parameters": [{"in": "query", "schema": PetSchema}]}, 933 "post": { 934 "requestBody": { 935 "content": {"application/json": {"schema": PetSchema}} 936 } 937 }, 938 } 939 ) 940 p = get_paths(spec_fixture.spec)["/pet"] 941 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 942 assert "schema" in c["get"]["parameters"][0] 943 post = c["post"] 944 schema_ref = post["requestBody"]["content"]["application/json"]["schema"] 945 assert schema_ref == build_ref(spec_fixture.spec, "schema", "Pet") 946 947 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 948 def test_schema_array_uses_ref_if_available_v2(self, spec_fixture): 949 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 950 spec_fixture.spec.path( 951 path="/pet", 952 operations={ 953 "get": { 954 "parameters": [ 955 { 956 "name": "petSchema", 957 "in": "body", 958 "schema": {"type": "array", "items": PetSchema}, 959 } 960 ], 961 "responses": { 962 200: {"schema": {"type": "array", "items": PetSchema}} 963 }, 964 } 965 }, 966 ) 967 get = get_paths(spec_fixture.spec)["/pet"]["get"] 968 assert len(get["parameters"]) == 1 969 resolved_schema = { 970 "type": "array", 971 "items": build_ref(spec_fixture.spec, "schema", "Pet"), 972 } 973 assert get["parameters"][0]["schema"] == resolved_schema 974 assert get["responses"]["200"]["schema"] == resolved_schema 975 976 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 977 def test_schema_array_uses_ref_if_available_v3(self, spec_fixture): 978 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 979 spec_fixture.spec.path( 980 path="/pet", 981 operations={ 982 "get": { 983 "parameters": [ 984 { 985 "name": "Pet", 986 "in": "query", 987 "content": { 988 "application/json": { 989 "schema": {"type": "array", "items": PetSchema} 990 } 991 }, 992 } 993 ], 994 "responses": { 995 200: { 996 "content": { 997 "application/json": { 998 "schema": {"type": "array", "items": PetSchema} 999 } 1000 } 1001 } 1002 }, 1003 } 1004 }, 1005 ) 1006 get = get_paths(spec_fixture.spec)["/pet"]["get"] 1007 assert len(get["parameters"]) == 1 1008 resolved_schema = { 1009 "type": "array", 1010 "items": build_ref(spec_fixture.spec, "schema", "Pet"), 1011 } 1012 request_schema = get["parameters"][0]["content"]["application/json"]["schema"] 1013 assert request_schema == resolved_schema 1014 response_schema = get["responses"]["200"]["content"]["application/json"][ 1015 "schema" 1016 ] 1017 assert response_schema == resolved_schema 1018 1019 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 1020 def test_callback_schema_array_uses_ref_if_available_v3( 1021 self, make_pet_callback_spec 1022 ): 1023 spec_fixture = make_pet_callback_spec( 1024 { 1025 "get": { 1026 "parameters": [ 1027 { 1028 "name": "Pet", 1029 "in": "query", 1030 "content": { 1031 "application/json": { 1032 "schema": {"type": "array", "items": PetSchema} 1033 } 1034 }, 1035 } 1036 ], 1037 "responses": { 1038 "200": { 1039 "content": { 1040 "application/json": { 1041 "schema": {"type": "array", "items": PetSchema} 1042 } 1043 } 1044 } 1045 }, 1046 } 1047 } 1048 ) 1049 p = get_paths(spec_fixture.spec)["/pet"] 1050 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 1051 get = c["get"] 1052 assert len(get["parameters"]) == 1 1053 resolved_schema = { 1054 "type": "array", 1055 "items": build_ref(spec_fixture.spec, "schema", "Pet"), 1056 } 1057 request_schema = get["parameters"][0]["content"]["application/json"]["schema"] 1058 assert request_schema == resolved_schema 1059 response_schema = get["responses"]["200"]["content"]["application/json"][ 1060 "schema" 1061 ] 1062 assert response_schema == resolved_schema 1063 1064 @pytest.mark.parametrize("spec_fixture", ("2.0",), indirect=True) 1065 def test_schema_partially_v2(self, spec_fixture): 1066 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 1067 spec_fixture.spec.path( 1068 path="/parents", 1069 operations={ 1070 "get": { 1071 "responses": { 1072 200: { 1073 "schema": { 1074 "type": "object", 1075 "properties": { 1076 "mother": PetSchema, 1077 "father": PetSchema, 1078 }, 1079 } 1080 } 1081 } 1082 } 1083 }, 1084 ) 1085 get = get_paths(spec_fixture.spec)["/parents"]["get"] 1086 assert get["responses"]["200"]["schema"] == { 1087 "type": "object", 1088 "properties": { 1089 "mother": build_ref(spec_fixture.spec, "schema", "Pet"), 1090 "father": build_ref(spec_fixture.spec, "schema", "Pet"), 1091 }, 1092 } 1093 1094 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 1095 def test_schema_partially_v3(self, spec_fixture): 1096 spec_fixture.spec.components.schema("Pet", schema=PetSchema) 1097 spec_fixture.spec.path( 1098 path="/parents", 1099 operations={ 1100 "get": { 1101 "responses": { 1102 200: { 1103 "content": { 1104 "application/json": { 1105 "schema": { 1106 "type": "object", 1107 "properties": { 1108 "mother": PetSchema, 1109 "father": PetSchema, 1110 }, 1111 } 1112 } 1113 } 1114 } 1115 } 1116 } 1117 }, 1118 ) 1119 get = get_paths(spec_fixture.spec)["/parents"]["get"] 1120 assert get["responses"]["200"]["content"]["application/json"]["schema"] == { 1121 "type": "object", 1122 "properties": { 1123 "mother": build_ref(spec_fixture.spec, "schema", "Pet"), 1124 "father": build_ref(spec_fixture.spec, "schema", "Pet"), 1125 }, 1126 } 1127 1128 @pytest.mark.parametrize("spec_fixture", ("3.0.0",), indirect=True) 1129 def test_callback_schema_partially_v3(self, make_pet_callback_spec): 1130 spec_fixture = make_pet_callback_spec( 1131 { 1132 "get": { 1133 "responses": { 1134 "200": { 1135 "content": { 1136 "application/json": { 1137 "schema": { 1138 "type": "object", 1139 "properties": { 1140 "mother": PetSchema, 1141 "father": PetSchema, 1142 }, 1143 } 1144 } 1145 } 1146 } 1147 } 1148 } 1149 } 1150 ) 1151 p = get_paths(spec_fixture.spec)["/pet"] 1152 c = p["post"]["callbacks"]["petEvent"]["petCallbackUrl"] 1153 get = c["get"] 1154 assert get["responses"]["200"]["content"]["application/json"]["schema"] == { 1155 "type": "object", 1156 "properties": { 1157 "mother": build_ref(spec_fixture.spec, "schema", "Pet"), 1158 "father": build_ref(spec_fixture.spec, "schema", "Pet"), 1159 }, 1160 } 1161 1162 def test_parameter_reference(self, spec_fixture): 1163 if spec_fixture.spec.openapi_version.major < 3: 1164 param = {"schema": PetSchema} 1165 else: 1166 param = {"content": {"application/json": {"schema": PetSchema}}} 1167 spec_fixture.spec.components.parameter("Pet", "body", param) 1168 spec_fixture.spec.path( 1169 path="/parents", operations={"get": {"parameters": ["Pet"]}} 1170 ) 1171 get = get_paths(spec_fixture.spec)["/parents"]["get"] 1172 assert get["parameters"] == [build_ref(spec_fixture.spec, "parameter", "Pet")] 1173 1174 def test_response_reference(self, spec_fixture): 1175 if spec_fixture.spec.openapi_version.major < 3: 1176 resp = {"schema": PetSchema} 1177 else: 1178 resp = {"content": {"application/json": {"schema": PetSchema}}} 1179 spec_fixture.spec.components.response("Pet", resp) 1180 spec_fixture.spec.path( 1181 path="/parents", operations={"get": {"responses": {"200": "Pet"}}} 1182 ) 1183 get = get_paths(spec_fixture.spec)["/parents"]["get"] 1184 assert get["responses"] == { 1185 "200": build_ref(spec_fixture.spec, "response", "Pet") 1186 } 1187 1188 def test_schema_global_state_untouched_2json(self, spec_fixture): 1189 assert get_nested_schema(RunSchema, "sample") is None 1190 data = spec_fixture.openapi.schema2jsonschema(RunSchema) 1191 json.dumps(data) 1192 assert get_nested_schema(RunSchema, "sample") is None 1193 1194 def test_schema_global_state_untouched_2parameters(self, spec_fixture): 1195 assert get_nested_schema(RunSchema, "sample") is None 1196 data = spec_fixture.openapi.schema2parameters(RunSchema, location="json") 1197 json.dumps(data) 1198 assert get_nested_schema(RunSchema, "sample") is None 1199 1200 def test_resolve_schema_dict_ref_as_string(self, spec): 1201 """Test schema ref passed as string""" 1202 # The case tested here is a reference passed as string, not a 1203 # marshmallow Schema passed by name as string. We want to ensure the 1204 # MarshmallowPlugin does not interfere with the feature interpreting 1205 # strings as references. Therefore, we use a specific name to ensure 1206 # there is no Schema with that name in the marshmallow registry from 1207 # somewhere else in the tests. 1208 # e.g. PetSchema is in the registry already so it wouldn't work. 1209 schema = {"schema": "SomeSpecificPetSchema"} 1210 if spec.openapi_version.major >= 3: 1211 schema = {"content": {"application/json": schema}} 1212 spec.path("/pet/{petId}", operations={"get": {"responses": {"200": schema}}}) 1213 resp = get_paths(spec)["/pet/{petId}"]["get"]["responses"]["200"] 1214 if spec.openapi_version.major < 3: 1215 schema = resp["schema"] 1216 else: 1217 schema = resp["content"]["application/json"]["schema"] 1218 assert schema == build_ref(spec, "schema", "SomeSpecificPetSchema") 1219 1220 1221class TestCircularReference: 1222 def test_circular_referencing_schemas(self, spec): 1223 spec.components.schema("Analysis", schema=AnalysisSchema) 1224 definitions = get_schemas(spec) 1225 ref = definitions["Analysis"]["properties"]["sample"] 1226 assert ref == build_ref(spec, "schema", "Sample") 1227 1228 1229# Regression tests for issue #55 1230class TestSelfReference: 1231 def test_self_referencing_field_single(self, spec): 1232 spec.components.schema("SelfReference", schema=SelfReferencingSchema) 1233 definitions = get_schemas(spec) 1234 ref = definitions["SelfReference"]["properties"]["single"] 1235 assert ref == build_ref(spec, "schema", "SelfReference") 1236 1237 def test_self_referencing_field_many(self, spec): 1238 spec.components.schema("SelfReference", schema=SelfReferencingSchema) 1239 definitions = get_schemas(spec) 1240 result = definitions["SelfReference"]["properties"]["many"] 1241 assert result == { 1242 "type": "array", 1243 "items": build_ref(spec, "schema", "SelfReference"), 1244 } 1245 1246 1247class TestOrderedSchema: 1248 def test_ordered_schema(self, spec): 1249 spec.components.schema("Ordered", schema=OrderedSchema) 1250 result = get_schemas(spec)["Ordered"]["properties"] 1251 assert list(result.keys()) == ["field1", "field2", "field3", "field4", "field5"] 1252 1253 1254class TestFieldWithCustomProps: 1255 def test_field_with_custom_props(self, spec): 1256 spec.components.schema("PatternedObject", schema=PatternedObjectSchema) 1257 result = get_schemas(spec)["PatternedObject"]["properties"]["count"] 1258 assert "x-count" in result 1259 assert result["x-count"] == 1 1260 1261 def test_field_with_custom_props_passed_as_snake_case(self, spec): 1262 spec.components.schema("PatternedObject", schema=PatternedObjectSchema) 1263 result = get_schemas(spec)["PatternedObject"]["properties"]["count2"] 1264 assert "x-count2" in result 1265 assert result["x-count2"] == 2 1266 1267 1268class TestSchemaWithDefaultValues: 1269 def test_schema_with_default_values(self, spec): 1270 spec.components.schema("DefaultValuesSchema", schema=DefaultValuesSchema) 1271 definitions = get_schemas(spec) 1272 props = definitions["DefaultValuesSchema"]["properties"] 1273 assert props["number_auto_default"]["default"] == 12 1274 assert props["number_manual_default"]["default"] == 42 1275 assert "default" not in props["string_callable_default"] 1276 assert props["string_manual_default"]["default"] == "Manual" 1277 assert "default" not in props["numbers"] 1278 1279 1280class TestDictValues: 1281 def test_dict_values_resolve_to_additional_properties(self, spec): 1282 class SchemaWithDict(Schema): 1283 dict_field = Dict(values=String()) 1284 1285 spec.components.schema("SchemaWithDict", schema=SchemaWithDict) 1286 result = get_schemas(spec)["SchemaWithDict"]["properties"]["dict_field"] 1287 assert result == {"type": "object", "additionalProperties": {"type": "string"}} 1288 1289 def test_dict_with_empty_values_field(self, spec): 1290 class SchemaWithDict(Schema): 1291 dict_field = Dict() 1292 1293 spec.components.schema("SchemaWithDict", schema=SchemaWithDict) 1294 result = get_schemas(spec)["SchemaWithDict"]["properties"]["dict_field"] 1295 assert result == {"type": "object"} 1296 1297 def test_dict_with_nested(self, spec): 1298 class SchemaWithDict(Schema): 1299 dict_field = Dict(values=Nested(PetSchema)) 1300 1301 spec.components.schema("SchemaWithDict", schema=SchemaWithDict) 1302 1303 assert len(get_schemas(spec)) == 2 1304 1305 result = get_schemas(spec)["SchemaWithDict"]["properties"]["dict_field"] 1306 assert result == { 1307 "additionalProperties": build_ref(spec, "schema", "Pet"), 1308 "type": "object", 1309 } 1310 1311 1312class TestList: 1313 def test_list_with_nested(self, spec): 1314 class SchemaWithList(Schema): 1315 list_field = List(Nested(PetSchema)) 1316 1317 spec.components.schema("SchemaWithList", schema=SchemaWithList) 1318 1319 assert len(get_schemas(spec)) == 2 1320 1321 result = get_schemas(spec)["SchemaWithList"]["properties"]["list_field"] 1322 assert result == {"items": build_ref(spec, "schema", "Pet"), "type": "array"} 1323 1324 1325class TestTimeDelta: 1326 def test_timedelta_x_unit(self, spec): 1327 class SchemaWithTimeDelta(Schema): 1328 sec = TimeDelta("seconds") 1329 day = TimeDelta("days") 1330 1331 spec.components.schema("SchemaWithTimeDelta", schema=SchemaWithTimeDelta) 1332 1333 assert ( 1334 get_schemas(spec)["SchemaWithTimeDelta"]["properties"]["sec"]["x-unit"] 1335 == "seconds" 1336 ) 1337 assert ( 1338 get_schemas(spec)["SchemaWithTimeDelta"]["properties"]["day"]["x-unit"] 1339 == "days" 1340 ) 1341