mirror of
https://github.com/kennethreitz/responder.git
synced 2026-06-05 06:46:14 +00:00
ff6d530338
## About A few cosmetic adjustments aka. code formatting. Also validate the outcome on CI/GHA. Feel free to improve now or later at your disposal. ## Details The updates are based on using the most recent versions of pyproject-fmt and ruff. Specifically, spots marked with `noqa` might need further love, also at your disposal. --------- Co-authored-by: Kenneth Reitz <me@kennethreitz.org>
237 lines
7.5 KiB
Python
237 lines
7.5 KiB
Python
# ruff: noqa: E402
|
|
import json
|
|
|
|
import pytest
|
|
|
|
graphene = pytest.importorskip("graphene")
|
|
|
|
from responder.ext.graphql import GraphQLView
|
|
|
|
|
|
@pytest.fixture
|
|
def schema():
|
|
class Query(graphene.ObjectType):
|
|
hello = graphene.String(name=graphene.String(default_value="stranger"))
|
|
|
|
def resolve_hello(self, info, name):
|
|
return f"Hello {name}"
|
|
|
|
return graphene.Schema(query=Query)
|
|
|
|
|
|
@pytest.fixture
|
|
def mutation_schema():
|
|
class Query(graphene.ObjectType):
|
|
hello = graphene.String(name=graphene.String(default_value="stranger"))
|
|
|
|
def resolve_hello(self, info, name):
|
|
return f"Hello {name}"
|
|
|
|
class CreateUser(graphene.Mutation):
|
|
class Arguments:
|
|
name = graphene.String(required=True)
|
|
|
|
ok = graphene.Boolean()
|
|
name = graphene.String()
|
|
|
|
def mutate(self, info, name):
|
|
return CreateUser(ok=True, name=name)
|
|
|
|
class Mutation(graphene.ObjectType):
|
|
create_user = CreateUser.Field()
|
|
|
|
return graphene.Schema(query=Query, mutation=Mutation)
|
|
|
|
|
|
@pytest.fixture
|
|
def multi_op_schema():
|
|
class Query(graphene.ObjectType):
|
|
hello = graphene.String(name=graphene.String(default_value="stranger"))
|
|
goodbye = graphene.String(name=graphene.String(default_value="stranger"))
|
|
|
|
def resolve_hello(self, info, name):
|
|
return f"Hello {name}"
|
|
|
|
def resolve_goodbye(self, info, name):
|
|
return f"Goodbye {name}"
|
|
|
|
return graphene.Schema(query=Query)
|
|
|
|
|
|
def test_graphql_schema_query_querying(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.get("http://;/?q={ hello }", headers={"Accept": "json"})
|
|
assert r.status_code == 200
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|
|
|
|
|
|
def test_graphql_schema_json_query(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post("http://;/", json={"query": "{ hello }"})
|
|
assert r.status_code < 300
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|
|
|
|
|
|
def test_graphiql(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.get("http://;/", headers={"Accept": "text/html"})
|
|
assert r.status_code < 300
|
|
assert "GraphiQL" in r.text
|
|
|
|
|
|
def test_graphql_shorthand(api, schema):
|
|
"""Test the api.graphql() shorthand method."""
|
|
api.graphql("/gql", schema=schema)
|
|
r = api.requests.post("http://;/gql", json={"query": "{ hello }"})
|
|
assert r.status_code < 300
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|
|
|
|
|
|
def test_graphql_missing_query_key(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post("http://;/", json={"not_query": "foo"})
|
|
assert r.status_code == 400
|
|
assert "errors" in r.json()
|
|
|
|
|
|
def test_graphql_query_param(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.get("http://;/?query={ hello }", headers={"Accept": "json"})
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|
|
|
|
|
|
def test_graphql_error_response(api, schema):
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post("http://;/", json={"query": "{ nonexistent }"})
|
|
assert "errors" in r.json()
|
|
|
|
|
|
def test_graphql_variables_json(api, schema):
|
|
"""Variables passed via JSON body."""
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post(
|
|
"http://;/",
|
|
json={
|
|
"query": "query Hello($name: String!) { hello(name: $name) }",
|
|
"variables": {"name": "Alice"},
|
|
},
|
|
)
|
|
assert r.json() == {"data": {"hello": "Hello Alice"}}
|
|
|
|
|
|
def test_graphql_variables_query_param(api, schema):
|
|
"""Variables passed as JSON string in query parameter."""
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
variables = json.dumps({"name": "Bob"})
|
|
r = api.requests.get(
|
|
f"http://;/?query=query Hello($name: String!) "
|
|
f"{{ hello(name: $name) }}&variables={variables}",
|
|
headers={"Accept": "json"},
|
|
)
|
|
assert r.json() == {"data": {"hello": "Hello Bob"}}
|
|
|
|
|
|
def test_graphql_operation_name_json(api, multi_op_schema):
|
|
"""operationName selects which operation to run."""
|
|
api.add_route("/", GraphQLView(schema=multi_op_schema, api=api))
|
|
query = """
|
|
query SayHello { hello }
|
|
query SayGoodbye { goodbye }
|
|
"""
|
|
r = api.requests.post(
|
|
"http://;/",
|
|
json={
|
|
"query": query,
|
|
"operationName": "SayHello",
|
|
},
|
|
)
|
|
data = r.json()
|
|
assert data["data"]["hello"] == "Hello stranger"
|
|
|
|
|
|
def test_graphql_operation_name_query_param(api, multi_op_schema):
|
|
"""operationName via query parameter."""
|
|
api.add_route("/", GraphQLView(schema=multi_op_schema, api=api))
|
|
query = "query SayHello { hello } query SayGoodbye { goodbye }"
|
|
r = api.requests.get(
|
|
f"http://;/?query={query}&operationName=SayGoodbye",
|
|
headers={"Accept": "json"},
|
|
)
|
|
data = r.json()
|
|
assert data["data"]["goodbye"] == "Goodbye stranger"
|
|
|
|
|
|
def test_graphql_mutation(api, mutation_schema):
|
|
"""Mutations work via JSON body."""
|
|
api.add_route("/", GraphQLView(schema=mutation_schema, api=api))
|
|
r = api.requests.post(
|
|
"http://;/",
|
|
json={
|
|
"query": 'mutation { createUser(name: "Eve") { ok name } }',
|
|
},
|
|
)
|
|
data = r.json()
|
|
assert data["data"]["createUser"]["ok"] is True
|
|
assert data["data"]["createUser"]["name"] == "Eve"
|
|
|
|
|
|
def test_graphql_mutation_with_variables(api, mutation_schema):
|
|
"""Mutations with variables."""
|
|
api.add_route("/", GraphQLView(schema=mutation_schema, api=api))
|
|
r = api.requests.post(
|
|
"http://;/",
|
|
json={
|
|
"query": "mutation CreateUser($name: String!) "
|
|
"{ createUser(name: $name) { ok name } }",
|
|
"variables": {"name": "Frank"},
|
|
},
|
|
)
|
|
data = r.json()
|
|
assert data["data"]["createUser"]["ok"] is True
|
|
assert data["data"]["createUser"]["name"] == "Frank"
|
|
|
|
|
|
def test_graphql_context_access(api):
|
|
"""Resolvers can access request and response via info.context."""
|
|
|
|
class Query(graphene.ObjectType):
|
|
method = graphene.String()
|
|
|
|
def resolve_method(self, info):
|
|
return info.context["request"].method
|
|
|
|
schema = graphene.Schema(query=Query)
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post("http://;/", json={"query": "{ method }"})
|
|
assert r.json() == {"data": {"method": "post"}}
|
|
|
|
|
|
def test_graphql_malformed_query(api, schema):
|
|
"""Malformed GraphQL syntax returns errors."""
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post("http://;/", json={"query": "{ this is not valid"})
|
|
data = r.json()
|
|
assert "errors" in data
|
|
assert len(data["errors"]) > 0
|
|
|
|
|
|
def test_graphql_raw_text_query(api, schema):
|
|
"""Query sent as raw text body."""
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.post(
|
|
"http://;/",
|
|
content=b"{ hello }",
|
|
headers={"Content-Type": "text/plain"},
|
|
)
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|
|
|
|
|
|
def test_graphql_invalid_variables_query_param(api, schema):
|
|
"""Invalid JSON in variables query param is treated as None."""
|
|
api.add_route("/", GraphQLView(schema=schema, api=api))
|
|
r = api.requests.get(
|
|
"http://;/?query={ hello }&variables=not-json",
|
|
headers={"Accept": "json"},
|
|
)
|
|
assert r.json() == {"data": {"hello": "Hello stranger"}}
|