• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MrThearMan / undine / 19819090087

01 Dec 2025 09:23AM UTC coverage: 96.147% (+0.8%) from 95.382%
19819090087

push

github

matti-lamppu
Support one_of input objects from graphql-core 3.2.7

11 of 14 new or added lines in 3 files covered. (78.57%)

311 existing lines in 28 files now uncovered.

32636 of 33944 relevant lines covered (96.15%)

3.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.13
/tests/test_utils/test_graphql/test_validation_rules/test_one_of_input_object.py
1
from __future__ import annotations
4✔
2

3
import pytest
4✔
4

5
from example_project.app.models import Comment, Project, Task, TaskTypeChoices
4✔
6
from undine import Entrypoint, Field, Input, MutationType, QueryType, RootType, create_schema
4✔
7
from undine.utils.graphql.validation_rules import core_implements_one_of_directive
4✔
8

9

10
@pytest.mark.django_db
4✔
11
def test_validation_rules__one_of_input_object__multiple_keys(graphql, undine_settings) -> None:
4✔
12
    class TaskType(QueryType[Task]):
4✔
13
        name = Field()
4✔
14

15
    class ProjectType(QueryType[Project]):
4✔
16
        name = Field()
4✔
17

18
    class CommentType(QueryType[Comment]):
4✔
19
        contents = Field()
4✔
20
        target = Field()
4✔
21

22
    class CommentCreateMutation(MutationType[Comment]):
4✔
23
        contents = Input()
4✔
24
        target = Input()
4✔
25

26
    class Query(RootType):
4✔
27
        comments = Entrypoint(CommentType, many=True)
4✔
28

29
    class Mutation(RootType):
4✔
30
        create_comment = Entrypoint(CommentCreateMutation)
4✔
31

32
    undine_settings.SCHEMA = create_schema(query=Query, mutation=Mutation)
4✔
33

34
    query = """
4✔
35
        mutation($input: CommentCreateMutation!) {
36
            createComment(input: $input) {
37
                contents
38
                target {
39
                    __typename
40
                    ... on TaskType {
41
                        name
42
                    }
43
                    ... on ProjectType {
44
                        name
45
                    }
46
                }
47
            }
48
        }
49
    """
50

51
    input_data = {
4✔
52
        "contents": "Comment",
53
        "target": {
54
            "task": {
55
                "name": "Test Task",
56
                "type": TaskTypeChoices.TASK,
57
            },
58
            "project": {
59
                "name": "Test Project",
60
            },
61
        },
62
    }
63
    response = graphql(query, variables={"input": input_data})
4✔
64

65
    # Different error message due to how input argument validation is implemented
66
    # in GraphQL core 3.2.7 onwards, this is part of `coerce_input_value`, and before
67
    # that it's implemented using a custom 'out_type' function.
68
    if core_implements_one_of_directive():
4✔
69
        msg = (
4✔
70
            "Variable '$input' got invalid value "
71
            "{'task': {'name': 'Test Task', 'type': 'TASK'}, 'project': {'name': 'Test Project'}} at 'input.target'; "
72
            "Exactly one key must be specified for OneOf type 'CommentTargetInput'."
73
        )
74
    else:
NEW
75
        msg = "OneOf Input Object 'CommentTargetInput' must specify exactly one key."
×
76

77
    assert response.errors == [
4✔
78
        {
79
            "message": msg,
80
            "extensions": {"status_code": 400},
81
        }
82
    ]
83

84

85
@pytest.mark.django_db
4✔
86
def test_validation_rules__one_of_input_object__null_key(graphql, undine_settings) -> None:
4✔
87
    class TaskType(QueryType[Task]):
4✔
88
        name = Field()
4✔
89

90
    class CommentType(QueryType[Comment]):
4✔
91
        contents = Field()
4✔
92
        target = Field()
4✔
93

94
    class CommentCreateMutation(MutationType[Comment]):
4✔
95
        contents = Input()
4✔
96
        target = Input()
4✔
97

98
    class Query(RootType):
4✔
99
        comments = Entrypoint(CommentType, many=True)
4✔
100

101
    class Mutation(RootType):
4✔
102
        create_comment = Entrypoint(CommentCreateMutation)
4✔
103

104
    undine_settings.SCHEMA = create_schema(query=Query, mutation=Mutation)
4✔
105

106
    query = """
4✔
107
        mutation($input: CommentCreateMutation!) {
108
            createComment(input: $input) {
109
                contents
110
                target {
111
                    __typename
112
                    ... on TaskType {
113
                        name
114
                    }
115
                }
116
            }
117
        }
118
    """
119

120
    input_data = {
4✔
121
        "contents": "Comment",
122
        "target": {
123
            "task": None,
124
        },
125
    }
126
    response = graphql(query, variables={"input": input_data})
4✔
127

128
    # Different error message due to how input argument validation is implemented
129
    # in GraphQL core 3.2.7 onwards, this is part of `coerce_input_value`, and before
130
    # that it's implemented using a custom 'out_type' function.
131
    if core_implements_one_of_directive():
4✔
132
        msg = "Variable '$input' got invalid value None at 'input.target.task'; Field 'task' must be non-null."
4✔
133
    else:
