Skip to content

Commit 84583cf

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

File tree

2 files changed

+146
-15
lines changed

2 files changed

+146
-15
lines changed

Diff for: tests/schema.py

+50-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
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

@@ -25,13 +27,59 @@ def resolve_raises(*_):
2527
}
2628
)
2729

30+
31+
GraphQLFileUpload = GraphQLScalarType(
32+
name='FileUpload',
33+
description='File upload',
34+
serialize=lambda x: None,
35+
parse_value=lambda value: value,
36+
parse_literal=lambda node: None,
37+
)
38+
39+
40+
FileUploadTestResult = GraphQLObjectType(
41+
name='FileUploadTestResult',
42+
fields={
43+
'data': GraphQLField(GraphQLString),
44+
'name': GraphQLField(GraphQLString),
45+
'type': GraphQLField(GraphQLString),
46+
}
47+
)
48+
49+
50+
def to_object(dct):
51+
class MyObject(object):
52+
pass
53+
54+
obj = MyObject()
55+
for key, val in dct.items():
56+
setattr(obj, key, val)
57+
return obj
58+
59+
60+
def resolve_file_upload_test(obj, info, file):
61+
data = file.stream.read().decode()
62+
63+
# Need to return an object, not a dict
64+
return to_object({
65+
'data': data,
66+
'name': file.filename,
67+
'type': file.content_type,
68+
})
69+
70+
2871
MutationRootType = GraphQLObjectType(
2972
name='MutationRoot',
3073
fields={
3174
'writeTest': GraphQLField(
3275
type=QueryRootType,
3376
resolver=lambda *_: QueryRootType
34-
)
77+
),
78+
'fileUploadTest': GraphQLField(
79+
type=FileUploadTestResult,
80+
args={'file': GraphQLArgument(GraphQLFileUpload)},
81+
resolver=resolve_file_upload_test,
82+
),
3583
}
3684
)
3785

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)