Skip to content

Commit fdb0950

Browse files
committed
Add test for custom scalars (#93)
1 parent df8b548 commit fdb0950

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed

tests/type/test_custom_scalars.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
from typing import Any, Dict, NamedTuple
2+
3+
from graphql import graphql_sync
4+
from graphql.error import GraphQLError
5+
from graphql.language import ValueNode
6+
from graphql.pyutils import inspect, is_finite
7+
from graphql.type import (
8+
GraphQLArgument,
9+
GraphQLField,
10+
GraphQLFloat,
11+
GraphQLObjectType,
12+
GraphQLScalarType,
13+
GraphQLSchema,
14+
)
15+
from graphql.utilities import value_from_ast_untyped
16+
17+
# this test is not (yet) part of GraphQL.js, see
18+
# https://github.com/graphql/graphql-js/issues/2657
19+
20+
21+
class Money(NamedTuple):
22+
amount: float
23+
currency: str
24+
25+
26+
def serialize_money(output_value: Any) -> Dict[str, float]:
27+
if not isinstance(output_value, Money):
28+
raise GraphQLError("Cannot serialize money value: " + inspect(output_value))
29+
return output_value._asdict()
30+
31+
32+
def parse_money_value(input_value: Any) -> Money:
33+
if not isinstance(input_value, Money):
34+
raise GraphQLError("Cannot parse money value: " + inspect(input_value))
35+
return input_value
36+
37+
38+
def parse_money_literal(value_node: ValueNode, variables=None) -> Money:
39+
money = value_from_ast_untyped(value_node, variables)
40+
if variables is not None and (
41+
# variables are not set when checked with ValuesIOfCorrectTypeRule
42+
not money
43+
or not is_finite(money.get("amount"))
44+
or not isinstance(money.get("currency"), str)
45+
):
46+
raise GraphQLError("Cannot parse literal money value: " + inspect(money))
47+
return Money(**money)
48+
49+
50+
MoneyScalar = GraphQLScalarType(
51+
name="Money",
52+
serialize=serialize_money,
53+
parse_value=parse_money_value,
54+
parse_literal=parse_money_literal,
55+
)
56+
57+
58+
def resolve_balance(root, _info):
59+
return root
60+
61+
62+
def resolve_to_euros(_root, _info, money):
63+
amount = money.amount
64+
currency = money.currency
65+
if not amount or currency == "EUR":
66+
return amount
67+
if currency == "DM":
68+
return amount * 0.5
69+
raise ValueError("Cannot convert to euros: " + inspect(money))
70+
71+
72+
schema = GraphQLSchema(
73+
query=GraphQLObjectType(
74+
name="RootQueryType",
75+
fields={
76+
"balance": GraphQLField(MoneyScalar, resolve=resolve_balance),
77+
"toEuros": GraphQLField(
78+
GraphQLFloat,
79+
args={"money": GraphQLArgument(MoneyScalar)},
80+
resolve=resolve_to_euros,
81+
),
82+
},
83+
)
84+
)
85+
86+
87+
def describe_custom_scalar():
88+
def serialize():
89+
source = """
90+
{
91+
balance
92+
}
93+
"""
94+
95+
result = graphql_sync(schema, source, root_value=Money(42, "DM"))
96+
assert result == ({"balance": {"amount": 42, "currency": "DM"}}, None)
97+
98+
def serialize_with_error():
99+
source = """
100+
{
101+
balance
102+
}
103+
"""
104+
105+
result = graphql_sync(schema, source, root_value=21)
106+
assert result == (
107+
{"balance": None},
108+
[
109+
{
110+
"message": "Cannot serialize money value: 21",
111+
"locations": [(3, 15)],
112+
"path": ["balance"],
113+
}
114+
],
115+
)
116+
117+
def parse_value():
118+
source = """
119+
query Money($money: Money!) {
120+
toEuros(money: $money)
121+
}
122+
"""
123+
124+
result = graphql_sync(
125+
schema, source, variable_values={"money": Money(24, "EUR")}
126+
)
127+
assert result == ({"toEuros": 24}, None)
128+
129+
result = graphql_sync(
130+
schema, source, variable_values={"money": Money(42, "DM")}
131+
)
132+
assert result == ({"toEuros": 21}, None)
133+
134+
def parse_value_with_error():
135+
source = """
136+
query Money($money: Money!) {
137+
toEuros(money: $money)
138+
}
139+
"""
140+
141+
result = graphql_sync(
142+
schema, source, variable_values={"money": Money(42, "USD")}
143+
)
144+
assert result == (
145+
{"toEuros": None},
146+
[
147+
{
148+
"message": "Cannot convert to euros: (42, 'USD')",
149+
"locations": [(3, 15)],
150+
}
151+
],
152+
)
153+
154+
result = graphql_sync(schema, source, variable_values={"money": 21})
155+
assert result == (
156+
None,
157+
[
158+
{
159+
"message": "Variable '$money' got invalid value 21;"
160+
" Cannot parse money value: 21",
161+
"locations": [(2, 25)],
162+
}
163+
],
164+
)
165+
166+
def parse_literal():
167+
source = """
168+
query Money($amount: Float!, $currency: String!) {
169+
toEuros(money: {amount: $amount, currency: $currency})
170+
}
171+
"""
172+
173+
variable_values = {"amount": 42, "currency": "DM"}
174+
result = graphql_sync(schema, source, variable_values=variable_values)
175+
assert result == ({"toEuros": 21}, None)
176+
177+
def parse_literal_with_errors():
178+
source = """
179+
query Money($amount: String!, $currency: Float!) {
180+
toEuros(money: {amount: $amount, currency: $currency})
181+
}
182+
"""
183+
184+
variable_values = {"amount": "DM", "currency": 42}
185+
result = graphql_sync(schema, source, variable_values=variable_values)
186+
assert result == (
187+
{"toEuros": None},
188+
[
189+
{
190+
"message": "Argument 'money' has invalid value"
191+
" {amount: $amount, currency: $currency}.",
192+
"locations": [(3, 30)],
193+
},
194+
],
195+
)

0 commit comments

Comments
 (0)