Skip to content

Commit c67fc48

Browse files
committed
Tests for multipart/form-data request handling
1 parent cbde5b3 commit c67fc48

File tree

2 files changed

+139
-15
lines changed

2 files changed

+139
-15
lines changed

Diff for: tests/schema.py

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
from graphql.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType
1+
from graphql.type.definition import (
2+
GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType,
3+
GraphQLScalarType)
24
from graphql.type.scalars import GraphQLString
35
from graphql.type.schema import GraphQLSchema
46

7+
from flask_graphql.fields import GraphQLFileUpload
8+
59

610
def resolve_raises(*_):
711
raise Exception("Throws!")
@@ -25,13 +29,50 @@ def resolve_raises(*_):
2529
}
2630
)
2731

32+
33+
FileUploadTestResult = GraphQLObjectType(
34+
name='FileUploadTestResult',
35+
fields={
36+
'data': GraphQLField(GraphQLString),
37+
'name': GraphQLField(GraphQLString),
38+
'type': GraphQLField(GraphQLString),
39+
}
40+
)
41+
42+
43+
def to_object(dct):
44+
class MyObject(object):
45+
pass
46+
47+
obj = MyObject()
48+
for key, val in dct.items():
49+
setattr(obj, key, val)
50+
return obj
51+
52+
53+
def resolve_file_upload_test(obj, info, file):
54+
data = file.stream.read().decode()
55+
56+
# Need to return an object, not a dict
57+
return to_object({
58+
'data': data,
59+
'name': file.filename,
60+
'type': file.content_type,
61+
})
62+
63+
2864
MutationRootType = GraphQLObjectType(
2965
name='MutationRoot',
3066
fields={
3167
'writeTest': GraphQLField(
3268
type=QueryRootType,
3369
resolver=lambda *_: QueryRootType
34-
)
70+
),
71+
'fileUploadTest': GraphQLField(
72+
type=FileUploadTestResult,
73+
args={'file': GraphQLArgument(GraphQLFileUpload)},
74+
resolver=resolve_file_upload_test,
75+
),
3576
}
3677
)
3778

Diff for: tests/test_graphqlview.py

+96-13
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
1-
import pytest
21
import json
2+
from io import BytesIO
3+
4+
import pytest
5+
6+
from flask import url_for
7+
from flask_graphql.graphqlview import place_files_in_operations
8+
from werkzeug.test import EnvironBuilder
9+
10+
from .app import create_app
311

412
try:
513
from StringIO import StringIO
614
except ImportError:
715
from io import StringIO
816

17+
18+
919
try:
1020
from urllib import urlencode
1121
except ImportError:
1222
from urllib.parse import urlencode
1323

14-
from .app import create_app
15-
from flask import url_for
1624

1725

1826
@pytest.fixture
@@ -465,18 +473,93 @@ def test_supports_pretty_printing(client):
465473

466474

467475
def test_post_multipart_data(client):
468-
query = 'mutation TestMutation { writeTest { test } }'
476+
query = """
477+
mutation TestMutation($file: FileUpload!) {
478+
fileUploadTest(file: $file) {
479+
data, name, type
480+
}
481+
}
482+
"""
483+
469484
response = client.post(
470485
url_string(),
471-
data= {
472-
'query': query,
473-
'file': (StringIO(), 'text1.txt'),
474-
},
475-
content_type='multipart/form-data'
476-
)
486+
method='POST',
487+
data={
488+
# Form data
489+
'operations': json.dumps({
490+
'query': query,
491+
'variables': {'file': None},
492+
}),
493+
'map': json.dumps({
494+
'0': ['variables.file'],
495+
}),
496+
'0': (BytesIO(b'FILE-DATA-HERE'), 'hello.txt', 'text/plain'),
497+
})
477498

478499
assert response.status_code == 200
479-
assert response_json(response) == {'data': {u'writeTest': {u'test': u'Hello World'}}}
500+
assert response_json(response) == {
501+
'data': {u'fileUploadTest': {
502+
'data': u'FILE-DATA-HERE',
503+
'name': 'hello.txt',
504+
'type': 'text/plain',
505+
}},
506+
}
507+
508+
509+
def test_can_place_file_in_flat_variable():
510+
operations = {
511+
'variables': {'myfile': None},
512+
"query": "QUERY",
513+
}
514+
files_map = {"0": ["variables.myfile"]}
515+
files = {"0": "FILE-0-HERE"}
516+
517+
assert place_files_in_operations(operations, files_map, files) == {
518+
'variables': {'myfile': "FILE-0-HERE"},
519+
"query": "QUERY",
520+
}
521+
522+
523+
def test_can_place_file_in_list_variable():
524+
operations = {
525+
'variables': {'myfile': [None]},
526+
"query": "QUERY",
527+
}
528+
files_map = {"0": ["variables.myfile.0"]}
529+
files = {"0": "FILE-0-HERE"}
530+
531+
assert place_files_in_operations(operations, files_map, files) == {
532+
'variables': {'myfile': ["FILE-0-HERE"]},
533+
"query": "QUERY",
534+
}
535+
536+
537+
def test_can_place_file_in_flat_variable_in_ops_list():
538+
operations = [{
539+
'variables': {'myfile': None},
540+
"query": "QUERY",
541+
}]
542+
files_map = {"0": ["0.variables.myfile"]}
543+
files = {"0": "FILE-0-HERE"}
544+
545+
assert place_files_in_operations(operations, files_map, files) == [{
546+
'variables': {'myfile': "FILE-0-HERE"},
547+
"query": "QUERY",
548+
}]
549+
550+
551+
def test_can_place_file_in_list_variable_in_ops_list():
552+
operations = [{
553+
'variables': {'myfile': [None]},
554+
"query": "QUERY",
555+
}]
556+
files_map = {"0": ["0.variables.myfile.0"]}
557+
files = {"0": "FILE-0-HERE"}
558+
559+
assert place_files_in_operations(operations, files_map, files) == [{
560+
'variables': {'myfile': ["FILE-0-HERE"]},
561+
"query": "QUERY",
562+
}]
480563

481564

482565
@pytest.mark.parametrize('app', [create_app(batch=True)])
@@ -514,8 +597,8 @@ def test_batch_supports_post_json_query_with_json_variables(client):
514597
# 'id': 1,
515598
'data': {'test': "Hello Dolly"}
516599
}]
517-
518-
600+
601+
519602
@pytest.mark.parametrize('app', [create_app(batch=True)])
520603
def test_batch_allows_post_with_operation_name(client):
521604
response = client.post(

0 commit comments

Comments
 (0)