File: test_models.py

package info (click to toggle)
python-odmantic 1.0.2-4
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,640 kB
  • sloc: python: 8,547; sh: 37; makefile: 34; xml: 13; javascript: 3
file content (186 lines) | stat: -rw-r--r-- 5,132 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
from datetime import datetime
from inspect import getdoc
from typing import Type

import pytest
from bson import ObjectId as BSONObjectId

from odmantic import Model, ObjectId, Reference
from odmantic.bson import BaseBSONModel, Binary, Decimal128, Int64, Regex
from odmantic.model import EmbeddedModel


def test_object_id_fastapi_get_query(fastapi_app, test_client):
    value_injected = None

    @fastapi_app.get("/{id}")
    def get(id: ObjectId):
        nonlocal value_injected
        value_injected = id
        return "ok"

    id_get_str = "5f79d7e8b305f24ca43593e2"
    test_client.get(f"/{id_get_str}")
    assert value_injected == BSONObjectId(id_get_str)


def test_object_id_fastapi_get_query_invalid_id(fastapi_app, test_client):
    @fastapi_app.get("/{id}")
    def get(id: ObjectId):
        return "ok"  # pragma: no cover

    invalid_oid_str = "a"
    response = test_client.get(f"/{invalid_oid_str}")
    assert response.status_code == 422
    assert response.json()["detail"][0]["loc"] == [
        "path",
        "id",
        "is-instance[ObjectId]",
    ]


@pytest.mark.skip("Need to specify custom json_encoder or to use a root_type")
def test_object_id_fastapi_response(fastapi_app, test_client):
    id_get_str = "5f79d7e8b305f24ca43593e2"

    @fastapi_app.get("/")
    def get():
        return {"id": ObjectId(id_get_str)}

    response = test_client.get("/")
    assert response.json() == {"id": id_get_str}


def test_object_id_fastapi_pydantic_response_model(fastapi_app, test_client):
    id_get_str = "5f79d7e8b305f24ca43593e2"

    class PydanticModel(BaseBSONModel):
        id: ObjectId

        # Defining a config object WITHOUT json_encoders arguments
        model_config = {}

    @fastapi_app.get("/", response_model=PydanticModel)
    def get():
        return {"id": ObjectId(id_get_str)}

    response = test_client.get("/")
    assert response.json() == {"id": id_get_str}


def test_object_id_fastapi_odmantic_response_pydantic_model(fastapi_app, test_client):
    class ODMModel(Model): ...

    object = ODMModel()

    @fastapi_app.get("/", response_model=ODMModel.__pydantic_model__)
    def get():
        return object

    response = test_client.get("/")
    assert response.json() == {"id": str(object.id)}


def test_object_id_fastapi_odmantic_response_model(fastapi_app, test_client):
    class ODMModel(Model): ...

    object = ODMModel()

    @fastapi_app.get("/", response_model=ODMModel)
    def get():
        return object

    response = test_client.get("/")
    assert response.json() == {"id": str(object.id)}


def test_openapi_json_with_bson_fields(fastapi_app, test_client):
    class ODMModel(Model):
        oid: ObjectId
        int64: Int64
        decimal: Decimal128
        binary: Binary
        regex: Regex
        datetime_: datetime

    @fastapi_app.get("/", response_model=ODMModel)
    def get():
        return None  # pragma: no cover

    response = test_client.get("/openapi.json")
    assert response.status_code == 200


@pytest.mark.parametrize("base", (Model, EmbeddedModel))
def test_docstring_not_nullified(base: Type):
    class M(base):
        """My docstring"""

    doc = getdoc(M)
    assert doc is None or doc == "My docstring"
    description = M.model_json_schema()["description"]
    assert description == "My docstring"


@pytest.mark.parametrize("base", (Model, EmbeddedModel))
def test_docstring_nullified(base: Type):
    class M(base): ...

    doc = getdoc(M)
    assert doc == ""
    assert "description" not in M.model_json_schema()


@pytest.mark.parametrize("base", (Model, EmbeddedModel, BaseBSONModel))
def test_base_classes_docstring_not_nullified(base: Type):
    doc = getdoc(base)
    assert doc is not None and doc != ""


@pytest.mark.parametrize("base", (Model, EmbeddedModel))
def test_pydantic_model_title(base: Type):
    class M(base): ...

    assert M.__pydantic_model__.model_json_schema()["title"] == "M"


@pytest.mark.parametrize("base", (Model, EmbeddedModel))
def test_pydantic_model_custom_title(base: Type):
    class M(base):
        model_config = {"title": "CustomTitle"}

    assert M.__pydantic_model__.model_json_schema()["title"] == "CustomTitle"


def test_pydantic_model_references():
    class Referenced(Model): ...

    class Base(Model):
        field: Referenced = Reference()

    assert not hasattr(
        Base.__pydantic_model__, "field"
    ), "class attribute should be empty"
    assert not issubclass(
        Base.__pydantic_model__, Model
    ), "the pydantic_model should inherit from Model"

    b_pure = Base.__pydantic_model__(field=Referenced().__pydantic_model__())
    assert not issubclass(
        type(b_pure.field),  # type: ignore
        Model,
    ), "the pure field should not inherit from Model"


def test_openapi_json_references(fastapi_app, test_client):
    class Referenced(Model): ...

    class Base(Model):
        field: Referenced = Reference()

    @fastapi_app.get("/", response_model=Base)
    def get():
        return None  # pragma: no cover

    response = test_client.get("/openapi.json")
    assert response.status_code == 200