2016-09-18 02:29:00 +03:00
|
|
|
import json
|
|
|
|
|
2016-09-20 08:15:10 +03:00
|
|
|
import pytest
|
|
|
|
|
2020-12-31 02:37:57 +03:00
|
|
|
from mock import patch
|
|
|
|
|
|
|
|
from django.db import connection
|
|
|
|
|
|
|
|
from graphene_django.settings import graphene_settings
|
|
|
|
|
|
|
|
from .models import Pet
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
try:
|
|
|
|
from urllib import urlencode
|
|
|
|
except ImportError:
|
|
|
|
from urllib.parse import urlencode
|
2016-09-18 02:29:00 +03:00
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
|
2018-07-20 02:51:33 +03:00
|
|
|
def url_string(string="/graphql", **url_params):
|
2016-09-20 08:04:23 +03:00
|
|
|
if url_params:
|
2018-07-20 02:51:33 +03:00
|
|
|
string += "?" + urlencode(url_params)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
return string
|
|
|
|
|
|
|
|
|
2016-10-31 13:56:51 +03:00
|
|
|
def batch_url_string(**url_params):
|
2018-07-20 02:51:33 +03:00
|
|
|
return url_string("/graphql/batch", **url_params)
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def response_json(response):
|
2016-09-18 02:29:00 +03:00
|
|
|
return json.loads(response.content.decode())
|
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
j = lambda **kwargs: json.dumps(kwargs)
|
2016-10-31 13:56:51 +03:00
|
|
|
jl = lambda **kwargs: json.dumps([kwargs])
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_graphiql_is_enabled(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(), HTTP_ACCEPT="text/html")
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response["Content-Type"].split(";")[0] == "text/html"
|
|
|
|
|
2017-12-12 05:08:42 +03:00
|
|
|
|
|
|
|
def test_qfactor_graphiql(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(query="{test}"),
|
|
|
|
HTTP_ACCEPT="application/json;q=0.8, text/html;q=0.9",
|
|
|
|
)
|
2017-12-12 05:08:42 +03:00
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response["Content-Type"].split(";")[0] == "text/html"
|
|
|
|
|
2017-12-12 05:08:42 +03:00
|
|
|
|
|
|
|
def test_qfactor_json(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(query="{test}"),
|
|
|
|
HTTP_ACCEPT="text/html;q=0.8, application/json;q=0.9",
|
|
|
|
)
|
2017-12-12 05:08:42 +03:00
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response["Content-Type"].split(";")[0] == "application/json"
|
|
|
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_get_with_query_param(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{test}"))
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_get_with_variable_values(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables=json.dumps({"who": "Dolly"}),
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_get_with_operation_name(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
query helloYou { test(who: "You"), ...shared }
|
|
|
|
query helloWorld { test(who: "World"), ...shared }
|
|
|
|
query helloDolly { test(who: "Dolly"), ...shared }
|
|
|
|
fragment shared on QueryRoot {
|
|
|
|
shared: test(who: "Everyone")
|
|
|
|
}
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
operationName="helloWorld",
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
2016-09-18 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_reports_validation_errors(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{ test, unknownOne, unknownTwo }"))
|
2016-09-18 02:29:00 +03:00
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [
|
2016-09-20 08:04:23 +03:00
|
|
|
{
|
2020-05-09 14:13:47 +03:00
|
|
|
"message": "Cannot query field 'unknownOne' on type 'QueryRoot'.",
|
2018-07-20 02:51:33 +03:00
|
|
|
"locations": [{"line": 1, "column": 9}],
|
2020-05-09 14:13:47 +03:00
|
|
|
"path": None,
|
2016-09-20 08:04:23 +03:00
|
|
|
},
|
|
|
|
{
|
2020-05-09 14:13:47 +03:00
|
|
|
"message": "Cannot query field 'unknownTwo' on type 'QueryRoot'.",
|
2018-07-20 02:51:33 +03:00
|
|
|
"locations": [{"line": 1, "column": 21}],
|
2020-05-09 14:13:47 +03:00
|
|
|
"path": None,
|
2018-07-20 02:51:33 +03:00
|
|
|
},
|
2016-09-20 08:04:23 +03:00
|
|
|
]
|
|
|
|
}
|
2016-09-18 02:29:00 +03:00
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
def test_errors_when_missing_operation_name(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
query TestQuery { test }
|
|
|
|
mutation TestMutation { writeTest { test } }
|
2018-07-20 02:51:33 +03:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [
|
2016-09-20 08:04:23 +03:00
|
|
|
{
|
2020-05-09 14:13:47 +03:00
|
|
|
"message": "Must provide operation name if query contains multiple operations.",
|
|
|
|
"locations": None,
|
|
|
|
"path": None,
|
2016-09-18 02:29:00 +03:00
|
|
|
}
|
2016-09-20 08:04:23 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_errors_when_sending_a_mutation_via_get(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
mutation TestMutation { writeTest { test } }
|
2018-07-20 02:51:33 +03:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 405
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [
|
|
|
|
{"message": "Can only perform a mutation operation from a POST request."}
|
2016-09-20 08:04:23 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_errors_when_selecting_a_mutation_within_a_get(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
query TestQuery { test }
|
|
|
|
mutation TestMutation { writeTest { test } }
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
operationName="TestMutation",
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 405
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [
|
|
|
|
{"message": "Can only perform a mutation operation from a POST request."}
|
2016-09-20 08:04:23 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_allows_mutation_to_exist_within_a_get(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
query TestQuery { test }
|
|
|
|
mutation TestMutation { writeTest { test } }
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
operationName="TestQuery",
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_post_with_json_encoding(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(url_string(), j(query="{test}"), "application/json")
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
2016-10-31 13:56:51 +03:00
|
|
|
def test_batch_allows_post_with_json_encoding(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
batch_url_string(), jl(id=1, query="{test}"), "application/json"
|
|
|
|
)
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == [
|
|
|
|
{"id": 1, "data": {"test": "Hello World"}, "status": 200}
|
|
|
|
]
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
|
2017-02-20 12:08:42 +03:00
|
|
|
def test_batch_fails_if_is_empty(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(batch_url_string(), "[]", "application/json")
|
2017-02-20 12:08:42 +03:00
|
|
|
|
2017-02-20 12:15:13 +03:00
|
|
|
assert response.status_code == 400
|
2017-02-20 12:08:42 +03:00
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "Received an empty list in the batch request."}]
|
2017-02-20 12:08:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_allows_sending_a_mutation_via_post(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
j(query="mutation TestMutation { writeTest { test } }"),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"writeTest": {"test": "Hello World"}}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_post_with_url_encoding(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
urlencode(dict(query="{test}")),
|
|
|
|
"application/x-www-form-urlencoded",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_supports_post_json_query_with_string_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
j(
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables=json.dumps({"who": "Dolly"}),
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
def test_batch_supports_post_json_query_with_string_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
batch_url_string(),
|
|
|
|
jl(
|
|
|
|
id=1,
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables=json.dumps({"who": "Dolly"}),
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == [
|
|
|
|
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
|
|
|
]
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_supports_post_json_query_with_json_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
j(
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables={"who": "Dolly"},
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
2016-10-31 13:56:51 +03:00
|
|
|
def test_batch_supports_post_json_query_with_json_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
batch_url_string(),
|
|
|
|
jl(
|
|
|
|
id=1,
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables={"who": "Dolly"},
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == [
|
|
|
|
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
|
|
|
]
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_supports_post_url_encoded_query_with_string_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
urlencode(
|
|
|
|
dict(
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }",
|
|
|
|
variables=json.dumps({"who": "Dolly"}),
|
|
|
|
)
|
|
|
|
),
|
|
|
|
"application/x-www-form-urlencoded",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_supports_post_json_quey_with_get_variable_values(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(variables=json.dumps({"who": "Dolly"})),
|
|
|
|
j(query="query helloWho($who: String){ test(who: $who) }"),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_post_url_encoded_query_with_get_variable_values(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(variables=json.dumps({"who": "Dolly"})),
|
|
|
|
urlencode(dict(query="query helloWho($who: String){ test(who: $who) }")),
|
|
|
|
"application/x-www-form-urlencoded",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_supports_post_raw_text_query_with_get_variable_values(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(variables=json.dumps({"who": "Dolly"})),
|
|
|
|
"query helloWho($who: String){ test(who: $who) }",
|
|
|
|
"application/graphql",
|
2016-09-20 08:04:23 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_allows_post_with_operation_name(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(),
|
|
|
|
j(
|
|
|
|
query="""
|
2016-09-20 08:04:23 +03:00
|
|
|
query helloYou { test(who: "You"), ...shared }
|
|
|
|
query helloWorld { test(who: "World"), ...shared }
|
|
|
|
query helloDolly { test(who: "Dolly"), ...shared }
|
|
|
|
fragment shared on QueryRoot {
|
|
|
|
shared: test(who: "Everyone")
|
|
|
|
}
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
operationName="helloWorld",
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
2016-09-18 02:29:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-31 13:56:51 +03:00
|
|
|
def test_batch_allows_post_with_operation_name(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
batch_url_string(),
|
|
|
|
jl(
|
|
|
|
id=1,
|
|
|
|
query="""
|
2016-10-31 13:56:51 +03:00
|
|
|
query helloYou { test(who: "You"), ...shared }
|
|
|
|
query helloWorld { test(who: "World"), ...shared }
|
|
|
|
query helloDolly { test(who: "Dolly"), ...shared }
|
|
|
|
fragment shared on QueryRoot {
|
|
|
|
shared: test(who: "Everyone")
|
|
|
|
}
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
operationName="helloWorld",
|
|
|
|
),
|
|
|
|
"application/json",
|
|
|
|
)
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == [
|
|
|
|
{
|
|
|
|
"id": 1,
|
|
|
|
"data": {"test": "Hello World", "shared": "Hello Everyone"},
|
|
|
|
"status": 200,
|
|
|
|
}
|
|
|
|
]
|
2016-10-31 13:56:51 +03:00
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_allows_post_with_get_operation_name(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(operationName="helloWorld"),
|
|
|
|
"""
|
2016-09-20 08:04:23 +03:00
|
|
|
query helloYou { test(who: "You"), ...shared }
|
|
|
|
query helloWorld { test(who: "World"), ...shared }
|
|
|
|
query helloDolly { test(who: "Dolly"), ...shared }
|
|
|
|
fragment shared on QueryRoot {
|
|
|
|
shared: test(who: "Everyone")
|
|
|
|
}
|
2018-07-20 02:51:33 +03:00
|
|
|
""",
|
|
|
|
"application/graphql",
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-20 02:51:33 +03:00
|
|
|
@pytest.mark.urls("graphene_django.tests.urls_inherited")
|
2017-07-11 13:18:31 +03:00
|
|
|
def test_inherited_class_with_attributes_works(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
inherited_url = "/graphql/inherited/"
|
2017-07-11 13:18:31 +03:00
|
|
|
# Check schema and pretty attributes work
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(url_string(inherited_url, query="{test}"))
|
2017-07-11 13:18:31 +03:00
|
|
|
assert response.content.decode() == (
|
2018-07-20 02:51:33 +03:00
|
|
|
"{\n" ' "data": {\n' ' "test": "Hello World"\n' " }\n" "}"
|
2017-07-11 13:18:31 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
# Check graphiql works
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(inherited_url), HTTP_ACCEPT="text/html")
|
2017-07-11 13:18:31 +03:00
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
2018-07-20 02:51:33 +03:00
|
|
|
@pytest.mark.urls("graphene_django.tests.urls_pretty")
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_supports_pretty_printing(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{test}"))
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.content.decode() == (
|
2018-07-20 02:51:33 +03:00
|
|
|
"{\n" ' "data": {\n' ' "test": "Hello World"\n' " }\n" "}"
|
2016-09-20 08:04:23 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_supports_pretty_printing_by_request(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{test}", pretty="1"))
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.content.decode() == (
|
2018-07-20 02:51:33 +03:00
|
|
|
"{\n" ' "data": {\n' ' "test": "Hello World"\n' " }\n" "}"
|
2016-09-20 08:04:23 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_field_errors_caught_by_graphql(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{thrower}"))
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 200
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"data": None,
|
|
|
|
"errors": [
|
|
|
|
{
|
|
|
|
"locations": [{"column": 2, "line": 1}],
|
|
|
|
"path": ["thrower"],
|
|
|
|
"message": "Throws!",
|
|
|
|
}
|
|
|
|
],
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_syntax_errors_caught_by_graphql(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="syntaxerror"))
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [
|
|
|
|
{
|
|
|
|
"locations": [{"column": 1, "line": 1}],
|
2020-05-09 14:13:47 +03:00
|
|
|
"message": "Syntax Error: Unexpected Name 'syntaxerror'.",
|
|
|
|
"path": None,
|
2018-07-20 02:51:33 +03:00
|
|
|
}
|
|
|
|
]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_errors_caused_by_a_lack_of_query(client):
|
|
|
|
response = client.get(url_string())
|
|
|
|
|
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "Must provide query string."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-20 12:15:13 +03:00
|
|
|
def test_handles_not_expected_json_bodies(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(url_string(), "[]", "application/json")
|
2016-09-20 08:04:23 +03:00
|
|
|
|
2017-02-20 12:15:13 +03:00
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "The received data is not a valid JSON query."}]
|
2017-02-20 12:15:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_invalid_json_bodies(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(url_string(), "[oh}", "application/json")
|
2017-02-20 12:15:13 +03:00
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "POST body sent invalid JSON."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-12 14:04:03 +03:00
|
|
|
def test_handles_django_request_error(client, monkeypatch):
|
|
|
|
def mocked_read(*args):
|
|
|
|
raise IOError("foo-bar")
|
|
|
|
|
|
|
|
monkeypatch.setattr("django.http.request.HttpRequest.read", mocked_read)
|
|
|
|
|
2018-07-20 02:51:33 +03:00
|
|
|
valid_json = json.dumps(dict(foo="bar"))
|
|
|
|
response = client.post(url_string(), valid_json, "application/json")
|
2017-04-12 13:25:51 +03:00
|
|
|
|
|
|
|
assert response.status_code == 400
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"errors": [{"message": "foo-bar"}]}
|
2017-04-12 13:25:51 +03:00
|
|
|
|
|
|
|
|
2016-09-20 08:04:23 +03:00
|
|
|
def test_handles_incomplete_json_bodies(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(url_string(), '{"query":', "application/json")
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "POST body sent invalid JSON."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_plain_post_text(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.post(
|
|
|
|
url_string(variables=json.dumps({"who": "Dolly"})),
|
|
|
|
"query helloWho($who: String){ test(who: $who) }",
|
|
|
|
"text/plain",
|
2016-09-20 08:04:23 +03:00
|
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "Must provide query string."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_poorly_formed_variables(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(
|
|
|
|
url_string(
|
|
|
|
query="query helloWho($who: String){ test(who: $who) }", variables="who:You"
|
|
|
|
)
|
|
|
|
)
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 400
|
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "Variables are invalid JSON."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_handles_unsupported_http_methods(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.put(url_string(query="{test}"))
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response.status_code == 405
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response["Allow"] == "GET, POST"
|
2016-09-20 08:04:23 +03:00
|
|
|
assert response_json(response) == {
|
2018-07-20 02:51:33 +03:00
|
|
|
"errors": [{"message": "GraphQL only supports GET and POST requests."}]
|
2016-09-20 08:04:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def test_passes_request_into_context_request(client):
|
2018-07-20 02:51:33 +03:00
|
|
|
response = client.get(url_string(query="{request}", q="testing"))
|
2016-09-20 08:04:23 +03:00
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-07-20 02:51:33 +03:00
|
|
|
assert response_json(response) == {"data": {"request": "testing"}}
|
2020-12-31 02:37:57 +03:00
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": True}
|
|
|
|
)
|
|
|
|
def test_form_mutation_multiple_creation_invalid_atomic_request(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petFormMutation1: petFormMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petFormMutation2: petFormMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation2"]["errors"] == []
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": True, "ATOMIC_REQUESTS": False}
|
|
|
|
)
|
|
|
|
def test_form_mutation_multiple_creation_invalid_atomic_mutation_1(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petFormMutation1: petFormMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petFormMutation2: petFormMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation2"]["errors"] == []
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", True)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": False}
|
|
|
|
)
|
|
|
|
def test_form_mutation_multiple_creation_invalid_atomic_mutation_2(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petFormMutation1: petFormMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petFormMutation2: petFormMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation2"]["errors"] == []
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": False}
|
|
|
|
)
|
|
|
|
def test_form_mutation_multiple_creation_invalid_non_atomic(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petFormMutation1: petFormMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petFormMutation2: petFormMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petFormMutation2"]["errors"] == []
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 1
|
|
|
|
|
|
|
|
pet = Pet.objects.get()
|
|
|
|
assert pet.name == "Enzo"
|
|
|
|
assert pet.age == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": True}
|
|
|
|
)
|
|
|
|
def test_model_form_mutation_multiple_creation_invalid_atomic_request(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petMutation1: petMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
pet {
|
|
|
|
name
|
|
|
|
age
|
|
|
|
}
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petMutation2: petMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
pet {
|
|
|
|
name
|
|
|
|
age
|
|
|
|
}
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petMutation1"]["pet"] is None
|
|
|
|
assert content["data"]["petMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petMutation2"]["pet"] == {"name": "Enzo", "age": 0}
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": False}
|
|
|
|
)
|
|
|
|
def test_model_form_mutation_multiple_creation_invalid_non_atomic(client):
|
|
|
|
query = """
|
|
|
|
mutation PetMutations {
|
|
|
|
petMutation1: petMutation(input: { name: "Mia", age: 99 }) {
|
|
|
|
pet {
|
|
|
|
name
|
|
|
|
age
|
|
|
|
}
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
petMutation2: petMutation(input: { name: "Enzo", age: 0 }) {
|
|
|
|
pet {
|
|
|
|
name
|
|
|
|
age
|
|
|
|
}
|
|
|
|
errors {
|
|
|
|
field
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
response = client.post(url_string(query=query))
|
|
|
|
content = response_json(response)
|
|
|
|
|
|
|
|
assert "errors" not in content
|
|
|
|
|
|
|
|
assert content["data"]["petMutation1"]["pet"] is None
|
|
|
|
assert content["data"]["petMutation1"]["errors"] == [
|
|
|
|
{"field": "age", "messages": ["Too old"]}
|
|
|
|
]
|
|
|
|
|
|
|
|
assert content["data"]["petMutation2"]["pet"] == {"name": "Enzo", "age": 0}
|
|
|
|
|
|
|
|
assert Pet.objects.count() == 1
|
|
|
|
|
|
|
|
pet = Pet.objects.get()
|
|
|
|
assert pet.name == "Enzo"
|
|
|
|
assert pet.age == 0
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.utils.utils.transaction.set_rollback")
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": True}
|
|
|
|
)
|
|
|
|
def test_query_errors_atomic_request(set_rollback_mock, client):
|
|
|
|
client.get(url_string(query="force error"))
|
|
|
|
set_rollback_mock.assert_called_once_with(True)
|
|
|
|
|
|
|
|
|
|
|
|
@patch("graphene_django.utils.utils.transaction.set_rollback")
|
|
|
|
@patch("graphene_django.settings.graphene_settings.ATOMIC_MUTATIONS", False)
|
|
|
|
@patch.dict(
|
|
|
|
connection.settings_dict, {"ATOMIC_MUTATIONS": False, "ATOMIC_REQUESTS": False}
|
|
|
|
)
|
|
|
|
def test_query_errors_non_atomic(set_rollback_mock, client):
|
|
|
|
client.get(url_string(query="force error"))
|
|
|
|
set_rollback_mock.assert_not_called()
|