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

tests/schema.py

Lines changed: 50 additions & 2 deletions
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

tests/test_graphqlview.py

Lines changed: 96 additions & 13 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy