test_docs.py
1 from unittest.mock import MagicMock 2 3 from fastapi import FastAPI 4 from pydantic import BaseModel, Field 5 from pytest_mock import MockerFixture 6 7 from .._utils import mock_dict 8 from api.utils import docs 9 10 11 async def test__responses() -> None: 12 default = MagicMock() 13 14 def make_exception(status_code: int) -> MagicMock: 15 out = MagicMock() 16 out.__name__ = MagicMock() 17 out.status_code = status_code 18 return out 19 20 args = [a := make_exception(401), b := make_exception(403), c := make_exception(403), d := make_exception(404)] 21 22 result = docs.responses(default, *args) 23 24 assert result == { 25 200: {"model": default}, 26 401: { 27 "description": b"Unauthorized", 28 "content": { 29 "application/json": { 30 "examples": {a.__name__: {"description": a.description, "value": {"detail": a.detail}}} 31 } 32 }, 33 }, 34 403: { 35 "description": b"Forbidden", 36 "content": { 37 "application/json": { 38 "examples": { 39 f"{b.__name__} (1/2)": {"description": b.description, "value": {"detail": b.detail}}, 40 f"{c.__name__} (2/2)": {"description": c.description, "value": {"detail": c.detail}}, 41 } 42 } 43 }, 44 }, 45 404: { 46 "description": b"Not Found", 47 "content": { 48 "application/json": { 49 "examples": {d.__name__: {"description": d.description, "value": {"detail": d.detail}}} 50 } 51 }, 52 }, 53 } 54 55 56 async def test__get_example() -> None: 57 arg = MagicMock() 58 arg.Config.schema_extra = {"example": (expected := MagicMock())} 59 60 assert docs.get_example(arg) == expected 61 62 63 async def test__example(mocker: MockerFixture) -> None: 64 get_example_patch = mocker.patch("api.utils.docs.get_example") 65 66 args = [a := MagicMock(), b := MagicMock()] 67 get_example_patch.side_effect = lambda x: MagicMock( 68 items=lambda: [(x.first.key, x.first.value), (x.second.key, x.second.value)] 69 ) 70 kwargs = mock_dict(5, True) 71 72 result = docs.example(*args, **kwargs) 73 74 assert result.schema_extra == { 75 "example": { 76 a.first.key: a.first.value, 77 a.second.key: a.second.value, 78 b.first.key: b.first.value, 79 b.second.key: b.second.value, 80 **kwargs, 81 } 82 } 83 84 85 async def test__add_endpoint_links_to_openapi_docs() -> None: 86 app = FastAPI( 87 description="`GET /test` test `POST /foobar`", 88 openapi_tags=[{"name": "test", "description": "asdf `GET /test`"}], 89 ) 90 91 class Model(BaseModel): 92 test: str = Field(description="xyz `POST /foobar`") 93 94 @app.get("/test", tags=["test"], responses=docs.responses(Model)) 95 async def test() -> None: 96 """Test endpoint. `POST /foobar`""" 97 pass 98 99 @app.post("/foobar", tags=["test"]) 100 async def foobar() -> None: 101 """Foobar endpoint. `GET /test`""" 102 pass 103 104 docs.add_endpoint_links_to_openapi_docs(app.openapi()) 105 schema = app.openapi() 106 assert ( 107 schema["info"]["description"] 108 == "[`GET /test`](docs#/test/test_test_get) test [`POST /foobar`](docs#/test/foobar_foobar_post)" 109 ) 110 assert schema["tags"][0]["description"] == "asdf [`GET /test`](docs#/test/test_test_get)" 111 assert ( 112 schema["paths"]["/test"]["get"]["description"] 113 == "Test endpoint. [`POST /foobar`](docs#/test/foobar_foobar_post)" 114 ) 115 assert ( 116 schema["paths"]["/foobar"]["post"]["description"] == "Foobar endpoint. [`GET /test`](docs#/test/test_test_get)" 117 ) 118 assert ( 119 schema["components"]["schemas"]["Model"]["properties"]["test"]["description"] 120 == "xyz [`POST /foobar`](docs#/test/foobar_foobar_post)" 121 )