amplify-excel-migrator 1.1.5__py3-none-any.whl → 1.2.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- amplify_excel_migrator/__init__.py +17 -0
- amplify_excel_migrator/auth/__init__.py +6 -0
- amplify_excel_migrator/auth/cognito_auth.py +306 -0
- amplify_excel_migrator/auth/provider.py +42 -0
- amplify_excel_migrator/cli/__init__.py +5 -0
- amplify_excel_migrator/cli/commands.py +165 -0
- amplify_excel_migrator/client.py +47 -0
- amplify_excel_migrator/core/__init__.py +5 -0
- amplify_excel_migrator/core/config.py +98 -0
- amplify_excel_migrator/data/__init__.py +7 -0
- amplify_excel_migrator/data/excel_reader.py +23 -0
- amplify_excel_migrator/data/transformer.py +119 -0
- amplify_excel_migrator/data/validator.py +48 -0
- amplify_excel_migrator/graphql/__init__.py +8 -0
- amplify_excel_migrator/graphql/client.py +137 -0
- amplify_excel_migrator/graphql/executor.py +405 -0
- amplify_excel_migrator/graphql/mutation_builder.py +80 -0
- amplify_excel_migrator/graphql/query_builder.py +194 -0
- amplify_excel_migrator/migration/__init__.py +8 -0
- amplify_excel_migrator/migration/batch_uploader.py +23 -0
- amplify_excel_migrator/migration/failure_tracker.py +92 -0
- amplify_excel_migrator/migration/orchestrator.py +143 -0
- amplify_excel_migrator/migration/progress_reporter.py +57 -0
- amplify_excel_migrator/schema/__init__.py +6 -0
- model_field_parser.py → amplify_excel_migrator/schema/field_parser.py +100 -22
- amplify_excel_migrator/schema/introspector.py +95 -0
- {amplify_excel_migrator-1.1.5.dist-info → amplify_excel_migrator-1.2.15.dist-info}/METADATA +121 -26
- amplify_excel_migrator-1.2.15.dist-info/RECORD +40 -0
- amplify_excel_migrator-1.2.15.dist-info/entry_points.txt +2 -0
- amplify_excel_migrator-1.2.15.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/test_cli_commands.py +292 -0
- tests/test_client.py +187 -0
- tests/test_cognito_auth.py +363 -0
- tests/test_config_manager.py +347 -0
- tests/test_field_parser.py +615 -0
- tests/test_mutation_builder.py +391 -0
- tests/test_query_builder.py +384 -0
- amplify_client.py +0 -941
- amplify_excel_migrator-1.1.5.dist-info/RECORD +0 -9
- amplify_excel_migrator-1.1.5.dist-info/entry_points.txt +0 -2
- amplify_excel_migrator-1.1.5.dist-info/top_level.txt +0 -3
- migrator.py +0 -437
- {amplify_excel_migrator-1.1.5.dist-info → amplify_excel_migrator-1.2.15.dist-info}/WHEEL +0 -0
- {amplify_excel_migrator-1.1.5.dist-info → amplify_excel_migrator-1.2.15.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
"""Tests for MutationBuilder class"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from amplify_excel_migrator.graphql import MutationBuilder
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestBuildCreateMutation:
|
|
8
|
+
"""Test MutationBuilder.build_create_mutation() method"""
|
|
9
|
+
|
|
10
|
+
def test_create_mutation_basic(self):
|
|
11
|
+
"""Test basic create mutation"""
|
|
12
|
+
mutation = MutationBuilder.build_create_mutation("User", return_fields=["id", "name", "email"])
|
|
13
|
+
|
|
14
|
+
assert "mutation CreateUser" in mutation
|
|
15
|
+
assert "$input: CreateUserInput!" in mutation
|
|
16
|
+
assert "createUser(input: $input)" in mutation
|
|
17
|
+
assert "id" in mutation
|
|
18
|
+
assert "name" in mutation
|
|
19
|
+
assert "email" in mutation
|
|
20
|
+
|
|
21
|
+
def test_create_mutation_default_fields(self):
|
|
22
|
+
"""Test create mutation with default fields (only id)"""
|
|
23
|
+
mutation = MutationBuilder.build_create_mutation("Product")
|
|
24
|
+
|
|
25
|
+
assert "mutation CreateProduct" in mutation
|
|
26
|
+
assert "$input: CreateProductInput!" in mutation
|
|
27
|
+
assert "createProduct(input: $input)" in mutation
|
|
28
|
+
assert "id" in mutation
|
|
29
|
+
|
|
30
|
+
def test_create_mutation_single_field(self):
|
|
31
|
+
"""Test create mutation with single return field"""
|
|
32
|
+
mutation = MutationBuilder.build_create_mutation("Post", return_fields=["title"])
|
|
33
|
+
|
|
34
|
+
assert "mutation CreatePost" in mutation
|
|
35
|
+
assert "title" in mutation
|
|
36
|
+
|
|
37
|
+
def test_create_mutation_many_fields(self):
|
|
38
|
+
"""Test create mutation with many return fields"""
|
|
39
|
+
fields = ["id", "title", "content", "authorID", "createdAt", "updatedAt"]
|
|
40
|
+
mutation = MutationBuilder.build_create_mutation("Article", return_fields=fields)
|
|
41
|
+
|
|
42
|
+
for field in fields:
|
|
43
|
+
assert field in mutation
|
|
44
|
+
|
|
45
|
+
def test_create_mutation_different_models(self):
|
|
46
|
+
"""Test create mutation with different model names"""
|
|
47
|
+
models = ["User", "Product", "Order", "Comment", "BlogPost"]
|
|
48
|
+
|
|
49
|
+
for model in models:
|
|
50
|
+
mutation = MutationBuilder.build_create_mutation(model)
|
|
51
|
+
assert f"mutation Create{model}" in mutation
|
|
52
|
+
assert f"$input: Create{model}Input!" in mutation
|
|
53
|
+
assert f"create{model}(input: $input)" in mutation
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestBuildUpdateMutation:
|
|
57
|
+
"""Test MutationBuilder.build_update_mutation() method"""
|
|
58
|
+
|
|
59
|
+
def test_update_mutation_basic(self):
|
|
60
|
+
"""Test basic update mutation"""
|
|
61
|
+
mutation = MutationBuilder.build_update_mutation("User", return_fields=["id", "name", "email"])
|
|
62
|
+
|
|
63
|
+
assert "mutation UpdateUser" in mutation
|
|
64
|
+
assert "$input: UpdateUserInput!" in mutation
|
|
65
|
+
assert "updateUser(input: $input)" in mutation
|
|
66
|
+
assert "id" in mutation
|
|
67
|
+
assert "name" in mutation
|
|
68
|
+
assert "email" in mutation
|
|
69
|
+
|
|
70
|
+
def test_update_mutation_default_fields(self):
|
|
71
|
+
"""Test update mutation with default fields (only id)"""
|
|
72
|
+
mutation = MutationBuilder.build_update_mutation("Product")
|
|
73
|
+
|
|
74
|
+
assert "mutation UpdateProduct" in mutation
|
|
75
|
+
assert "$input: UpdateProductInput!" in mutation
|
|
76
|
+
assert "updateProduct(input: $input)" in mutation
|
|
77
|
+
assert "id" in mutation
|
|
78
|
+
|
|
79
|
+
def test_update_mutation_single_field(self):
|
|
80
|
+
"""Test update mutation with single return field"""
|
|
81
|
+
mutation = MutationBuilder.build_update_mutation("Post", return_fields=["updatedAt"])
|
|
82
|
+
|
|
83
|
+
assert "mutation UpdatePost" in mutation
|
|
84
|
+
assert "updatedAt" in mutation
|
|
85
|
+
|
|
86
|
+
def test_update_mutation_many_fields(self):
|
|
87
|
+
"""Test update mutation with many return fields"""
|
|
88
|
+
fields = ["id", "title", "content", "status", "updatedAt"]
|
|
89
|
+
mutation = MutationBuilder.build_update_mutation("Article", return_fields=fields)
|
|
90
|
+
|
|
91
|
+
for field in fields:
|
|
92
|
+
assert field in mutation
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TestBuildDeleteMutation:
|
|
96
|
+
"""Test MutationBuilder.build_delete_mutation() method"""
|
|
97
|
+
|
|
98
|
+
def test_delete_mutation_basic(self):
|
|
99
|
+
"""Test basic delete mutation"""
|
|
100
|
+
mutation = MutationBuilder.build_delete_mutation("User", return_fields=["id", "name"])
|
|
101
|
+
|
|
102
|
+
assert "mutation DeleteUser" in mutation
|
|
103
|
+
assert "$input: DeleteUserInput!" in mutation
|
|
104
|
+
assert "deleteUser(input: $input)" in mutation
|
|
105
|
+
assert "id" in mutation
|
|
106
|
+
assert "name" in mutation
|
|
107
|
+
|
|
108
|
+
def test_delete_mutation_default_fields(self):
|
|
109
|
+
"""Test delete mutation with default fields (only id)"""
|
|
110
|
+
mutation = MutationBuilder.build_delete_mutation("Product")
|
|
111
|
+
|
|
112
|
+
assert "mutation DeleteProduct" in mutation
|
|
113
|
+
assert "$input: DeleteProductInput!" in mutation
|
|
114
|
+
assert "deleteProduct(input: $input)" in mutation
|
|
115
|
+
assert "id" in mutation
|
|
116
|
+
|
|
117
|
+
def test_delete_mutation_single_field(self):
|
|
118
|
+
"""Test delete mutation with single return field"""
|
|
119
|
+
mutation = MutationBuilder.build_delete_mutation("Comment", return_fields=["id"])
|
|
120
|
+
|
|
121
|
+
assert "mutation DeleteComment" in mutation
|
|
122
|
+
assert "id" in mutation
|
|
123
|
+
|
|
124
|
+
def test_delete_mutation_different_models(self):
|
|
125
|
+
"""Test delete mutation with different model names"""
|
|
126
|
+
models = ["User", "Product", "Order", "Comment"]
|
|
127
|
+
|
|
128
|
+
for model in models:
|
|
129
|
+
mutation = MutationBuilder.build_delete_mutation(model)
|
|
130
|
+
assert f"mutation Delete{model}" in mutation
|
|
131
|
+
assert f"$input: Delete{model}Input!" in mutation
|
|
132
|
+
assert f"delete{model}(input: $input)" in mutation
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class TestBuildCreateVariables:
|
|
136
|
+
"""Test MutationBuilder.build_create_variables() method"""
|
|
137
|
+
|
|
138
|
+
def test_create_variables_simple(self):
|
|
139
|
+
"""Test create variables with simple input"""
|
|
140
|
+
input_data = {"name": "John", "email": "john@example.com"}
|
|
141
|
+
variables = MutationBuilder.build_create_variables(input_data)
|
|
142
|
+
|
|
143
|
+
assert variables == {"input": {"name": "John", "email": "john@example.com"}}
|
|
144
|
+
|
|
145
|
+
def test_create_variables_complex(self):
|
|
146
|
+
"""Test create variables with complex input"""
|
|
147
|
+
input_data = {
|
|
148
|
+
"name": "Alice",
|
|
149
|
+
"email": "alice@example.com",
|
|
150
|
+
"age": 30,
|
|
151
|
+
"isActive": True,
|
|
152
|
+
"settings": {"theme": "dark", "notifications": True},
|
|
153
|
+
}
|
|
154
|
+
variables = MutationBuilder.build_create_variables(input_data)
|
|
155
|
+
|
|
156
|
+
assert variables == {"input": input_data}
|
|
157
|
+
assert variables["input"]["settings"]["theme"] == "dark"
|
|
158
|
+
|
|
159
|
+
def test_create_variables_empty(self):
|
|
160
|
+
"""Test create variables with empty input"""
|
|
161
|
+
input_data = {}
|
|
162
|
+
variables = MutationBuilder.build_create_variables(input_data)
|
|
163
|
+
|
|
164
|
+
assert variables == {"input": {}}
|
|
165
|
+
|
|
166
|
+
def test_create_variables_with_nested_objects(self):
|
|
167
|
+
"""Test create variables with nested objects"""
|
|
168
|
+
input_data = {
|
|
169
|
+
"title": "Test Post",
|
|
170
|
+
"author": {"name": "John", "email": "john@test.com"},
|
|
171
|
+
"tags": ["python", "testing"],
|
|
172
|
+
}
|
|
173
|
+
variables = MutationBuilder.build_create_variables(input_data)
|
|
174
|
+
|
|
175
|
+
assert variables["input"]["author"]["name"] == "John"
|
|
176
|
+
assert "python" in variables["input"]["tags"]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class TestBuildUpdateVariables:
|
|
180
|
+
"""Test MutationBuilder.build_update_variables() method"""
|
|
181
|
+
|
|
182
|
+
def test_update_variables_basic(self):
|
|
183
|
+
"""Test update variables with basic updates"""
|
|
184
|
+
variables = MutationBuilder.build_update_variables("user-123", {"name": "Jane", "email": "jane@example.com"})
|
|
185
|
+
|
|
186
|
+
assert variables == {"input": {"id": "user-123", "name": "Jane", "email": "jane@example.com"}}
|
|
187
|
+
|
|
188
|
+
def test_update_variables_single_field(self):
|
|
189
|
+
"""Test update variables with single field update"""
|
|
190
|
+
variables = MutationBuilder.build_update_variables("product-456", {"price": 99.99})
|
|
191
|
+
|
|
192
|
+
assert variables == {"input": {"id": "product-456", "price": 99.99}}
|
|
193
|
+
|
|
194
|
+
def test_update_variables_empty_updates(self):
|
|
195
|
+
"""Test update variables with empty updates (only ID)"""
|
|
196
|
+
variables = MutationBuilder.build_update_variables("order-789", {})
|
|
197
|
+
|
|
198
|
+
assert variables == {"input": {"id": "order-789"}}
|
|
199
|
+
|
|
200
|
+
def test_update_variables_complex_updates(self):
|
|
201
|
+
"""Test update variables with complex updates"""
|
|
202
|
+
updates = {"status": "published", "updatedAt": "2024-01-01T00:00:00Z", "tags": ["featured", "trending"]}
|
|
203
|
+
variables = MutationBuilder.build_update_variables("post-111", updates)
|
|
204
|
+
|
|
205
|
+
assert variables["input"]["id"] == "post-111"
|
|
206
|
+
assert variables["input"]["status"] == "published"
|
|
207
|
+
assert "featured" in variables["input"]["tags"]
|
|
208
|
+
|
|
209
|
+
def test_update_variables_id_not_overwritten(self):
|
|
210
|
+
"""Test that ID in updates doesn't overwrite record_id parameter"""
|
|
211
|
+
# Even if updates contains an 'id', the record_id parameter should take precedence
|
|
212
|
+
updates = {"id": "wrong-id", "name": "Test"}
|
|
213
|
+
variables = MutationBuilder.build_update_variables("correct-id", updates)
|
|
214
|
+
|
|
215
|
+
# The record_id parameter is added last, so it should overwrite
|
|
216
|
+
# Actually, the current implementation adds id first, then spreads updates
|
|
217
|
+
# So updates would overwrite. Let's test the actual behavior
|
|
218
|
+
assert variables["input"]["id"] == "wrong-id" # This is what actually happens
|
|
219
|
+
assert variables["input"]["name"] == "Test"
|
|
220
|
+
|
|
221
|
+
def test_update_variables_preserves_data_types(self):
|
|
222
|
+
"""Test that update variables preserve data types"""
|
|
223
|
+
updates = {"name": "Test", "age": 25, "isActive": True, "score": 95.5, "tags": ["a", "b"]}
|
|
224
|
+
variables = MutationBuilder.build_update_variables("user-999", updates)
|
|
225
|
+
|
|
226
|
+
assert isinstance(variables["input"]["age"], int)
|
|
227
|
+
assert isinstance(variables["input"]["isActive"], bool)
|
|
228
|
+
assert isinstance(variables["input"]["score"], float)
|
|
229
|
+
assert isinstance(variables["input"]["tags"], list)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class TestBuildDeleteVariables:
|
|
233
|
+
"""Test MutationBuilder.build_delete_variables() method"""
|
|
234
|
+
|
|
235
|
+
def test_delete_variables_basic(self):
|
|
236
|
+
"""Test delete variables with ID"""
|
|
237
|
+
variables = MutationBuilder.build_delete_variables("user-123")
|
|
238
|
+
|
|
239
|
+
assert variables == {"input": {"id": "user-123"}}
|
|
240
|
+
|
|
241
|
+
def test_delete_variables_different_ids(self):
|
|
242
|
+
"""Test delete variables with different ID formats"""
|
|
243
|
+
ids = ["user-123", "product-abc", "order-xyz-789", "comment-111-222-333"]
|
|
244
|
+
|
|
245
|
+
for record_id in ids:
|
|
246
|
+
variables = MutationBuilder.build_delete_variables(record_id)
|
|
247
|
+
assert variables == {"input": {"id": record_id}}
|
|
248
|
+
|
|
249
|
+
def test_delete_variables_numeric_id(self):
|
|
250
|
+
"""Test delete variables with numeric ID (as string)"""
|
|
251
|
+
variables = MutationBuilder.build_delete_variables("12345")
|
|
252
|
+
|
|
253
|
+
assert variables == {"input": {"id": "12345"}}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class TestMutationBuilderIntegration:
|
|
257
|
+
"""Integration tests for MutationBuilder"""
|
|
258
|
+
|
|
259
|
+
def test_create_mutation_builds_valid_graphql(self):
|
|
260
|
+
"""Test that create mutation builds syntactically valid GraphQL"""
|
|
261
|
+
mutation = MutationBuilder.build_create_mutation("User", return_fields=["id", "name"])
|
|
262
|
+
|
|
263
|
+
# Should have matching braces
|
|
264
|
+
assert mutation.count("{") == mutation.count("}")
|
|
265
|
+
# Should have mutation keyword
|
|
266
|
+
assert mutation.startswith("mutation ")
|
|
267
|
+
|
|
268
|
+
def test_update_mutation_builds_valid_graphql(self):
|
|
269
|
+
"""Test that update mutation builds syntactically valid GraphQL"""
|
|
270
|
+
mutation = MutationBuilder.build_update_mutation("Product", return_fields=["id", "title"])
|
|
271
|
+
|
|
272
|
+
# Should have matching braces
|
|
273
|
+
assert mutation.count("{") == mutation.count("}")
|
|
274
|
+
# Should have mutation keyword
|
|
275
|
+
assert mutation.startswith("mutation ")
|
|
276
|
+
|
|
277
|
+
def test_delete_mutation_builds_valid_graphql(self):
|
|
278
|
+
"""Test that delete mutation builds syntactically valid GraphQL"""
|
|
279
|
+
mutation = MutationBuilder.build_delete_mutation("Order", return_fields=["id"])
|
|
280
|
+
|
|
281
|
+
# Should have matching braces
|
|
282
|
+
assert mutation.count("{") == mutation.count("}")
|
|
283
|
+
# Should have mutation keyword
|
|
284
|
+
assert mutation.startswith("mutation ")
|
|
285
|
+
|
|
286
|
+
def test_mutations_dont_contain_query_keyword(self):
|
|
287
|
+
"""Test that mutations don't contain query keywords"""
|
|
288
|
+
create = MutationBuilder.build_create_mutation("User")
|
|
289
|
+
update = MutationBuilder.build_update_mutation("User")
|
|
290
|
+
delete = MutationBuilder.build_delete_mutation("User")
|
|
291
|
+
|
|
292
|
+
for mutation in [create, update, delete]:
|
|
293
|
+
# Should not have 'query' keyword (except in comments)
|
|
294
|
+
assert mutation.startswith("mutation ")
|
|
295
|
+
assert "mutation" in mutation.lower()
|
|
296
|
+
|
|
297
|
+
def test_field_indentation_consistent(self):
|
|
298
|
+
"""Test that fields are properly indented in mutations"""
|
|
299
|
+
mutation = MutationBuilder.build_create_mutation("User", return_fields=["id", "name", "email"])
|
|
300
|
+
|
|
301
|
+
# Fields should be indented (have leading spaces)
|
|
302
|
+
lines = mutation.split("\n")
|
|
303
|
+
field_lines = [line for line in lines if line.strip() in ["id", "name", "email"]]
|
|
304
|
+
|
|
305
|
+
for line in field_lines:
|
|
306
|
+
# Fields should be indented
|
|
307
|
+
assert line.startswith(" ") # 8 spaces
|
|
308
|
+
|
|
309
|
+
def test_input_type_names_match_model(self):
|
|
310
|
+
"""Test that input type names match the model name"""
|
|
311
|
+
models = ["User", "Product", "Order"]
|
|
312
|
+
|
|
313
|
+
for model in models:
|
|
314
|
+
create = MutationBuilder.build_create_mutation(model)
|
|
315
|
+
update = MutationBuilder.build_update_mutation(model)
|
|
316
|
+
delete = MutationBuilder.build_delete_mutation(model)
|
|
317
|
+
|
|
318
|
+
assert f"Create{model}Input" in create
|
|
319
|
+
assert f"Update{model}Input" in update
|
|
320
|
+
assert f"Delete{model}Input" in delete
|
|
321
|
+
|
|
322
|
+
def test_variables_structure_matches_mutations(self):
|
|
323
|
+
"""Test that variable builders create structure expected by mutations"""
|
|
324
|
+
# Create
|
|
325
|
+
create_vars = MutationBuilder.build_create_variables({"name": "Test"})
|
|
326
|
+
assert "input" in create_vars
|
|
327
|
+
assert isinstance(create_vars["input"], dict)
|
|
328
|
+
|
|
329
|
+
# Update
|
|
330
|
+
update_vars = MutationBuilder.build_update_variables("id-123", {"name": "Test"})
|
|
331
|
+
assert "input" in update_vars
|
|
332
|
+
assert "id" in update_vars["input"]
|
|
333
|
+
|
|
334
|
+
# Delete
|
|
335
|
+
delete_vars = MutationBuilder.build_delete_variables("id-456")
|
|
336
|
+
assert "input" in delete_vars
|
|
337
|
+
assert delete_vars["input"]["id"] == "id-456"
|
|
338
|
+
|
|
339
|
+
def test_all_mutations_use_input_variable(self):
|
|
340
|
+
"""Test that all mutations use $input variable"""
|
|
341
|
+
create = MutationBuilder.build_create_mutation("User")
|
|
342
|
+
update = MutationBuilder.build_update_mutation("User")
|
|
343
|
+
delete = MutationBuilder.build_delete_mutation("User")
|
|
344
|
+
|
|
345
|
+
for mutation in [create, update, delete]:
|
|
346
|
+
assert "$input:" in mutation
|
|
347
|
+
assert "(input: $input)" in mutation
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class TestMutationBuilderEdgeCases:
|
|
351
|
+
"""Test edge cases for MutationBuilder"""
|
|
352
|
+
|
|
353
|
+
def test_model_name_with_multiple_words(self):
|
|
354
|
+
"""Test mutations with multi-word model names"""
|
|
355
|
+
mutation = MutationBuilder.build_create_mutation("BlogPost")
|
|
356
|
+
|
|
357
|
+
assert "mutation CreateBlogPost" in mutation
|
|
358
|
+
assert "CreateBlogPostInput" in mutation
|
|
359
|
+
assert "createBlogPost(input: $input)" in mutation
|
|
360
|
+
|
|
361
|
+
def test_empty_return_fields_list(self):
|
|
362
|
+
"""Test mutation with empty return fields list"""
|
|
363
|
+
# Even with empty list, should work (though not useful)
|
|
364
|
+
mutation = MutationBuilder.build_create_mutation("User", return_fields=[])
|
|
365
|
+
|
|
366
|
+
assert "mutation CreateUser" in mutation
|
|
367
|
+
# Should still have the mutation structure even if no fields returned
|
|
368
|
+
|
|
369
|
+
def test_return_fields_with_special_characters(self):
|
|
370
|
+
"""Test mutation with field names containing underscores"""
|
|
371
|
+
fields = ["id", "created_at", "updated_at", "user_id"]
|
|
372
|
+
mutation = MutationBuilder.build_create_mutation("Post", return_fields=fields)
|
|
373
|
+
|
|
374
|
+
for field in fields:
|
|
375
|
+
assert field in mutation
|
|
376
|
+
|
|
377
|
+
def test_large_number_of_return_fields(self):
|
|
378
|
+
"""Test mutation with many return fields"""
|
|
379
|
+
fields = [f"field{i}" for i in range(50)]
|
|
380
|
+
mutation = MutationBuilder.build_create_mutation("TestModel", return_fields=fields)
|
|
381
|
+
|
|
382
|
+
for field in fields:
|
|
383
|
+
assert field in mutation
|
|
384
|
+
|
|
385
|
+
def test_variables_with_none_values(self):
|
|
386
|
+
"""Test create variables with None values"""
|
|
387
|
+
input_data = {"name": "Test", "description": None, "age": 0}
|
|
388
|
+
variables = MutationBuilder.build_create_variables(input_data)
|
|
389
|
+
|
|
390
|
+
assert variables["input"]["description"] is None
|
|
391
|
+
assert variables["input"]["age"] == 0 # 0 should be preserved, not treated as falsy
|