1from math import inf, nan, pi
2from typing import Any
3
4from pytest import raises
5
6from graphql.error import GraphQLError
7from graphql.language import parse_value as parse_value_to_ast
8from graphql.pyutils import Undefined
9from graphql.type import (
10    GraphQLInt,
11    GraphQLFloat,
12    GraphQLString,
13    GraphQLBoolean,
14    GraphQLID,
15)
16
17
18def describe_type_system_specified_scalar_types():
19    def describe_graphql_int():
20        def parse_value():
21            _parse_value = GraphQLInt.parse_value
22
23            def _parse_value_raises(s: Any, message: str):
24                with raises(GraphQLError) as exc_info:
25                    _parse_value(s)
26                assert str(exc_info.value) == message
27
28            assert _parse_value(1) == 1
29            assert _parse_value(0) == 0
30            assert _parse_value(-1) == -1
31
32            _parse_value_raises(
33                9876504321,
34                "Int cannot represent non 32-bit signed integer value: 9876504321",
35            )
36            _parse_value_raises(
37                -9876504321,
38                "Int cannot represent non 32-bit signed integer value: -9876504321",
39            )
40            _parse_value_raises(0.1, "Int cannot represent non-integer value: 0.1")
41            _parse_value_raises(nan, "Int cannot represent non-integer value: nan")
42            _parse_value_raises(inf, "Int cannot represent non-integer value: inf")
43            _parse_value_raises(
44                Undefined, "Int cannot represent non-integer value: Undefined"
45            )
46            _parse_value_raises(None, "Int cannot represent non-integer value: None")
47            _parse_value_raises("", "Int cannot represent non-integer value: ''")
48            _parse_value_raises("123", "Int cannot represent non-integer value: '123'")
49            _parse_value_raises(False, "Int cannot represent non-integer value: False")
50            _parse_value_raises(True, "Int cannot represent non-integer value: True")
51            _parse_value_raises([1], "Int cannot represent non-integer value: [1]")
52            _parse_value_raises(
53                {"value": 1}, "Int cannot represent non-integer value: {'value': 1}"
54            )
55
56        def parse_literal():
57            def _parse_literal(s: str):
58                return GraphQLInt.parse_literal(parse_value_to_ast(s))
59
60            def _parse_literal_raises(s: str, message: str):
61                with raises(GraphQLError) as exc_info:
62                    _parse_literal(s)
63                assert str(exc_info.value).startswith(message + "\n")
64
65            assert _parse_literal("1") == 1
66            assert _parse_literal("0") == 0
67            assert _parse_literal("-1") == -1
68
69            _parse_literal_raises(
70                "9876504321",
71                "Int cannot represent non 32-bit signed integer value: 9876504321",
72            )
73            _parse_literal_raises(
74                "-9876504321",
75                "Int cannot represent non 32-bit signed integer value: -9876504321",
76            )
77            _parse_literal_raises("1.0", "Int cannot represent non-integer value: 1.0")
78            _parse_literal_raises(
79                "null", "Int cannot represent non-integer value: null"
80            )
81            _parse_literal_raises(
82                "None", "Int cannot represent non-integer value: None"
83            )
84            _parse_literal_raises('""', 'Int cannot represent non-integer value: ""')
85            _parse_literal_raises(
86                '"123"', 'Int cannot represent non-integer value: "123"'
87            )
88            _parse_literal_raises(
89                "false", "Int cannot represent non-integer value: false"
90            )
91            _parse_literal_raises(
92                "False", "Int cannot represent non-integer value: False"
93            )
94            _parse_literal_raises("[1]", "Int cannot represent non-integer value: [1]")
95            _parse_literal_raises(
96                "{value: 1}", "Int cannot represent non-integer value: {value: 1}"
97            )
98            _parse_literal_raises(
99                "ENUM_VALUE", "Int cannot represent non-integer value: ENUM_VALUE"
100            )
101            _parse_literal_raises(
102                "$var", "Int cannot represent non-integer value: $var"
103            )
104
105        def serializes():
106            serialize = GraphQLInt.serialize
107
108            assert serialize(1) == 1
109            assert serialize("123") == 123
110            assert serialize(0) == 0
111            assert serialize(-1) == -1
112            assert serialize(1e5) == 100000
113            assert serialize(False) == 0
114            assert serialize(True) == 1
115            assert serialize(type("Int", (int,), {})(5)) == 5
116
117            # The GraphQL specification does not allow serializing non-integer
118            # values as Int to avoid accidental data loss.
119            with raises(GraphQLError) as exc_info:
120                serialize(0.1)
121            assert str(exc_info.value) == "Int cannot represent non-integer value: 0.1"
122            with raises(GraphQLError) as exc_info:
123                serialize(1.1)
124            assert str(exc_info.value) == "Int cannot represent non-integer value: 1.1"
125            with raises(GraphQLError) as exc_info:
126                serialize(-1.1)
127            assert str(exc_info.value) == "Int cannot represent non-integer value: -1.1"
128            with raises(GraphQLError) as exc_info:
129                serialize("-1.1")
130            assert (
131                str(exc_info.value) == "Int cannot represent non-integer value: '-1.1'"
132            )
133            # Maybe a safe JavaScript int, but bigger than 2^32, so not
134            # representable as a GraphQL Int
135            with raises(GraphQLError) as exc_info:
136                serialize(9876504321)
137            assert str(exc_info.value) == (
138                "Int cannot represent non 32-bit signed integer value: 9876504321"
139            )
140            with raises(GraphQLError) as exc_info:
141                serialize(-9876504321)
142            assert str(exc_info.value) == (
143                "Int cannot represent non 32-bit signed integer value: -9876504321"
144            )
145            # Too big to represent as an Int in JavaScript or GraphQL
146            with raises(GraphQLError) as exc_info:
147                serialize(1e100)
148            assert str(exc_info.value) == (
149                "Int cannot represent non 32-bit signed integer value: 1e+100"
150            )
151            with raises(GraphQLError) as exc_info:
152                serialize(-1e100)
153            assert str(exc_info.value) == (
154                "Int cannot represent non 32-bit signed integer value: -1e+100"
155            )
156            with raises(GraphQLError) as exc_info:
157                serialize("one")
158            assert (
159                str(exc_info.value) == "Int cannot represent non-integer value: 'one'"
160            )
161            # Doesn't represent number
162            with raises(GraphQLError) as exc_info:
163                serialize("")
164            assert str(exc_info.value) == "Int cannot represent non-integer value: ''"
165            with raises(GraphQLError) as exc_info:
166                serialize(nan)
167            assert str(exc_info.value) == "Int cannot represent non-integer value: nan"
168            with raises(GraphQLError) as exc_info:
169                serialize(inf)
170            assert str(exc_info.value) == "Int cannot represent non-integer value: inf"
171            with raises(GraphQLError) as exc_info:
172                serialize([5])
173            assert str(exc_info.value) == "Int cannot represent non-integer value: [5]"
174
175    def describe_graphql_float():
176        def parse_value():
177            _parse_value = GraphQLFloat.parse_value
178
179            def _parse_value_raises(s: Any, message: str):
180                with raises(GraphQLError) as exc_info:
181                    _parse_value(s)
182                assert str(exc_info.value) == message
183
184            assert _parse_value(1) == 1
185            assert _parse_value(0) == 0
186            assert _parse_value(-1) == -1
187            assert _parse_value(0.1) == 0.1
188            assert _parse_value(pi) == pi
189
190            _parse_value_raises(nan, "Float cannot represent non numeric value: nan")
191            _parse_value_raises(inf, "Float cannot represent non numeric value: inf")
192            _parse_value_raises("", "Float cannot represent non numeric value: ''")
193            _parse_value_raises(
194                "123", "Float cannot represent non numeric value: '123'"
195            )
196            _parse_value_raises(
197                "123.5", "Float cannot represent non numeric value: '123.5'"
198            )
199            _parse_value_raises(
200                False, "Float cannot represent non numeric value: False"
201            )
202            _parse_value_raises(True, "Float cannot represent non numeric value: True")
203            _parse_value_raises(
204                [0.1], "Float cannot represent non numeric value: [0.1]"
205            )
206            _parse_value_raises(
207                {"value": 0.1},
208                "Float cannot represent non numeric value: {'value': 0.1}",
209            )
210
211        def parse_literal():
212            def _parse_literal(s: str):
213                return GraphQLFloat.parse_literal(parse_value_to_ast(s))
214
215            def _parse_literal_raises(s: str, message: str):
216                with raises(GraphQLError) as exc_info:
217                    _parse_literal(s)
218                assert str(exc_info.value).startswith(message + "\n")
219
220            assert _parse_literal("1") == 1
221            assert _parse_literal("0") == 0
222            assert _parse_literal("-1") == -1
223            assert _parse_literal("0.1") == 0.1
224            assert _parse_literal(str(pi)) == pi
225
226            _parse_literal_raises(
227                "null", "Float cannot represent non numeric value: null"
228            )
229            _parse_literal_raises(
230                "None", "Float cannot represent non numeric value: None"
231            )
232            _parse_literal_raises('""', 'Float cannot represent non numeric value: ""')
233            _parse_literal_raises(
234                '"123"', 'Float cannot represent non numeric value: "123"'
235            )
236            _parse_literal_raises(
237                '"123.5"', 'Float cannot represent non numeric value: "123.5"'
238            )
239            _parse_literal_raises(
240                "false", "Float cannot represent non numeric value: false"
241            )
242            _parse_literal_raises(
243                "False", "Float cannot represent non numeric value: False"
244            )
245            _parse_literal_raises(
246                "[0.1]", "Float cannot represent non numeric value: [0.1]"
247            )
248            _parse_literal_raises(
249                "{value: 0.1}", "Float cannot represent non numeric value: {value: 0.1}"
250            )
251            _parse_literal_raises(
252                "ENUM_VALUE", "Float cannot represent non numeric value: ENUM_VALUE"
253            )
254            _parse_literal_raises(
255                "$var", "Float cannot represent non numeric value: $var"
256            )
257
258        def serializes():
259            serialize = GraphQLFloat.serialize
260
261            assert serialize(1) == 1.0
262            assert serialize(0) == 0.0
263            assert serialize("123.5") == 123.5
264            assert serialize(-1) == -1.0
265            assert serialize(0.1) == 0.1
266            assert serialize(1.1) == 1.1
267            assert serialize(-1.1) == -1.1
268            assert serialize("-1.1") == -1.1
269            assert serialize(False) == 0
270            assert serialize(True) == 1
271            assert serialize(type("Float", (float,), {})(5.5)) == 5.5
272
273            with raises(GraphQLError) as exc_info:
274                serialize(nan)
275            assert (
276                str(exc_info.value) == "Float cannot represent non numeric value: nan"
277            )
278            with raises(GraphQLError) as exc_info:
279                serialize(inf)
280            assert (
281                str(exc_info.value) == "Float cannot represent non numeric value: inf"
282            )
283            with raises(GraphQLError) as exc_info:
284                serialize("one")
285            assert str(exc_info.value) == (
286                "Float cannot represent non numeric value: 'one'"
287            )
288            with raises(GraphQLError) as exc_info:
289                serialize("")
290            assert str(exc_info.value) == "Float cannot represent non numeric value: ''"
291            with raises(GraphQLError) as exc_info:
292                serialize([5])
293            assert (
294                str(exc_info.value) == "Float cannot represent non numeric value: [5]"
295            )
296
297    def describe_graphql_string():
298        def parse_value():
299            _parse_value = GraphQLString.parse_value
300
301            def _parse_value_raises(s: Any, message: str):
302                with raises(GraphQLError) as exc_info:
303                    _parse_value(s)
304                assert str(exc_info.value) == message
305
306            assert _parse_value("foo") == "foo"
307
308            _parse_value_raises(
309                Undefined, "String cannot represent a non string value: Undefined"
310            )
311            _parse_value_raises(
312                None, "String cannot represent a non string value: None"
313            )
314            _parse_value_raises(1, "String cannot represent a non string value: 1")
315            _parse_value_raises(nan, "String cannot represent a non string value: nan")
316            _parse_value_raises(
317                False, "String cannot represent a non string value: False"
318            )
319            _parse_value_raises(
320                ["foo"], "String cannot represent a non string value: ['foo']"
321            )
322            _parse_value_raises(
323                {"value": "foo"},
324                "String cannot represent a non string value: {'value': 'foo'}",
325            )
326
327        def parse_literal():
328            def _parse_literal(s: str):
329                return GraphQLString.parse_literal(parse_value_to_ast(s))
330
331            def _parse_literal_raises(s: str, message: str):
332                with raises(GraphQLError) as exc_info:
333                    _parse_literal(s)
334                assert str(exc_info.value).startswith(message + "\n")
335
336            assert _parse_literal('"foo"') == "foo"
337            assert _parse_literal('"""bar"""') == "bar"
338
339            _parse_literal_raises(
340                "null", "String cannot represent a non string value: null"
341            )
342            _parse_literal_raises(
343                "None", "String cannot represent a non string value: None"
344            )
345            _parse_literal_raises("1", "String cannot represent a non string value: 1")
346            _parse_literal_raises(
347                "0.1", "String cannot represent a non string value: 0.1"
348            )
349            _parse_literal_raises(
350                "false", "String cannot represent a non string value: false"
351            )
352            _parse_literal_raises(
353                "False", "String cannot represent a non string value: False"
354            )
355            _parse_literal_raises(
356                '["foo"]', 'String cannot represent a non string value: ["foo"]'
357            )
358            _parse_literal_raises(
359                '{value: "foo"}',
360                'String cannot represent a non string value: {value: "foo"}',
361            )
362            _parse_literal_raises(
363                "ENUM_VALUE", "String cannot represent a non string value: ENUM_VALUE"
364            )
365            _parse_literal_raises(
366                "$var", "String cannot represent a non string value: $var"
367            )
368
369        def serializes():
370            serialize = GraphQLString.serialize
371
372            assert serialize("string") == "string"
373            assert serialize(1) == "1"
374            assert serialize(-1.1) == "-1.1"
375            assert serialize(True) == "true"
376            assert serialize(False) == "false"
377
378            class StringableObjValue:
379                def __str__(self):
380                    return "something useful"
381
382            assert serialize(StringableObjValue()) == "something useful"
383
384            with raises(GraphQLError) as exc_info:
385                serialize(nan)
386            assert str(exc_info.value) == "String cannot represent value: nan"
387
388            with raises(GraphQLError) as exc_info:
389                serialize([1])
390            assert str(exc_info.value) == "String cannot represent value: [1]"
391
392            with raises(GraphQLError) as exc_info:
393                serialize({})
394            assert str(exc_info.value) == "String cannot represent value: {}"
395
396            with raises(GraphQLError) as exc_info:
397                serialize({"value_of": "value_of string"})
398            assert (
399                str(exc_info.value) == "String cannot represent value:"
400                " {'value_of': 'value_of string'}"
401            )
402
403    def describe_graphql_boolean():
404        def parse_value():
405            _parse_value = GraphQLBoolean.parse_value
406
407            def _parse_value_raises(s: Any, message: str):
408                with raises(GraphQLError) as exc_info:
409                    _parse_value(s)
410                assert str(exc_info.value) == message
411
412            assert _parse_value(True) is True
413            assert _parse_value(False) is False
414
415            _parse_value_raises(
416                Undefined, "Boolean cannot represent a non boolean value: Undefined"
417            )
418            _parse_value_raises(
419                None, "Boolean cannot represent a non boolean value: None"
420            )
421            _parse_value_raises(0, "Boolean cannot represent a non boolean value: 0")
422            _parse_value_raises(1, "Boolean cannot represent a non boolean value: 1")
423            _parse_value_raises(
424                nan, "Boolean cannot represent a non boolean value: nan"
425            )
426            _parse_value_raises("", "Boolean cannot represent a non boolean value: ''")
427            _parse_value_raises(
428                "false", "Boolean cannot represent a non boolean value: 'false'"
429            )
430            _parse_value_raises(
431                "False", "Boolean cannot represent a non boolean value: 'False'"
432            )
433            _parse_value_raises(
434                [False], "Boolean cannot represent a non boolean value: [False]"
435            )
436            _parse_value_raises(
437                {"value": False},
438                "Boolean cannot represent a non boolean value: {'value': False}",
439            )
440
441        def parse_literal():
442            def _parse_literal(s: str):
443                return GraphQLBoolean.parse_literal(parse_value_to_ast(s))
444
445            def _parse_literal_raises(s: str, message: str):
446                with raises(GraphQLError) as exc_info:
447                    _parse_literal(s)
448                assert str(exc_info.value).startswith(message + "\n")
449
450            assert _parse_literal("true") is True
451            assert _parse_literal("false") is False
452
453            _parse_literal_raises(
454                "True", "Boolean cannot represent a non boolean value: True"
455            )
456            _parse_literal_raises(
457                "False", "Boolean cannot represent a non boolean value: False"
458            )
459            _parse_literal_raises(
460                "null", "Boolean cannot represent a non boolean value: null"
461            )
462            _parse_literal_raises(
463                "None", "Boolean cannot represent a non boolean value: None"
464            )
465            _parse_literal_raises(
466                "0", "Boolean cannot represent a non boolean value: 0"
467            )
468            _parse_literal_raises(
469                "1", "Boolean cannot represent a non boolean value: 1"
470            )
471            _parse_literal_raises(
472                "0.1", "Boolean cannot represent a non boolean value: 0.1"
473            )
474            _parse_literal_raises(
475                '""', 'Boolean cannot represent a non boolean value: ""'
476            )
477            _parse_literal_raises(
478                '"false"', 'Boolean cannot represent a non boolean value: "false"'
479            )
480            _parse_literal_raises(
481                '"False"', 'Boolean cannot represent a non boolean value: "False"'
482            )
483            _parse_literal_raises(
484                "[false]", "Boolean cannot represent a non boolean value: [false]"
485            )
486            _parse_literal_raises(
487                "[False]", "Boolean cannot represent a non boolean value: [False]"
488            )
489            _parse_literal_raises(
490                "{value: false}",
491                "Boolean cannot represent a non boolean value: {value: false}",
492            )
493            _parse_literal_raises(
494                "{value: False}",
495                "Boolean cannot represent a non boolean value: {value: False}",
496            )
497            _parse_literal_raises(
498                "ENUM_VALUE", "Boolean cannot represent a non boolean value: ENUM_VALUE"
499            )
500            _parse_literal_raises(
501                "$var", "Boolean cannot represent a non boolean value: $var"
502            )
503
504        def serializes():
505            serialize = GraphQLBoolean.serialize
506
507            assert serialize(1) is True
508            assert serialize(0) is False
509            assert serialize(True) is True
510            assert serialize(False) is False
511            with raises(TypeError, match="not an acceptable base type"):
512                # you can't subclass bool in Python
513                assert serialize(type("Boolean", (bool,), {})(True)) is True
514
515            with raises(GraphQLError) as exc_info:
516                serialize(nan)
517            assert str(exc_info.value) == (
518                "Boolean cannot represent a non boolean value: nan"
519            )
520
521            with raises(GraphQLError) as exc_info:
522                serialize("")
523            assert str(exc_info.value) == (
524                "Boolean cannot represent a non boolean value: ''"
525            )
526
527            with raises(GraphQLError) as exc_info:
528                serialize("True")
529            assert str(exc_info.value) == (
530                "Boolean cannot represent a non boolean value: 'True'"
531            )
532
533            with raises(GraphQLError) as exc_info:
534                serialize([False])
535            assert str(exc_info.value) == (
536                "Boolean cannot represent a non boolean value: [False]"
537            )
538
539            with raises(GraphQLError) as exc_info:
540                serialize({})
541            assert str(exc_info.value) == (
542                "Boolean cannot represent a non boolean value: {}"
543            )
544
545    def describe_graphql_id():
546        def parse_value():
547            _parse_value = GraphQLID.parse_value
548
549            def _parse_value_raises(s: Any, message: str):
550                with raises(GraphQLError) as exc_info:
551                    _parse_value(s)
552                assert str(exc_info.value) == message
553
554            assert _parse_value("") == ""
555            assert _parse_value("1") == "1"
556            assert _parse_value("foo") == "foo"
557            assert _parse_value(1) == "1"
558            assert _parse_value(0) == "0"
559            assert _parse_value(-1) == "-1"
560
561            # Maximum and minimum safe numbers in JS
562            assert _parse_value(9007199254740991) == "9007199254740991"
563            assert _parse_value(-9007199254740991) == "-9007199254740991"
564
565            _parse_value_raises(Undefined, "ID cannot represent value: Undefined")
566            _parse_value_raises(None, "ID cannot represent value: None")
567            _parse_value_raises(0.1, "ID cannot represent value: 0.1")
568            _parse_value_raises(nan, "ID cannot represent value: nan")
569            _parse_value_raises(inf, "ID cannot represent value: inf")
570            _parse_value_raises(False, "ID cannot represent value: False")
571            _parse_value_raises(["1"], "ID cannot represent value: ['1']")
572            _parse_value_raises(
573                {"value": "1"}, "ID cannot represent value: {'value': '1'}"
574            )
575
576        def parse_literal():
577            def _parse_literal(s: str):
578                return GraphQLID.parse_literal(parse_value_to_ast(s))
579
580            def _parse_literal_raises(s: str, message: str):
581                with raises(GraphQLError) as exc_info:
582                    _parse_literal(s)
583                assert str(exc_info.value).startswith(message + "\n")
584
585            assert _parse_literal('""') == ""
586            assert _parse_literal('"1"') == "1"
587            assert _parse_literal('"foo"') == "foo"
588            assert _parse_literal('"""foo"""') == "foo"
589            assert _parse_literal("1") == "1"
590            assert _parse_literal("0") == "0"
591            assert _parse_literal("-1") == "-1"
592
593            # Support arbitrary long numbers even if they can't be represented in JS
594            assert _parse_literal("90071992547409910") == "90071992547409910"
595            assert _parse_literal("-90071992547409910") == "-90071992547409910"
596
597            _parse_literal_raises(
598                "null", "ID cannot represent a non-string and non-integer value: null"
599            )
600            _parse_literal_raises(
601                "None", "ID cannot represent a non-string and non-integer value: None"
602            )
603            _parse_literal_raises(
604                "0.1", "ID cannot represent a non-string and non-integer value: 0.1"
605            )
606            _parse_literal_raises(
607                "false", "ID cannot represent a non-string and non-integer value: false"
608            )
609            _parse_literal_raises(
610                "False", "ID cannot represent a non-string and non-integer value: False"
611            )
612            _parse_literal_raises(
613                '["1"]', 'ID cannot represent a non-string and non-integer value: ["1"]'
614            )
615            _parse_literal_raises(
616                '{value: "1"}',
617                "ID cannot represent a non-string and non-integer value:"
618                ' {value: "1"}',
619            )
620            _parse_literal_raises(
621                "ENUM_VALUE",
622                "ID cannot represent a non-string and non-integer value: ENUM_VALUE",
623            )
624            _parse_literal_raises(
625                "$var", "ID cannot represent a non-string and non-integer value: $var"
626            )
627
628        def serializes():
629            serialize = GraphQLID.serialize
630
631            assert serialize("string") == "string"
632            assert serialize("false") == "false"
633            assert serialize("") == ""
634            assert serialize(123) == "123"
635            assert serialize(0) == "0"
636            assert serialize(-1) == "-1"
637
638            class ObjValue:
639                def __init__(self, value):
640                    self._id = value
641
642                def __str__(self):
643                    return str(self._id)
644
645            obj_value = ObjValue(123)
646            assert serialize(obj_value) == "123"
647
648            with raises(GraphQLError) as exc_info:
649                serialize(True)
650            assert str(exc_info.value) == "ID cannot represent value: True"
651
652            with raises(GraphQLError) as exc_info:
653                serialize(3.14)
654            assert str(exc_info.value) == "ID cannot represent value: 3.14"
655
656            with raises(GraphQLError) as exc_info:
657                serialize({})
658            assert str(exc_info.value) == "ID cannot represent value: {}"
659
660            with raises(GraphQLError) as exc_info:
661                serialize(["abc"])
662            assert str(exc_info.value) == "ID cannot represent value: ['abc']"
663