NEW
134
        msg = "OneOf Input Object 'CommentTargetInput' must specify exactly one key."
×
135

136
    assert response.errors == [
4✔
137
        {
138
            "message": msg,
139
            "extensions": {"status_code": 400},
140
        }
141
    ]
142

143

144
@pytest.mark.django_db
4✔
145
def test_validation_rules__one_of_input_object__multiple_keys__document(graphql, undine_settings) -> None:
4✔
146
    class TaskType(QueryType[Task]):
4✔
147
        name = Field()
4✔
148

149
    class ProjectType(QueryType[Project]):
4✔
150
        name = Field()
4✔
151

152
    class CommentType(QueryType[Comment]):
4✔
153
        contents = Field()
4✔
154
        target = Field()
4✔
155

156
    class CommentCreateMutation(MutationType[Comment]):
4✔
157
        contents = Input()
4✔
158
        target = Input()
4✔
159

160
    class Query(RootType):
4✔
161
        comments = Entrypoint(CommentType, many=True)
4✔
162

163
    class Mutation(RootType):
4✔
164
        create_comment = Entrypoint(CommentCreateMutation)
4✔
165

166
    undine_settings.SCHEMA = create_schema(query=Query, mutation=Mutation)
4✔
167

168
    query = """
4✔
169
        mutation {
170
            createComment(
171
                input: {
172
                    contents: "Comment"
173
                    target: {
174
                        task: {
175
                            name: "Test Task"
176
                            type: TASK
177
                        }
178
                        project: {
179
                            name: "Test Project"
180
                        }
181
                    }
182
                }
183
            ) {
184
                contents
185
                target {
186
                    __typename
187
                    ... on TaskType {
188
                        name
189
                    }
190
                    ... on ProjectType {
191
                        name
192
                    }
193
                }
194
            }
195
        }
196
    """
197

198
    response = graphql(query)
4✔
199

200
    assert response.errors == [
4✔
201
        {
202
            "message": "OneOf Input Object 'CommentTargetInput' must specify exactly one key.",
203
            "extensions": {"status_code": 400},
204
        }
205
    ]
206

207

208
@pytest.mark.django_db
4✔
209
def test_validation_rules__one_of_input_object__null_key__document(graphql, undine_settings) -> None:
4✔
210
    class TaskType(QueryType[Task]):
4✔
211
        name = Field()
4✔
212

213
    class CommentType(QueryType[Comment]):
4✔
214
        contents = Field()
4✔
215
        target = Field()
4✔
216

217
    class CommentCreateMutation(MutationType[Comment]):
4✔
218
        contents = Input()
4✔
219
        target = Input()
4✔
220

221
    class Query(RootType):
4✔
222
        comments = Entrypoint(CommentType, many=True)
4✔
223

224
    class Mutation(RootType):
4✔
225
        create_comment = Entrypoint(CommentCreateMutation)
4✔
226

227
    undine_settings.SCHEMA = create_schema(query=Query, mutation=Mutation)
4✔
228

229
    query = """
4✔
230
        mutation {
231
            createComment(
232
                input: {
233
                    contents: "Comment"
234
                    target: {
235
                        task: null
236
                    }
237
                }
238
            ) {
239
                contents
240
                target {
241
                    __typename
242
                    ... on TaskType {
243
                        name
244
                    }
245
                }
246
            }
247
        }
248
    """
249

250
    response = graphql(query)
4✔
251

252
    assert response.errors == [
4✔
253
        {
254
            "message": "Field 'CommentTargetInput.task' must be non-null.",
255
            "extensions": {"status_code": 400},
256
        }
257
    ]
258

259

260
@pytest.mark.django_db
4✔
261
def test_validation_rules__one_of_input_object__null_key__document_variable(graphql, undine_settings) -> None:
4✔
262
    class TaskType(QueryType[Task]):
4✔
263
        name = Field()
4✔
264

265
    class CommentType(QueryType[Comment]):
4✔
266
        contents = Field()
4✔
267
        target = Field()
4✔
268

269
    class CommentCreateMutation(MutationType[Comment]):
4✔
270
        contents = Input()
4✔
271
        target = Input()
4✔
272

273
    class Query(RootType):
4✔
274
        comments = Entrypoint(CommentType, many=True)
4✔
275

276
    class Mutation(RootType):
4✔
277
        create_comment = Entrypoint(CommentCreateMutation)
4✔
278

279
    undine_settings.SCHEMA = create_schema(query=Query, mutation=Mutation)
4✔
280

281
    query = """
4✔
282
        mutation ($target: CommentTargetTaskInput!) {
283
            createComment(
284
                input: {
285
                    contents: "Comment"
286
                    target: {
287
                        task: $target
288
                    }
289
                }
290
            ) {
291
                contents
292
                target {
293
                    __typename
294
                    ... on TaskType {
295
                        name
296
                    }
297
                }
298
            }
299
        }
300
    """
301

302
    response = graphql(query, variables={"target": None})
4✔
303

304
    assert response.errors == [
4✔
305
        {
306
            "message": "Variable '$target' of non-null type 'CommentTargetTaskInput!' must not be null.",
307
            "extensions": {"status_code": 400},
308
        }
309
    ]
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc