edgedb/tests/test_http_graphql_query.py
Michael J. Sullivan ccd0a3a2e1
Support auth for HTTP endpoints (#6352)
This makes all extension endpoints except for 'auth' will go through
authentication, since it seems like the safe default.

We add a new SIMPLE_HTTP transport method for HTTP endpoints.
(Unfortunately, edgedb+http already uses HTTP, even though
HTTP would be a better name for *this* one.)

We also add a new Password auth method that only works for
SIMPLE_HTTP, and which is the default method for SIMPLE_HTTP.
JWT authentication is also supported, but SCRAM is not.

This is a backwards incompatible change! Unless "Trust" is configured
as a method for HTTP transports (which it will be in dev instances),
HTTP endpoints will stop working when not authenticated to.

Fixes #6345.

Password authentication uses HTTP basic auth.

JWT authentication is done by adding a header of the form:
```
Authorization: bearer <JWT>
```

The username for JWT and Trust auth can be specified with the X-EdgeDB-User
header.
2023-10-25 21:11:45 -07:00

4641 lines
137 KiB
Python

#
# This source file is part of the EdgeDB open source project.
#
# Copyright 2016-present MagicStack Inc. and the EdgeDB authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import os
import uuid
import urllib
import edgedb
from edb.testbase import http as tb
from edb.tools import test
class TestGraphQLFunctional(tb.GraphQLTestCase):
SCHEMA_DEFAULT = os.path.join(os.path.dirname(__file__), 'schemas',
'graphql.esdl')
SCHEMA_OTHER = os.path.join(os.path.dirname(__file__), 'schemas',
'graphql_other.esdl')
SCHEMA_OTHER_DEEP = os.path.join(os.path.dirname(__file__), 'schemas',
'graphql_schema_other_deep.esdl')
SETUP = os.path.join(os.path.dirname(__file__), 'schemas',
'graphql_setup.edgeql')
# GraphQL queries cannot run in a transaction
TRANSACTION_ISOLATION = False
def test_graphql_http_keepalive_01(self):
with self.http_con() as con:
for _ in range(3):
req1_data = {
'query': '''
{
Setting(order: {value: {dir: ASC}}) {
value
}
}
'''
}
data, headers, status = self.http_con_request(
con, req1_data,
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 200)
self.assertNotIn('connection', headers)
self.assertEqual(
headers.get('content-type'),
'application/json')
self.assertEqual(
json.loads(data)['data'],
{'Setting': [{'value': 'blue'}, {'value': 'full'},
{'value': 'none'}]})
req2_data = {
'query': '''
{
NON_EXISTING_TYPE {
name
}
}
'''
}
data, headers, status = self.http_con_request(
con, req2_data,
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 200)
self.assertNotIn('connection', headers)
self.assertEqual(
headers.get('content-type'),
'application/json')
self.assertIn(
'QueryError:',
json.loads(data)['errors'][0]['message'])
def test_graphql_http_errors_01(self):
with self.http_con() as con:
data, headers, status = self.http_con_request(
con, {}, path='non-existant',
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 404)
self.assertEqual(headers['connection'], 'close')
self.assertIn(b'Unknown path', data)
with self.assertRaises(OSError):
self.http_con_request(con, {}, path='non-existant2')
def test_graphql_http_errors_02(self):
with self.http_con() as con:
data, headers, status = self.http_con_request(
con, {},
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 400)
self.assertEqual(headers['connection'], 'close')
self.assertIn(b'query is missing', data)
with self.assertRaises(OSError):
self.http_con_request(con, {}, path='non-existant')
def test_graphql_http_errors_03(self):
with self.http_con() as con:
data, headers, status = self.http_con_request(
con, {'query': 'blah', 'variables': 'bazz'},
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 400)
self.assertEqual(headers['connection'], 'close')
self.assertIn(b'must be a JSON object', data)
with self.assertRaises(OSError):
self.http_con_request(con, {}, path='non-existant')
def test_graphql_http_errors_04(self):
with self.http_con() as con:
con.send(b'blah\r\n\r\n\r\n\r\n')
data, headers, status = self.http_con_request(
con, {'query': 'blah', 'variables': 'bazz'},
headers={
'Authorization': self.make_auth_header(),
},
)
self.assertEqual(status, 400)
self.assertEqual(headers['connection'], 'close')
self.assertIn(b'HttpParserInvalidMethodError', data)
with self.assertRaises(OSError):
self.http_con_request(con, {}, path='non-existant')
def test_graphql_functional_query_01(self):
for _ in range(10): # repeat to test prepared pgcon statements
self.assert_graphql_query_result(r"""
query {
Setting {
name
value
}
}
""", {
'Setting': [{
'name': 'template',
'value': 'blue',
}, {
'name': 'perks',
'value': 'full',
}, {
'name': 'template',
'value': 'none',
}],
}, sort=lambda x: x['value'])
def test_graphql_functional_query_02(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name
age
groups {
id
name
}
}
}
""", {
'User': [{
'name': 'Alice',
'age': 27,
'groups': []
}, {
'name': 'Bob',
'age': 21,
'groups': []
}, {
'name': 'Jane',
'age': 25,
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
}]
}, {
'name': 'John',
'age': 25,
'groups': [{
'id': uuid.UUID,
'name': 'basic',
}]
}],
})
def test_graphql_functional_query_03(self):
self.assert_graphql_query_result(r"""
query mixed {
User {
name
}
Setting {
name
}
}
""", {
'User': [{
'name': 'Alice',
}, {
'name': 'Bob',
}, {
'name': 'Jane',
}, {
'name': 'John',
}],
'Setting': [{
'name': 'perks',
}, {
'name': 'template',
}, {
'name': 'template',
}],
}, sort=lambda x: x['name'])
def test_graphql_functional_query_04(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {name: {eq: "John"}}) {
name
age
groups {
id
name
}
}
}
""", {
'User': [{
'name': 'John',
'age': 25,
'groups': [{
'id': uuid.UUID,
'name': 'basic',
}]
}],
})
def test_graphql_functional_query_05(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'Bogus' on type 'Query'",
_line=3, _col=21):
self.graphql_query(r"""
query {
Bogus {
name,
groups {
id
name
}
}
}
""")
def test_graphql_functional_query_06(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'bogus' on type 'User'",
_line=5, _col=25):
self.graphql_query(r"""
query {
User {
name,
bogus,
groups {
id
name
}
}
}
""")
def test_graphql_functional_query_07(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'age' on type 'NamedObject'",
_line=5, _col=25):
self.graphql_query(r"""
query {
NamedObject {
name,
age,
groups {
id
name
}
}
}
""")
def test_graphql_functional_query_08(self):
self.assert_graphql_query_result(
r"""
query names {
Setting {
name
}
}
query values {
Setting {
value
}
}
""",
{
'Setting': [{
'name': 'perks',
}, {
'name': 'template',
}, {
'name': 'template',
}],
},
sort=lambda x: x['name'],
operation_name='names'
)
self.assert_graphql_query_result(
r"""
query names {
Setting {
name
}
}
query values {
Setting {
value
}
}
""",
{
'Setting': [{
'value': 'blue',
}, {
'value': 'full',
}, {
'value': 'none',
}],
},
sort=lambda x: x['value'],
operation_name='values',
use_http_post=False
)
def test_graphql_functional_query_09(self):
with self.assertRaisesRegex(edgedb.QueryError,
r'provide operation name'):
self.graphql_query('''
query names {
Setting {
name
}
}
query values {
Setting {
value
}
}
''')
def test_graphql_functional_query_10(self):
with self.assertRaisesRegex(edgedb.QueryError,
r'unknown operation named "foo"'):
self.graphql_query('''
query names {
Setting {
name
}
}
query values {
Setting {
value
}
}
''', operation_name='foo')
def test_graphql_functional_query_11(self):
# Test that parse error marshal from the compiler correctly.
with self.assertRaisesRegex(edgedb.QueryError,
r"Expected Name, found '}'",
_line=4, _col=21):
self.graphql_query(r"""
query {
Setting {
}
}
""")
def test_graphql_functional_query_12(self):
# Regression test: variables names were shadowing query names.
self.assert_graphql_query_result(
r"""
query users($name: String, $age: Int64) {
User(filter: {or: [{name: {eq: $name}},
{age: {gt: $age}}]},
order: {name: {dir: ASC}})
{
name
age
}
}
query settings {
Setting {
name
}
}
""",
{
'User': [{
'name': 'Alice',
'age': 27
}],
},
variables={'age': 25, 'name': 'Alice'},
operation_name='users'
)
def test_graphql_functional_query_13(self):
# Test special case errors.
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'gibberish' on type 'Query'\. "
r"There's no corresponding type or alias \"gibberish\" "
r"exposed in EdgeDB\. Please check the configuration settings "
r"for this port to make sure that you're connecting to the "
r"right database\.",
_line=3, _col=21):
self.graphql_query(r"""
query {
gibberish
}
""")
def test_graphql_functional_query_14(self):
# Test special case errors.
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'more__gibberish' on type 'Query'\. "
r"There's no corresponding type or alias \"more::gibberish\" "
r"exposed in EdgeDB\. Please check the configuration settings "
r"for this port to make sure that you're connecting to the "
r"right database\.",
_line=3, _col=21):
self.graphql_query(r"""
query {
more__gibberish
}
""")
def test_graphql_functional_query_15(self):
# Test special case errors.
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'Uxer' on type 'Query'\. "
r"Did you mean 'User'\?",
_line=3, _col=21):
self.graphql_query(r"""
query {
Uxer
}
""")
def test_graphql_functional_query_16(self):
# test filtering by nested object
self.assert_graphql_query_result(r"""
query {
User(filter: {groups: {name: {eq: "basic"}}}) {
name
age
groups {
id
name
}
}
}
""", {
'User': [{
'name': 'John',
'age': 25,
'groups': [{
'id': uuid.UUID,
'name': 'basic',
}]
}],
})
def test_graphql_functional_query_17(self):
# Test unused & null variables
self.assert_graphql_query_result(
r"""
query Person {
Person
{
name
}
}
""",
{
'Person': [{
'name': 'Bob',
}],
},
variables={'name': None},
)
self.assert_graphql_query_result(
r"""
query Person($name: String) {
Person(filter: {name: {eq: $name}})
{
name
}
}
""",
{
'Person': [],
},
variables={'name': None},
)
def test_graphql_functional_query_18(self):
# test filtering by nested object
self.assert_graphql_query_result(r"""
query {
User(filter: {name: {eq: "Alice"}}) {
name
favorites(order: {name: {dir: ASC}}) {
name
}
}
}
""", {
'User': [{
'name': 'Alice',
'favorites': [
{'name': 'basic'},
{'name': 'perks'},
{'name': 'template'},
{'name': 'template'},
{'name': 'unused'},
{'name': 'upgraded'},
]
}],
})
def test_graphql_functional_query_19(self):
# Test built-in object types, by making sure we can query them
# and get some results.
res = self.graphql_query(r"""
{
Object {id}
}
""")
self.assertTrue(len(res) > 0,
'querying "Object" returned no results')
def test_graphql_functional_query_20(self):
# Test built-in object types, by making sure we can query them
# and get some results.
res = self.graphql_query(r"""
{
BaseObject {id}
}
""")
self.assertTrue(len(res) > 0,
'querying "BaseObject" returned no results')
def test_graphql_functional_query_21(self):
# test filtering by nested object
self.assert_graphql_query_result(r"""
query {
User(filter: {name: {eq: "Bob"}}) {
name
fav_users(order: {name: {dir: ASC}}) {
name
# only present in User
active
score
}
}
}
""", {
'User': [{
'name': 'Bob',
'fav_users': [
{
'name': 'Alice',
'active': True,
'score': 5,
},
{
'name': 'Jane',
'active': True,
'score': 1.23,
},
{
'name': 'John',
'active': True,
'score': 3.14,
},
]
}],
})
def test_graphql_functional_query_22(self):
# get an object that has a bunch of readonly properties
self.assert_graphql_query_result(r"""
query {
NotEditable {
__typename
id
computed
once
}
}
""", {
'NotEditable': [{
'__typename': 'NotEditable_Type',
'id': uuid.UUID,
'computed': 'a computed value',
'once': 'init',
}],
})
def test_graphql_functional_query_23(self):
# get an object from a deeply nested module
self.assert_graphql_query_result(r"""
query {
other__deep__NestedMod {
__typename
val
}
}
""", {
'other__deep__NestedMod': [{
'__typename': 'other__deep__NestedMod_Type',
'val': 'in nested module',
}],
})
def test_graphql_functional_alias_01(self):
self.assert_graphql_query_result(
r"""
{
SettingAlias {
__typename
name
value
}
Setting {
__typename
name
value
}
}
""",
{
"SettingAlias": [
{
"__typename": "SettingAlias",
"name": "template",
"value": "blue",
},
{
"__typename": "SettingAlias",
"name": "perks",
"value": "full",
},
{
"__typename": "SettingAlias",
"name": "template",
"value": "none",
},
],
"Setting": [
{
"__typename": "Setting_Type",
"name": "template",
"value": "blue",
},
{
"__typename": "Setting_Type",
"name": "perks",
"value": "full",
},
{
"__typename": "Setting_Type",
"name": "template",
"value": "none",
},
],
},
sort=lambda x: x['value']
)
def test_graphql_functional_alias_02(self):
self.assert_graphql_query_result(
r"""
{
SettingAlias {
__typename
name
value
of_group {
__typename
name
}
}
}
""",
{
"SettingAlias": [
{
"__typename": "SettingAlias",
"name": "template",
"value": "blue",
"of_group": {
"__typename": "UserGroup_Type",
"name": "upgraded",
}
},
{
"__typename": "SettingAlias",
"name": "perks",
"value": "full",
"of_group": {
"__typename": "UserGroup_Type",
"name": "upgraded",
}
},
{
"__typename": "SettingAlias",
"name": "template",
"value": "none",
"of_group": {
"__typename": "UserGroup_Type",
"name": "unused",
}
},
],
},
sort=lambda x: x['value']
)
def test_graphql_functional_alias_03(self):
self.assert_graphql_query_result(
r"""
{
SettingAliasAugmented {
__typename
name
value
of_group {
__typename
name
name_upper
}
}
}
""",
{
"SettingAliasAugmented": [
{
"__typename": "SettingAliasAugmented",
"name": "template",
"value": "blue",
"of_group": {
"__typename":
"__SettingAliasAugmented__of_group",
"name": "upgraded",
"name_upper": "UPGRADED",
}
},
{
"__typename": "SettingAliasAugmented",
"name": "perks",
"value": "full",
"of_group": {
"__typename":
"__SettingAliasAugmented__of_group",
"name": "upgraded",
"name_upper": "UPGRADED",
}
},
{
"__typename": "SettingAliasAugmented",
"name": "template",
"value": "none",
"of_group": {
"__typename":
"__SettingAliasAugmented__of_group",
"name": "unused",
"name_upper": "UNUSED",
}
},
],
},
sort=lambda x: x['value']
)
def test_graphql_functional_alias_04(self):
self.assert_graphql_query_result(
r"""
{
ProfileAlias {
__typename
name
value
owner {
__typename
id
}
}
}
""",
{
"ProfileAlias": [
{
"__typename": "ProfileAlias",
"name": "Alice profile",
"value": "special",
"owner": [
{
"__typename": "User_Type",
"id": uuid.UUID,
}
]
},
{
"__typename": "ProfileAlias",
"name": "Bob profile",
"value": "special",
"owner": [
{
"__typename": "Person_Type",
"id": uuid.UUID,
}
]
}
]
},
)
result = self.graphql_query(r"""
query {
ProfileAlias {
owner {
id
}
}
}
""")
user_id = result['ProfileAlias'][0]['owner'][0]['id']
self.assert_graphql_query_result(f"""
query {{
User(filter: {{id: {{eq: "{user_id}"}}}}) {{
name
}}
}}
""", {
'User': [{'name': 'Alice'}]
})
def test_graphql_functional_alias_05(self):
self.assert_graphql_query_result(
r"""
{
SettingAliasAugmented(
filter: {of_group: {name_upper: {eq: "UPGRADED"}}}
) {
name
of_group {
name
name_upper
}
}
}
""",
{
"SettingAliasAugmented": [
{
"name": "perks",
"of_group": {
"name": "upgraded",
"name_upper": "UPGRADED",
}
},
{
"name": "template",
"of_group": {
"name": "upgraded",
"name_upper": "UPGRADED",
}
},
],
},
sort=lambda x: x['name']
)
def test_graphql_functional_alias_06(self):
self.assert_graphql_query_result(
r"""
{
SettingAliasAugmented(
filter: {name: {eq: "perks"}}
) {
name
of_group(
filter: {name_upper: {gt: "U"}}
) {
name
name_upper
}
}
}
""",
{
"SettingAliasAugmented": [
{
"name": "perks",
"of_group": {
"name": "upgraded",
"name_upper": "UPGRADED",
}
},
],
},
)
def test_graphql_functional_alias_07(self):
self.assert_graphql_query_result(
r"""
{
TwoUsers {
name
initial
}
}
""",
{
"TwoUsers": [
{
"name": "Alice",
"initial": "A",
},
{
"name": "Bob",
"initial": "B",
},
],
},
)
def test_graphql_functional_alias_08(self):
self.assert_graphql_query_result(
r"""
{
TwoUsers(
filter: {initial: {eq: "A"}}
) {
name
initial
}
}
""",
{
"TwoUsers": [
{
"name": "Alice",
"initial": "A",
},
],
},
)
def test_graphql_functional_arguments_01(self):
result = self.graphql_query(r"""
query {
User {
id
name
age
}
}
""")
alice = [res for res in result['User']
if res['name'] == 'Alice'][0]
self.assert_graphql_query_result(f"""
query {{
User(filter: {{id: {{eq: "{alice['id']}"}}}}) {{
id
name
age
}}
}}
""", {
'User': [alice]
})
def test_graphql_functional_arguments_02(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
name: {eq: "Bob"},
active: {eq: true},
age: {eq: 21}
}) {
name
age
groups {
id
name
}
}
}
""", {
'User': [{
'name': 'Bob',
'age': 21,
'groups': [],
}],
})
def test_graphql_functional_arguments_03(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
and: [{name: {eq: "Bob"}}, {active: {eq: true}}],
age: {eq: 21}
}) {
name
score
}
}
""", {
'User': [{
'name': 'Bob',
'score': 4.2,
}],
})
def test_graphql_functional_arguments_04(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
not: {name: {eq: "Bob"}},
age: {eq: 21}
}) {
name
score
}
}
""", {
'User': [],
})
def test_graphql_functional_arguments_05(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {
or: [
{not: {name: {eq: "Bob"}}},
{age: {eq: 20}}
]
},
order: {name: {dir: ASC}}
) {
name
score
}
}
""", {
'User': [
{'name': 'Alice', 'score': 5},
{'name': 'Jane', 'score': 1.23},
{'name': 'John', 'score': 3.14},
],
})
def test_graphql_functional_arguments_06(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {
or: [
{name: {neq: "Bob"}},
{age: {eq: 20}}
]
},
order: {name: {dir: ASC}}
) {
name
score
}
}
""", {
'User': [
{'name': 'Alice', 'score': 5},
{'name': 'Jane', 'score': 1.23},
{'name': 'John', 'score': 3.14},
],
})
def test_graphql_functional_arguments_07(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
name: {ilike: "%o%"},
age: {gt: 22}
}) {
name
age
}
}
""", {
'User': [
{'name': 'John', 'age': 25},
],
}, sort=lambda x: x['name'])
def test_graphql_functional_arguments_08(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
name: {like: "J%"},
score: {
gte: 3
lt: 4.5
}
}) {
name
score
}
}
""", {
'User': [
{'name': 'John', 'score': 3.14},
],
}, sort=lambda x: x['name'])
def test_graphql_functional_arguments_09(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {
name: {ilike: "%e"},
age: {lte: 25}
}) {
name
age
}
}
""", {
'User': [
{'name': 'Jane', 'age': 25},
],
}, sort=lambda x: x['name'])
def test_graphql_functional_arguments_10(self):
self.assert_graphql_query_result(r"""
query {
User(
order: {
age: {dir: DESC}
name: {dir: ASC}
}
) {
name
age
}
}
""", {
'User': [
{'age': 27, 'name': 'Alice'},
{'age': 25, 'name': 'Jane'},
{'age': 25, 'name': 'John'},
{'age': 21, 'name': 'Bob'},
],
})
def test_graphql_functional_arguments_11(self):
self.assert_graphql_query_result(r"""
query {
User(
order: {
name: {dir: ASC}
age: {dir: DESC}
}
) {
name
age
}
}
""", {
'User': [
{'age': 27, 'name': 'Alice'},
{'age': 21, 'name': 'Bob'},
{'age': 25, 'name': 'Jane'},
{'age': 25, 'name': 'John'},
],
})
def test_graphql_functional_arguments_12(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(
order: {
select: {dir: ASC, nulls: BIGGEST}
}
) {
after
select
}
}
""", {
'other__Foo': [
{'after': None, 'select': 'a'},
{'after': 'w', 'select': 'b'},
{'after': 'q', 'select': None},
],
})
def test_graphql_functional_arguments_13(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(
order: {
select: {dir: DESC, nulls: SMALLEST}
}
) {
after
select
}
}
""", {
'other__Foo': [
{'after': 'w', 'select': 'b'},
{'after': None, 'select': 'a'},
{'after': 'q', 'select': None},
],
})
def test_graphql_functional_arguments_14(self):
self.assert_graphql_query_result(r"""
query {
User(
order: {name: {dir: ASC}},
first: 2
) {
name
age
}
}
""", {
'User': [
{'age': 27, 'name': 'Alice'},
{'age': 21, 'name': 'Bob'},
],
})
def test_graphql_functional_arguments_15(self):
self.assert_graphql_query_result(r"""
query {
u0: User(
order: {name: {dir: ASC}},
after: "0",
first: 2
) {
name
}
u1: User(
order: {name: {dir: ASC}},
first: 2
) {
name
}
u2: User(
order: {name: {dir: ASC}},
after: "0",
before: "2"
) {
name
}
u3: User(
order: {name: {dir: ASC}},
before: "2",
last: 1
) {
name
}
}
""", {
'u0': [
{'name': 'Bob'},
{'name': 'Jane'},
],
'u1': [
{'name': 'Alice'},
{'name': 'Bob'},
],
'u2': [
{'name': 'Bob'},
],
'u3': [
{'name': 'Bob'},
],
})
@test.xerror('''
'last' is not fully implemented in all cases and ideally
requires negative OFFSET to be implemented
''')
def test_graphql_functional_arguments_16(self):
self.assert_graphql_query_result(r"""
query {
u4: User(
order: {name: {dir: ASC}},
after: "2",
last: 2
) {
name
}
u5: User(
order: {name: {dir: ASC}},
after: "0",
last: 2
) {
name
}
u6: User(
order: {name: {dir: ASC}},
after: "0",
before: "3",
first: 2,
last: 1
) {
name
}
}
""", {
'u4': [
{'name': 'John'},
],
'u5': [
{'name': 'Jane'},
{'name': 'John'},
],
'u6': [
{'name': 'Jane'},
],
})
def test_graphql_functional_arguments_17(self):
self.assert_graphql_query_result(r"""
query {
User(filter: {name: {eq: "Jane"}}) {
name
groups {
name
settings(
order: {name: {dir: ASC}},
first: 1
) {
name
}
}
}
}
""", {
'User': [{
'name': 'Jane',
'groups': [{
'name': 'upgraded',
'settings': [{
'name': 'perks'
}]
}]
}]
})
def test_graphql_functional_arguments_18(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Expected type String, found 42',
_line=3, _col=46):
self.graphql_query(r"""
query {
User(filter: {name: {eq: 42}}) {
id,
}
}
""")
def test_graphql_functional_arguments_19(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Expected type String, found 20\.5',
_line=3, _col=46):
self.graphql_query(r"""
query {
User(filter: {name: {eq: 20.5}}) {
id,
}
}
""")
def test_graphql_functional_arguments_20(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Expected type Float, found "3\.5"',
_line=3, _col=47):
self.graphql_query(r"""
query {
User(filter: {score: {eq: "3.5"}}) {
id,
}
}
""")
def test_graphql_functional_arguments_21(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Expected type Boolean, found 0',
_line=3, _col=48):
self.graphql_query(r"""
query {
User(filter: {active: {eq: 0}}) {
id,
}
}
""")
def test_graphql_functional_arguments_22(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
# this error message is subpar, but this is what we get
# from postgres, because we transfer bigint values to postgres
# as strings
r'invalid input syntax for type Int64: "aaaaa"',
# _line=5, _col=32,
):
self.graphql_query(r"""
query {
u0: User(
order: {name: {dir: ASC}},
after: "aaaaa",
first: 2
) {
name
}
}
""")
def test_graphql_functional_arguments_23(self):
self.assert_graphql_query_result(r"""
query {
User(
order: {name: {dir: ASC}},
first: 1
) {
name
}
}
""", {
'User': [{
'name': 'Alice',
}]
})
def test_graphql_functional_arguments_24(self):
# Test boolean AND handling {} like Postgres
self.assert_graphql_query_result(r"""
query {
other__Foo(
filter: {
not: {
color: {eq: GREEN},
after: {neq: "b"},
},
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
})
def test_graphql_functional_arguments_25(self):
# Test boolean AND handling {} like Postgres
self.assert_graphql_query_result(r"""
query {
other__Foo(
filter: {
not: {
and: [
{color: {eq: GREEN}},
{after: {neq: "b"}},
]
},
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
})
def test_graphql_functional_arguments_26(self):
# Test boolean OR handling {} like Postgres
self.assert_graphql_query_result(r"""
query {
other__Foo(
filter: {
or: [
{color: {neq: GREEN}},
{after: {eq: "b"}},
]
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
})
def test_graphql_functional_enums_01(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(
order: {color: {dir: DESC}},
first: 1
) {
select
color
}
}
""", {
'other__Foo': [{
'select': None,
'color': "BLUE",
}]
})
def test_graphql_functional_enums_02(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(
order: {color: {dir: ASC}},
after: "0"
) {
select
color
}
}
""", {
"other__Foo": [{
"select": "b",
"color": "GREEN",
}, {
"select": None,
"color": "BLUE",
}]
})
def test_graphql_functional_enums_03(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(
filter: {color: {eq: RED}},
) {
select
color
}
}
""", {
"other__Foo": [{
"select": "a",
"color": "RED",
}]
})
def test_graphql_functional_enums_04(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'String cannot represent a non string value: admin',
_line=4, _col=51):
self.graphql_query(r"""
query {
# enum supplied instead of a string
UserGroup(filter: {name: {eq: admin}}) {
id,
name,
}
}
""")
def test_graphql_functional_fragment_01(self):
self.assert_graphql_query_result(r"""
fragment groupFrag on UserGroup {
id
name
}
query {
User(filter: {name: {eq: "Jane"}}) {
name,
groups {
... groupFrag
}
}
}
""", {
'User': [{
'name': 'Jane',
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
}]
}],
})
def test_graphql_functional_fragment_02(self):
self.assert_graphql_query_result(r"""
fragment userFrag1 on User {
name
... userFrag2
}
fragment userFrag2 on User {
groups {
... groupFrag
}
}
fragment groupFrag on UserGroup {
id
name
}
query {
User(filter: {name: {eq: "Jane"}}) {
... userFrag1
}
}
""", {
'User': [{
'name': 'Jane',
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
}]
}],
})
def test_graphql_functional_fragment_03(self):
self.assert_graphql_query_result(r"""
fragment userFrag2 on User {
groups {
... groupFrag
}
}
fragment groupFrag on UserGroup {
id
name
}
query {
User(filter: {name: {eq: "Jane"}}) {
... on User {
name
... userFrag2
}
}
}
""", {
'User': [{
'name': 'Jane',
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
}]
}],
})
def test_graphql_functional_fragment_04(self):
self.assert_graphql_query_result(r"""
fragment userFrag1 on User {
name
... {
groups {
... groupFrag
}
}
}
fragment groupFrag on UserGroup {
id
name
}
query {
User(filter: {name: {eq: "Jane"}}) {
... userFrag1
}
}
""", {
'User': [{
'name': 'Jane',
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
}]
}],
})
def test_graphql_functional_fragment_05(self):
# ISSUE #3514
#
# Fragment on the actual type should also work.
self.assert_graphql_query_result(r"""
fragment userFrag on User_Type {
active
profile {
value
}
}
query {
User(filter: {name: {eq: "Alice"}}) {
name
... userFrag
}
}
""", {
'User': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
def test_graphql_functional_fragment_06(self):
self.assert_graphql_query_result(r"""
fragment userFrag on User {
id,
name,
}
query {
User(filter: {name: {eq: "Alice"}}) {
... userFrag
}
}
""", {
'User': [{
'id': uuid.UUID,
'name': 'Alice',
}],
})
def test_graphql_functional_fragment_07(self):
self.assert_graphql_query_result(r"""
fragment namedFrag on NamedObject {
id,
name,
}
query {
User(filter: {name: {eq: "Alice"}}) {
... namedFrag
}
}
""", {
'User': [{
'id': uuid.UUID,
'name': 'Alice',
}],
})
def test_graphql_functional_fragment_08(self):
self.assert_graphql_query_result(r"""
fragment namedFrag on NamedObject {
id,
name,
}
fragment userFrag on User {
... namedFrag
age
}
query {
User(filter: {name: {eq: "Alice"}}) {
... userFrag
}
}
""", {
'User': [{
'id': uuid.UUID,
'name': 'Alice',
'age': 27,
}],
})
def test_graphql_functional_fragment_09(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Fragment 'userFrag' cannot be spread here "
r"as objects of type 'UserGroup' can never be of type 'User'.",
_line=9, _col=25):
self.graphql_query(r"""
fragment userFrag on User {
id,
name,
}
query {
UserGroup {
... userFrag
}
}
""")
def test_graphql_functional_fragment_10(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Fragment 'userFrag' cannot be spread here "
r"as objects of type 'UserGroup' can never be of type 'User'.",
_line=8, _col=21):
self.graphql_query(r"""
fragment userFrag on User {
id,
name,
}
fragment groupFrag on UserGroup {
... userFrag
}
query {
User {
... userFrag
groups {
... groupFrag
}
}
}
""")
def test_graphql_functional_fragment_11(self):
self.assert_graphql_query_result(r"""
fragment userFrag on User {
age
score
}
query {
NamedObject {
name
... userFrag
}
}
""", {
"NamedObject": [
{"age": None, "name": "1st", "score": None},
{"age": None, "name": "2nd", "score": None},
{"age": None, "name": "3rd", "score": None},
{"age": None, "name": "4th", "score": None},
{"age": 27, "name": "Alice", "score": 5},
{"age": None, "name": "Alice profile", "score": None},
{"age": 21, "name": "Bob", "score": 4.2},
{"age": None, "name": "Bob profile", "score": None},
{"age": 25, "name": "Jane", "score": 1.23},
{"age": 25, "name": "John", "score": 3.14},
{"age": None, "name": "basic", "score": None},
{"age": None, "name": "perks", "score": None},
{"age": None, "name": "template", "score": None},
{"age": None, "name": "template", "score": None},
{"age": None, "name": "unused", "score": None},
{"age": None, "name": "upgraded", "score": None},
]
}, sort=lambda x: x['name'])
def test_graphql_functional_fragment_12(self):
self.assert_graphql_query_result(r"""
fragment frag on NamedObject {
id,
name,
}
query {
NamedObject {
... frag
}
}
""", {
"NamedObject": [
{"id": uuid.UUID, "name": "1st"},
{"id": uuid.UUID, "name": "2nd"},
{"id": uuid.UUID, "name": "3rd"},
{"id": uuid.UUID, "name": "4th"},
{"id": uuid.UUID, "name": "Alice"},
{"id": uuid.UUID, "name": "Alice profile"},
{"id": uuid.UUID, "name": "Bob"},
{"id": uuid.UUID, "name": "Bob profile"},
{"id": uuid.UUID, "name": "Jane"},
{"id": uuid.UUID, "name": "John"},
{"id": uuid.UUID, "name": "basic"},
{"id": uuid.UUID, "name": "perks"},
{"id": uuid.UUID, "name": "template"},
{"id": uuid.UUID, "name": "template"},
{"id": uuid.UUID, "name": "unused"},
{"id": uuid.UUID, "name": "upgraded"},
]
}, sort=lambda x: x['name'])
def test_graphql_functional_fragment_13(self):
with self.assertRaisesRegex(
edgedb.QueryError,
"Cannot query field 'age' on type 'NamedObject'",
_line=5, _col=21):
self.graphql_query(r"""
fragment frag on NamedObject {
id,
name,
age,
}
query {
User {
... frag
}
}
""")
def test_graphql_functional_fragment_14(self):
with self.assertRaisesRegex(
edgedb.QueryError,
"Cannot query field 'age' on type 'NamedObject'",
_line=7, _col=29):
self.graphql_query(r"""
query {
User {
... on NamedObject {
id,
name,
age,
}
}
}
""")
def test_graphql_functional_fragment_15(self):
self.assert_graphql_query_result(r"""
fragment namedFrag on NamedObject {
id,
name,
... userFrag
}
fragment userFrag on User {
age
}
query {
NamedObject {
... namedFrag
}
}
""", {
"NamedObject": [
{"id": uuid.UUID, "name": "1st", "age": None},
{"id": uuid.UUID, "name": "2nd", "age": None},
{"id": uuid.UUID, "name": "3rd", "age": None},
{"id": uuid.UUID, "name": "4th", "age": None},
{"id": uuid.UUID, "name": "Alice", "age": 27},
{"id": uuid.UUID, "name": "Alice profile", "age": None},
{"id": uuid.UUID, "name": "Bob", "age": 21},
{"id": uuid.UUID, "name": "Bob profile", "age": None},
{"id": uuid.UUID, "name": "Jane", "age": 25},
{"id": uuid.UUID, "name": "John", "age": 25},
{"id": uuid.UUID, "name": "basic", "age": None},
{"id": uuid.UUID, "name": "perks", "age": None},
{"id": uuid.UUID, "name": "template", "age": None},
{"id": uuid.UUID, "name": "template", "age": None},
{"id": uuid.UUID, "name": "unused", "age": None},
{"id": uuid.UUID, "name": "upgraded", "age": None},
]
}, sort=lambda x: x['name'])
def test_graphql_functional_fragment_16(self):
self.assert_graphql_query_result(r"""
fragment namedFrag on NamedObject {
id,
name,
... userFrag
}
fragment userFrag on User {
age
}
query {
User {
... namedFrag
}
}
""", {
"User": [
{"id": uuid.UUID, "name": "Alice", "age": 27},
{"id": uuid.UUID, "name": "Bob", "age": 21},
{"id": uuid.UUID, "name": "Jane", "age": 25},
{"id": uuid.UUID, "name": "John", "age": 25},
]
}, sort=lambda x: x['name'])
@test.xerror(
"Known collation issue on Heroku Postgres",
unless=os.getenv("EDGEDB_TEST_BACKEND_VENDOR") != "heroku-postgres"
)
def test_graphql_functional_fragment_17(self):
self.assert_graphql_query_result(r"""
query {
NamedObject(order: {name: {dir: ASC}}) {
... on User {
age
}
}
}
""", {
"NamedObject": [
{"age": None},
{"age": None},
{"age": None},
{"age": None},
{"age": 27},
{"age": None},
{"age": 21},
{"age": None},
{"age": 25},
{"age": 25},
{"age": None},
{"age": None},
{"age": None},
{"age": None},
{"age": None},
{"age": None},
]
})
def test_graphql_functional_fragment_18(self):
# ISSUE #1800
#
# After using a typed inline fragment the nested fields or
# fields following after the fragment are erroneously using
# the type intersection.
self.assert_graphql_query_result(r"""
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... on User {
active
profile {
value
}
}
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
def test_graphql_functional_fragment_19(self):
# ISSUE #1800
#
# After using a typed inline fragment the nested fields or
# fields following after the fragment are erroneously using
# the type intersection.
self.assert_graphql_query_result(r"""
fragment userFrag on User {
active
profile {
value
}
}
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... userFrag
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
def test_graphql_functional_fragment_20(self):
# ISSUE #1800
#
# After using a typed inline fragment the nested fields or
# fields following after the fragment are erroneously using
# the type intersection.
self.assert_graphql_query_result(r"""
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... on User {
active
profile(filter: {name: {eq: "Alice profile"}}) {
value
}
}
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
self.assert_graphql_query_result(r"""
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... on User {
active
profile(filter: {name: {eq: "no such profile"}}) {
value
}
}
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': None,
}],
})
def test_graphql_functional_fragment_21(self):
# ISSUE #1800
#
# After using a typed inline fragment the nested fields or
# fields following after the fragment are erroneously using
# the type intersection.
self.assert_graphql_query_result(r"""
fragment userFrag on User {
active
profile(filter: {name: {eq: "Alice profile"}}) {
value
}
}
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... userFrag
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
self.assert_graphql_query_result(r"""
fragment userFrag on User {
active
profile(filter: {name: {eq: "no such profile"}}) {
value
}
}
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... userFrag
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': None,
}],
})
def test_graphql_functional_fragment_22(self):
# ISSUE #1800
#
# After using a typed inline fragment the nested fields or
# fields following after the fragment are erroneously using
# the type intersection.
self.assert_graphql_query_result(r"""
query {
NamedObject(filter: {name: {eq: "Alice"}}) {
... on User {
... {
active
profile {
value
}
}
}
name
}
}
""", {
'NamedObject': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
def test_graphql_functional_fragment_23(self):
# ISSUE #3514
#
# Fragment on the actual type should also work.
self.assert_graphql_query_result(r"""
query {
User(filter: {name: {eq: "Alice"}}) {
... on User_Type {
active
profile {
value
}
}
name
}
}
""", {
'User': [{
'name': 'Alice',
'active': True,
'profile': {
'value': 'special',
}
}],
})
def test_graphql_functional_fragment_24(self):
# ISSUE #5985
#
# Types from non-default module should be recognized in fragments.
self.assert_graphql_query_result(r"""
fragment myFrag on other__Foo {
id,
color,
}
query {
other__Foo(filter: {color: {eq: RED}}) {
... myFrag
}
}
""", {
'other__Foo': [{
'id': uuid.UUID,
'color': 'RED',
}],
})
self.assert_graphql_query_result(r"""
fragment myFrag on other__Foo_Type {
id,
color,
}
query {
other__Foo(filter: {color: {eq: RED}}) {
... myFrag
}
}
""", {
'other__Foo': [{
'id': uuid.UUID,
'color': 'RED',
}],
})
def test_graphql_functional_fragment_25(self):
# ISSUE #5985
#
# Types from non-default module should be recognized in fragments.
self.assert_graphql_query_result(r"""
query {
other__Foo(filter: {color: {eq: RED}}) {
... on other__Foo {
id,
color,
}
}
}
""", {
'other__Foo': [{
'id': uuid.UUID,
'color': 'RED',
}],
})
self.assert_graphql_query_result(r"""
query {
other__Foo(filter: {color: {eq: RED}}) {
... on other__Foo_Type {
id,
color,
}
}
}
""", {
'other__Foo': [{
'id': uuid.UUID,
'color': 'RED',
}],
})
def test_graphql_functional_directives_01(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name @include(if: true),
groups @include(if: false) {
id
name
}
}
}
""", {
"User": [
{"name": "Alice"},
{"name": "Bob"},
{"name": "Jane"},
{"name": "John"},
]
})
def test_graphql_functional_directives_02(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name @skip(if: true),
groups @skip(if: false) {
id @skip(if: true)
name @skip(if: false)
}
}
}
""", {
"User": [
{"groups": []},
{"groups": []},
{"groups": [{"name": "upgraded"}]},
{"groups": [{"name": "basic"}]},
]
})
def test_graphql_functional_directives_03(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name @skip(if: true), @include(if: true),
groups @skip(if: false), @include(if: true) {
id @skip(if: true), @include(if: false)
name @skip(if: false), @include(if: true)
}
}
}
""", {
"User": [
{"groups": []},
{"groups": []},
{"groups": [{"name": "upgraded"}]},
{"groups": [{"name": "basic"}]},
]
})
def test_graphql_functional_directives_04(self):
self.assert_graphql_query_result(r"""
fragment userFrag1 on User {
name
... {
groups @include(if: false) {
... groupFrag
}
}
}
fragment groupFrag on UserGroup {
id
name
}
query {
User(order: {name: {dir: ASC}}) {
... userFrag1
}
}
""", {
"User": [
{"name": "Alice"},
{"name": "Bob"},
{"name": "Jane"},
{"name": "John"},
]
})
def test_graphql_functional_directives_05(self):
self.assert_graphql_query_result(r"""
fragment userFrag1 on User {
name
... @skip(if: true) {
groups {
... groupFrag
}
}
}
fragment groupFrag on UserGroup {
id
name
}
query {
User(order: {name: {dir: ASC}}) {
... userFrag1
}
}
""", {
"User": [
{"name": "Alice"},
{"name": "Bob"},
{"name": "Jane"},
{"name": "John"},
]
})
def test_graphql_functional_directives_06(self):
self.assert_graphql_query_result(r"""
fragment userFrag1 on User {
name
... {
groups {
... groupFrag @skip(if: true)
name
}
}
}
fragment groupFrag on UserGroup {
id
}
query {
User(order: {name: {dir: ASC}}) {
... userFrag1
}
}
""", {
"User": [
{"name": "Alice", "groups": []},
{"name": "Bob", "groups": []},
{"name": "Jane", "groups": [{"name": "upgraded"}]},
{"name": "John", "groups": [{"name": "basic"}]},
]
})
def test_graphql_functional_directives_07(self):
with self.assertRaisesRegex(
edgedb.QueryError,
'Expected type Boolean!, found "true".',
_line=4, _col=43):
self.graphql_query(r"""
query {
User {
name @include(if: "true"),
id
}
}
""")
def test_graphql_functional_typename_01(self):
self.assert_graphql_query_result(r"""
query {
User {
name
__typename
groups {
id
name
__typename
}
}
}
""", {
'User': [{
'name': 'Alice',
'__typename': 'User_Type',
'groups': []
}, {
'name': 'Bob',
'__typename': 'Person_Type',
'groups': []
}, {
'name': 'Jane',
'__typename': 'User_Type',
'groups': [{
'id': uuid.UUID,
'name': 'upgraded',
'__typename': 'UserGroup_Type',
}]
}, {
'name': 'John',
'__typename': 'User_Type',
'groups': [{
'id': uuid.UUID,
'name': 'basic',
'__typename': 'UserGroup_Type',
}]
}],
}, sort=lambda x: x['name'])
def test_graphql_functional_typename_02(self):
self.assert_graphql_query_result(r"""
query {
__typename
__schema {
__typename
}
}
""", {
'__typename': 'Query',
'__schema': {
'__typename': '__Schema',
},
})
def test_graphql_functional_typename_03(self):
self.assert_graphql_query_result(r"""
query {
foo: __typename
User(order: {name: {dir: ASC}}) {
name
bar: __typename
}
}
""", {
"foo": "Query",
"User": [
{"bar": "User_Type", "name": "Alice"},
{"bar": "Person_Type", "name": "Bob"},
{"bar": "User_Type", "name": "Jane"},
{"bar": "User_Type", "name": "John"},
]
})
def test_graphql_functional_typename_04(self):
self.assert_graphql_query_result(r"""
query {
other__Foo(order: {color: {dir: ASC}}) {
__typename
color
}
}
""", {
"other__Foo": [
{"__typename": "other__Foo_Type", "color": "RED"},
{"__typename": "other__Foo_Type", "color": "GREEN"},
{"__typename": "other__Foo_Type", "color": "BLUE"},
]
})
def test_graphql_functional_scalars_01(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_bool
p_str
p_datetime
p_local_datetime
p_local_date
p_local_time
p_duration
p_int16
p_int32
p_int64
p_bigint
p_float32
p_float64
p_decimal
}
}
""", {
"ScalarTest": [{
'p_bool': True,
'p_str': 'Hello world',
'p_datetime': '2018-05-07T20:01:22.306916+00:00',
'p_local_datetime': '2018-05-07T20:01:22.306916',
'p_local_date': '2018-05-07',
'p_local_time': '20:01:22.306916',
'p_duration': 'PT20H',
'p_int16': 12345,
'p_int32': 1234567890,
'p_int64': 1234567890123,
'p_bigint': 123456789123456789123456789,
'p_float32': 2.5,
'p_float64': 2.5,
'p_decimal':
123456789123456789123456789.123456789123456789123456789,
}]
})
def test_graphql_functional_scalars_02(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_json
}
}
""", {
"ScalarTest": [{
'p_json': {"foo": [1, None, "bar"]},
}]
})
def test_graphql_functional_scalars_03(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'p_bytes' on type 'ScalarTest'",
_line=4, _col=25):
self.graphql_query(r"""
query {
ScalarTest {
p_bytes
}
}
""")
def test_graphql_functional_scalars_04(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_array_json
}
}
""", {
"ScalarTest": [{
'p_array_json': ["hello", "world"],
}]
})
def test_graphql_functional_scalars_05(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Cannot query field 'p_array_bytes' on type 'ScalarTest'",
_line=4, _col=25):
self.graphql_query(r"""
query {
ScalarTest {
p_array_bytes
}
}
""")
def test_graphql_functional_scalars_06(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_posint
}
}
""", {
"ScalarTest": [{
'p_posint': 42,
}]
})
def test_graphql_functional_scalars_07(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_array_str
}
}
""", {
"ScalarTest": [{
'p_array_str': ['hello', 'world'],
}]
})
def test_graphql_functional_scalars_08(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_tuple
}
}
""", {
"ScalarTest": [{
'p_tuple': [123, "test"],
}]
})
def test_graphql_functional_scalars_09(self):
self.assert_graphql_query_result(r"""
query {
ScalarTest {
p_array_tuple
}
}
""", {
"ScalarTest": [{
'p_array_tuple': [["hello", True], ["world", False]],
}]
})
def test_graphql_functional_range_01(self):
self.assert_graphql_query_result(r"""
query {
RangeTest(order: {name: {dir: ASC}}) {
name
rval
mval
rdate
mdate
}
}
""", {
"RangeTest": [
{
"name": "missing boundaries",
"rval": {
"empty": True,
},
"mval": [
{
"lower": None,
"upper": None,
"inc_lower": False,
"inc_upper": False,
}
],
"rdate": {
"empty": True,
},
"mdate": [
{
"lower": None,
"upper": None,
"inc_lower": False,
"inc_upper": False,
},
]
},
{
"name": "test01",
"rval": {
"lower": -1.3,
"upper": 1.2,
"inc_lower": True,
"inc_upper": False
},
"mval": [
{
"lower": None,
"upper": -10,
"inc_lower": False,
"inc_upper": False
},
{
"lower": -1.3,
"upper": 1.2,
"inc_lower": True,
"inc_upper": False
},
{
"lower": 10,
"upper": None,
"inc_lower": True,
"inc_upper": False
}
],
"rdate": {
"lower": "2018-01-23",
"upper": "2023-07-25",
"inc_lower": True,
"inc_upper": False
},
"mdate": [
{
"lower": "2018-01-23",
"upper": "2023-07-25",
"inc_lower": True,
"inc_upper": False
},
{
"lower": "2025-11-22",
"upper": None,
"inc_lower": True,
"inc_upper": False
}
]
}
]
})
def test_graphql_functional_duplicates_01(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name
name
name
age
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_02(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
name @include(if: true)
age
name @include(if: true)
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_03(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
... on User @skip(if: false) {
name @include(if: true)
}
age
name @include(if: true)
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_04(self):
self.assert_graphql_query_result(r"""
fragment f1 on User {
name @include(if: true)
}
fragment f2 on User {
age
name @include(if: true)
... f1
}
query {
User(order: {name: {dir: ASC}}) {
... f2
age
name @include(if: true)
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_05(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
age
name
name @include(if: true)
name @skip(if: false)
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_06(self):
self.assert_graphql_query_result(r"""
query {
User(order: {name: {dir: ASC}}) {
... @skip(if: false) {
name @include(if: true)
}
age
name
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_duplicates_07(self):
self.assert_graphql_query_result(r"""
fragment f1 on User {
name @skip(if: false)
}
fragment f2 on User {
age
name @include(if: true)
... f1
}
query {
User(order: {name: {dir: ASC}}) {
... f2
age
name @include(if: true)
}
}
""", {
'User': [
{"age": 27, "name": "Alice"},
{"age": 21, "name": "Bob"},
{"age": 25, "name": "Jane"},
{"age": 25, "name": "John"},
]
})
def test_graphql_functional_variables_01(self):
query = r"""
query($name: String) {
User(filter: {name: {eq: $name}}) {
name,
groups {
name
}
}
}
"""
expected_result = {
'User': [{
'name': 'John',
'groups': [{
'name': 'basic',
}]
}],
}
self.assert_graphql_query_result(
query,
expected_result,
variables={'name': 'John'},
use_http_post=True
)
self.assert_graphql_query_result(
query,
expected_result,
variables={'name': 'John'},
use_http_post=False
)
def test_graphql_functional_variables_02(self):
self.assert_graphql_query_result(
r"""
query($name: String, $age: Int64) {
User(filter: {or: [{name: {eq: $name}},
{age: {gt: $age}}]},
order: {name: {dir: ASC}})
{
name
age
}
}
""",
{
"User": [
{
"name": "Alice",
"age": 27,
},
{
"name": "Jane",
"age": 25,
},
{
"name": "John",
"age": 25,
},
]
},
variables={
"age": 24,
"name": "Alice"
}
)
def test_graphql_functional_variables_03(self):
self.assert_graphql_query_result(r"""
query($val: Int = 3) {
User(filter: {score: {eq: $val}}) {
id,
}
}
""", {
'User': [],
})
def test_graphql_functional_variables_04(self):
self.assert_graphql_query_result(r"""
query($val: Boolean = true) {
User(order: {name: {dir: ASC}}) {
name @include(if: $val),
groups @skip(if: $val) {
name
}
}
}
""", {
"User": [
{"name": "Alice"},
{"name": "Bob"},
{"name": "Jane"},
{"name": "John"},
]
})
def test_graphql_functional_variables_05(self):
self.assert_graphql_query_result(r"""
query($val: Boolean! = true) {
User(order: {name: {dir: ASC}}) {
name @include(if: $val),
id
}
}
""", {
"User": [
{"name": "Alice"},
{"name": "Bob"},
{"name": "Jane"},
{"name": "John"},
]
})
def test_graphql_functional_variables_06(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"no value for the 'val' variable",
_line=4, _col=31):
self.graphql_query(r"""
query($val: Boolean!) {
User {
name @include(if: $val),
id
}
}
""")
def test_graphql_functional_variables_07(self):
self.assert_graphql_query_result(r"""
query($val: String = "John") {
User(filter: {name: {eq: $val}}) {
age,
}
}
""", {
"User": [
{"age": 25},
]
})
def test_graphql_functional_variables_08(self):
self.assert_graphql_query_result(r"""
query($val: Int64 = 20) {
User(filter: {age: {eq: $val}}) {
name,
}
}
""", {
"User": []
})
def test_graphql_functional_variables_09(self):
self.assert_graphql_query_result(r"""
query($val: Float = 3.5) {
User(filter: {score: {eq: $val}}) {
name,
}
}
""", {
"User": []
})
def test_graphql_functional_variables_10(self):
self.assert_graphql_query_result(r"""
query($val: Int = 3) {
User(filter: {score: {eq: $val}}) {
id,
}
}
""", {
"User": []
})
def test_graphql_functional_variables_11(self):
self.assert_graphql_query_result(r"""
query($val: Float = 3) {
User(filter: {score: {eq: $val}}) {
id,
}
}
""", {
"User": []
})
def test_graphql_functional_variables_12(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Boolean cannot represent a non boolean value: 1',
_line=2, _col=39):
self.graphql_query(r"""
query($val: Boolean = 1) {
User {
name @include(if: $val),
id
}
}
""")
def test_graphql_functional_variables_13(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Boolean cannot represent a non boolean value: "1"',
_line=2, _col=39):
self.graphql_query(r"""
query($val: Boolean = "1") {
User {
name @include(if: $val),
id
}
}
""")
def test_graphql_functional_variables_14(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Boolean cannot represent a non boolean value: 1\.3',
_line=2, _col=39):
self.graphql_query(r"""
query($val: Boolean = 1.3) {
User {
name @include(if: $val),
id
}
}
""")
def test_graphql_functional_variables_15(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'String cannot represent a non string value: 1',
_line=2, _col=38):
self.graphql_query(r"""
query($val: String = 1) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_16(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'String cannot represent a non string value: 1\.1',
_line=2, _col=38):
self.graphql_query(r"""
query($val: String = 1.1) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_17(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'String cannot represent a non string value: true',
_line=2, _col=38):
self.graphql_query(r"""
query($val: String = true) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_18(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Int cannot represent non-integer value: 1\.1',
_line=2, _col=35):
self.graphql_query(r"""
query($val: Int = 1.1) {
User(filter: {age: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_19(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Int cannot represent non-integer value: "1"',
_line=2, _col=35):
self.graphql_query(r"""
query($val: Int = "1") {
User(filter: {age: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_20(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Int cannot represent non-integer value: true',
_line=2, _col=35):
self.graphql_query(r"""
query($val: Int = true) {
User(filter: {age: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_21(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Float cannot represent non numeric value: "1"',
_line=2, _col=37):
self.graphql_query(r"""
query($val: Float = "1") {
User(filter: {score: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_22(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'Float cannot represent non numeric value: true',
_line=2, _col=37):
self.graphql_query(r"""
query($val: Float = true) {
User(filter: {score: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_23(self):
self.assert_graphql_query_result(r"""
query($val: ID = "00000000-3576-11e9-8723-cf18c8790091") {
User(filter: {id: {eq: $val}}) {
name
}
}
""", {
"User": []
})
def test_graphql_functional_variables_25(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'ID cannot represent a non-string and non-integer.+: 1\.1',
_line=2, _col=34):
self.graphql_query(r"""
query($val: ID = 1.1) {
User(filter: {id: {eq: $val}}) {
name
}
}
""")
def test_graphql_functional_variables_26(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r'ID cannot represent a non-string and non-integer.+: true',
_line=2, _col=34):
self.graphql_query(r"""
query($val: ID = true) {
User(filter: {id: {eq: $val}}) {
name
}
}
""")
def test_graphql_functional_variables_27(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variable '\$val' of type '\[String\]' used in position "
r"expecting type 'String'\."):
self.graphql_query(r"""
query($val: [String] = "Foo") {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_28(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variable '\$val' of type '\[String\]' used in position "
r"expecting type 'String'\."):
self.graphql_query(r"""
query($val: [String]) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_29(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variable '\$val' of type '\[String\]!' used in position "
r"expecting type 'String'."):
self.graphql_query(r"""
query($val: [String]!) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_30(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"parameter \$val is required"):
self.graphql_query(r"""
query($val: String!) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_31(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"String cannot represent a non string value: 123",
_line=2, _col=48):
self.graphql_query(r"""
query($val: [String] = ["Foo", 123]) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_32(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variable '\$val' of type '\[String\]' used in position "
r"expecting type 'String'\."):
self.graphql_query(r"""
query($val: [String]) {
User(filter: {name: {eq: $val}}) {
id
}
}
""")
def test_graphql_functional_variables_33(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
r'expected JSON string or null; got JSON number'):
self.graphql_query(
r"""
query($name: String) {
User(filter: {name: {eq: $name}}) {
name,
groups {
name
}
}
}
""",
variables={'name': 11})
def test_graphql_functional_variables_34(self):
# Test multiple requests to make sure that caching works correctly
for _ in range(2):
for _ in range(2):
self.assert_graphql_query_result(
r"""
query($val: Boolean!, $min_age: Int64!) {
User(filter: {age: {gt: $min_age}}) {
name @include(if: $val),
age
}
}
""",
{'User': [{'age': 27, 'name': 'Alice'}]},
variables={'val': True, 'min_age': 26}
)
self.assert_graphql_query_result(
r"""
query($val: Boolean!, $min_age: Int64!) {
User(filter: {age: {gt: $min_age}}) {
name @include(if: $val),
age
}
}
""",
{'User': [{'age': 27}]},
variables={'val': False, 'min_age': 26}
)
def test_graphql_functional_variables_35(self):
self.assert_graphql_query_result(
r"""
query($limit: Int!) {
User(
order: {name: {dir: ASC}},
first: $limit
) {
name
}
}
""",
{
'User': [{
'name': 'Alice',
}]
},
variables={'limit': 1},
)
def test_graphql_functional_variables_36(self):
self.assert_graphql_query_result(
r"""
query($idx: String!) {
User(
order: {name: {dir: ASC}},
# this is actually equivalent to OFFSET 2,
# since 'after' doesn't include the value
# referenced by the index
after: $idx
) {
name
}
}
""",
{
'User': [{
'name': 'Jane',
}, {
'name': 'John',
}]
},
variables={'idx': '1'},
)
def test_graphql_functional_variables_37(self):
self.assert_graphql_query_result(
r"""
query($idx: String!, $num: Int!) {
User(
order: {name: {dir: ASC}},
# this is actually equivalent to OFFSET 2,
# since 'after' doesn't include the value
# referenced by the index
after: $idx,
first: $num
) {
name
}
}
""",
{
'User': [{
'name': 'Jane',
}]
},
variables={'idx': '1', 'num': 1},
)
def test_graphql_functional_variables_38(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variable '\$limit' of type 'String!' used in position "
r"expecting type 'Int'."):
self.graphql_query(
r"""
query($limit: String!) {
User(
order: {name: {dir: ASC}},
first: $limit
) {
name
}
}
""",
variables={'limit': '1'},
)
# FIXME: the error here comes all the way from Postgres and as
# such refers to Postgres types, ideally we'd like to have an
# error message expressed in terms of GraphQL types.
def test_graphql_functional_variables_39(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
r'expected JSON number.+got JSON string'):
self.graphql_query(
r"""
query($limit: Int!) {
User(
order: {name: {dir: ASC}},
first: $limit
) {
name
}
}
""",
variables={'limit': '1'},
)
def test_graphql_functional_variables_40(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Only scalar defaults are allowed\. "
r"Variable 'val' has non-scalar default value\."):
self.graphql_query(r"""
query($val: FilterFloat = {eq: 3.0}) {
User(filter: {score: $val}) {
id,
}
}
""")
def test_graphql_functional_variables_41(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variables starting with '_edb_arg__' are prohibited"):
self.graphql_query(r"""
query($_edb_arg__1: Int!) {
User(limit: $_edb_arg__1) {
id,
}
}
""", variables={'_edb_arg__1': 1})
def test_graphql_functional_variables_42(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Variables starting with '_edb_arg__' are prohibited"):
self.graphql_query(r"""
query($_edb_arg__1: Int = 1) {
User(limit: $_edb_arg__1) {
id,
}
}
""")
def test_graphql_functional_variables_43(self):
with self.assertRaisesRegex(
edgedb.QueryError,
r"Only scalar input variables are allowed\. "
r"Variable 'f' has non-scalar value\."):
self.graphql_query(r"""
query user($f: FilterUser!) {
User(filter: $f) {
name
}
}
""", variables={"f": {"name": {"eq": "Alice"}}})
def test_graphql_functional_variables_44(self):
self.assert_graphql_query_result(
r"""
query foo($color: other__ColorEnum!) {
other__Foo(
filter: {color: {eq: $color}},
) {
select
color
}
}
""", {
"other__Foo": [{
"select": "a",
"color": "RED",
}]
},
variables={"color": "RED"},
)
def test_graphql_functional_variables_45(self):
self.assert_graphql_query_result(
r"""
query foo($color: other__ColorEnum! = GREEN) {
other__Foo(
filter: {color: {eq: $color}},
) {
select
color
}
}
""", {
"other__Foo": [{
"select": "b",
"color": "GREEN",
}]
},
)
def test_graphql_functional_variables_46(self):
self.assert_graphql_query_result(
r"""
query($val: JSON) {
ScalarTest(filter: {p_json: {eq: $val}}) {
p_json
}
}
""", {
"ScalarTest": [{
'p_json': {"foo": [1, None, "bar"]},
}]
},
# JSON can only be passed as a variable.
variables={"val": {"foo": [1, None, "bar"]}},
)
def test_graphql_functional_variables_47(self):
# Test boolean AND handling {} like Postgres
self.assert_graphql_query_result(
r"""
query($color: other__ColorEnum!, $after: String!) {
other__Foo(
filter: {
not: {
color: {eq: $color},
after: {neq: $after},
},
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
},
variables={'color': 'GREEN', 'after': 'b'},
)
def test_graphql_functional_variables_48(self):
# Test boolean AND handling {} like Postgres
self.assert_graphql_query_result(
r"""
query($color: other__ColorEnum!, $after: String!) {
other__Foo(
filter: {
not: {
and: [
{color: {eq: $color}},
{after: {neq: $after}},
]
},
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
},
variables={'color': 'GREEN', 'after': 'b'},
)
def test_graphql_functional_variables_49(self):
# Test boolean OR handling {} like Postgres
self.assert_graphql_query_result(
r"""
query($color: other__ColorEnum!, $after: String!) {
other__Foo(
filter: {
or: [
{color: {neq: $color}},
{after: {eq: $after}},
]
},
order: {color: {dir: ASC}}
) {
select
after
color
}
}
""", {
"other__Foo": [{
"select": "a",
"after": None,
"color": "RED",
}, {
"select": None,
"after": "q",
"color": "BLUE",
}]
},
variables={'color': 'GREEN', 'after': 'b'},
)
def test_graphql_functional_inheritance_01(self):
# ISSUE: #709
#
# Testing type and sub-type.
self.assert_graphql_query_result(r"""
query {
Bar {
__typename
q
}
}
""", {
'Bar': [{
'__typename': 'Bar_Type',
'q': 'bar',
}, {
'__typename': 'Bar2_Type',
'q': 'bar2',
}],
}, sort=lambda x: x['q'])
def test_graphql_functional_inheritance_02(self):
# ISSUE: #709
#
# Testing type and sub-type, with a covariant lint target.
self.assert_graphql_query_result(r"""
query {
Rab {
__typename
blah {
__typename
q
}
}
}
""", {
'Rab': [{
'__typename': 'Rab_Type',
'blah': {
'__typename': 'Bar_Type',
'q': 'bar',
}
}, {
'__typename': 'Rab2_Type',
'blah': {
'__typename': 'Bar2_Type',
'q': 'bar2',
}
}],
}, sort=lambda x: x['blah']['q'])
def test_graphql_functional_inheritance_03(self):
# ISSUE: #709
#
# Testing type and sub-type, with a covariant lint target.
#
# Rab2 must keep the target type of the link same as the base
# type, due to limitations of GraphQL inheritance. But as long
# as the actual target type is known, it can be explicitly
# referenced.
self.assert_graphql_query_result(r"""
query {
Rab2 {
blah {
__typename
... on Bar2 {
q
w
}
}
}
}
""", {
'Rab2': [{
'blah': {
'__typename': 'Bar2_Type',
'q': 'bar2',
'w': 'special'
}
}],
})
def test_graphql_functional_order_01(self):
# Test order by nested objects
self.assert_graphql_query_result(r"""
query {
Rab(order: {blah: {q: {dir: DESC}}}) {
blah {
q
}
}
}
""", {
"Rab": [
{
"blah": {
"q": "bar2"
}
},
{
"blah": {
"q": "bar"
},
}
]
})
def test_graphql_functional_order_02(self):
# Test order by nested objects
self.assert_graphql_query_result(r"""
query {
SettingAliasAugmented(
order: {
of_group: {name_upper: {dir: ASC}},
name: {dir: DESC}
}
) {
name
of_group {
name_upper
}
}
}
""", {
"SettingAliasAugmented": [
{
"name": "template",
"of_group": {
"name_upper": "UNUSED"
},
},
{
"name": "template",
"of_group": {
"name_upper": "UPGRADED"
},
},
{
"name": "perks",
"of_group": {
"name_upper": "UPGRADED"
},
},
]
})
def test_graphql_functional_order_03(self):
# Test order by nested objects
self.assert_graphql_query_result(r"""
query {
LinkedList(order: {
next: {next: {name: {dir: DESC, nulls: SMALLEST}}},
name: {dir: ASC}
}) {
name
}
}
""", {
"LinkedList": [
{
"name": "2nd"
},
{
"name": "1st"
},
{
"name": "3rd"
},
{
"name": "4th"
}
]
})
def test_graphql_functional_order_04(self):
# Test order by nested objects
self.assert_graphql_query_result(r"""
query {
User(order: {
profile: {
value: {dir: ASC},
name: {dir: DESC}
}
}) {
name
profile {
name
value
}
}
}
""", {
"User": [
{
"name": "John",
"profile": None,
},
{
"name": "Jane",
"profile": None,
},
{
"name": "Bob",
"profile": {
"name": "Bob profile",
"value": "special",
},
},
{
"name": "Alice",
"profile": {
"name": "Alice profile",
"value": "special",
},
}
]
})
def test_graphql_functional_exists_01(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {profile: {exists: true}},
order: {name: {dir: ASC}}
) {
name
profile {
name
}
}
}
""", {
"User": [
{
"name": "Alice",
"profile": {
"name": "Alice profile",
},
},
{
"name": "Bob",
"profile": {
"name": "Bob profile",
},
},
]
})
def test_graphql_functional_exists_02(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {profile: {exists: false}},
order: {name: {dir: ASC}}
) {
name
profile {
name
}
}
}
""", {
"User": [
{
"name": "Jane",
"profile": None,
},
{
"name": "John",
"profile": None,
},
]
})
def test_graphql_functional_exists_03(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {groups: {settings: {exists: false}}},
order: {name: {dir: ASC}}
) {
name
groups {
name
settings {
name
}
}
}
}
""", {
"User": [
{
"name": "Alice",
"groups": [],
},
{
"name": "Bob",
"groups": [],
},
{
"name": "John",
"groups": [
{
"name": "basic",
"settings": [],
}
],
},
]
})
def test_graphql_functional_exists_04(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {groups: {settings: {exists: true}}}
) {
name
groups {
name
settings(order: {name: {dir: ASC}}) {
name
}
}
}
}
""", {
"User": [
{
"name": "Jane",
"groups": [
{
"name": "upgraded",
"settings": [
{
"name": "perks",
},
{
"name": "template",
},
]
}
]
}
]
})
def test_graphql_functional_exists_05(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {groups: {settings: {id: {exists: false}}}},
order: {name: {dir: ASC}}
) {
name
groups {
name
settings {
name
}
}
}
}
""", {
"User": [
{
"name": "Alice",
"groups": [],
},
{
"name": "Bob",
"groups": [],
},
{
"name": "John",
"groups": [
{
"name": "basic",
"settings": [],
}
],
},
]
})
def test_graphql_functional_exists_06(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {groups: {settings: {id: {exists: true}}}}
) {
name
groups {
name
settings(order: {name: {dir: ASC}}) {
name
}
}
}
}
""", {
"User": [
{
"name": "Jane",
"groups": [
{
"name": "upgraded",
"settings": [
{
"name": "perks",
},
{
"name": "template",
},
]
}
]
}
]
})
def test_graphql_functional_in_01(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {name: {in: ["Alice", "Bob"]}},
order: {name: {dir: ASC}}
) {
name
}
}
""", {
"User": [
{
"name": "Alice",
},
{
"name": "Bob",
},
]
})
def test_graphql_functional_in_02(self):
self.assert_graphql_query_result(r"""
query {
User(
filter: {name: {in: ["Zoe", "Alice"]}},
order: {name: {dir: ASC}}
) {
name
}
}
""", {
"User": [
{
"name": "Alice",
},
]
})
def test_graphql_functional_type_union_01(self):
self.assert_graphql_query_result(r"""
query {
Combo(
order: {name: {dir: ASC}}
) {
name
data {
__typename
name
value
... on Profile {
tags
}
}
}
}
""", {
"Combo": [
{
"name": "combo 0",
"data": None,
},
{
"name": "combo 1",
"data": {
"__typename": "Setting_Type",
"name": "template",
"value": "blue",
},
},
{
"name": "combo 2",
"data": {
"__typename": "Profile_Type",
"name": "Alice profile",
"value": "special",
"tags": ['1st', '2nd'],
},
},
]
})
def test_graphql_functional_edgedb_errors_01(self):
with self.assertRaisesRegex(
edgedb.DivisionByZeroError,
r"division by zero"
):
self.graphql_query(r"""
query {
ErrorTest {
div_by_val
}
}
""")
def test_graphql_functional_edgedb_errors_02(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
r"LIMIT must not be negative"
):
self.graphql_query(r"""
query {
ErrorTest(first: -2) {
text
}
}
""")
def test_graphql_functional_edgedb_errors_03(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
r"OFFSET must not be negative"
):
self.graphql_query(r"""
query {
ErrorTest(after: "-2") {
text
}
}
""")
def test_graphql_functional_edgedb_errors_04(self):
with self.assertRaisesRegex(
edgedb.InvalidValueError,
r"invalid regular expression"
):
self.graphql_query(r"""
query {
ErrorTest {
re_text
}
}
""")
def test_graphql_globals_01(self):
Q = r'''query { GlobalTest { gstr, garray, gid, gdef, gdef2 } }'''
for use_http_post in [True, False]:
self.assert_graphql_query_result(
Q,
{
"GlobalTest": [{
'gstr': 'WOO',
'gid': '84ed3d8b-5eb2-4d31-9e1e-efb66180445c',
'gdef': '',
'gdef2': None, 'garray': ['x', 'y', 'z']
}],
},
use_http_post=use_http_post,
deprecated_globals={
'default::test_global_str': "WOO",
'default::test_global_id': (
'84ed3d8b-5eb2-4d31-9e1e-efb66180445c'),
'default::test_global_def': None,
'default::test_global_def2': None,
'default::test_global_array': ['x', 'y', 'z'],
},
)
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
deprecated_globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
)
self.assert_graphql_query_result(
Q,
{'GlobalTest': [
{'gstr': None, 'garray': None, 'gid': None,
'gdef': '', 'gdef2': ''}
]},
use_http_post=use_http_post,
)
def test_graphql_globals_02(self):
Q = r'''query { GlobalTest { gstr, garray, gid, gdef, gdef2 } }'''
for use_http_post in [True, False]:
self.assert_graphql_query_result(
Q,
{
"GlobalTest": [{
'gstr': 'WOO',
'gid': '84ed3d8b-5eb2-4d31-9e1e-efb66180445c',
'gdef': '',
'gdef2': None, 'garray': ['x', 'y', 'z']
}],
},
use_http_post=use_http_post,
globals={
'default::test_global_str': "WOO",
'default::test_global_id': (
'84ed3d8b-5eb2-4d31-9e1e-efb66180445c'),
'default::test_global_def': None,
'default::test_global_def2': None,
'default::test_global_array': ['x', 'y', 'z'],
},
)
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
)
self.assert_graphql_query_result(
Q,
{'GlobalTest': [
{'gstr': None, 'garray': None, 'gid': None,
'gdef': '', 'gdef2': ''}
]},
use_http_post=use_http_post,
)
def test_graphql_globals_03(self):
Q = r'''query { GlobalTest { gstr, garray, gid, gdef, gdef2 } }'''
# Test that globals work fine if both the deprecated way and the new
# way are used and the values match.
for use_http_post in [True, False]:
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
deprecated_globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
)
def test_graphql_globals_04(self):
Q = r'''query { GlobalTest { gstr, garray, gid, gdef, gdef2 } }'''
# Test that globals must match if two ways are used to specify them.
for use_http_post in [True, False]:
with self.assertRaises(
urllib.error.HTTPError,
):
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
deprecated_globals={
'default::test_global_def': 'not x',
'default::test_global_def2': 'not x',
},
)
def test_graphql_globals_05(self):
Q = r'''query { GlobalTest { gstr, garray, gid, gdef, gdef2 } }'''
# Test that globals must match if two ways are used to specify them,
# even if one of these is an empty dict.
for use_http_post in [True, False]:
with self.assertRaises(
urllib.error.HTTPError,
):
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
deprecated_globals={},
)
for use_http_post in [True, False]:
with self.assertRaises(
urllib.error.HTTPError,
):
self.assert_graphql_query_result(
Q,
{'GlobalTest': [{'gdef': 'x', 'gdef2': 'x'}]},
use_http_post=use_http_post,
globals={},
deprecated_globals={
'default::test_global_def': 'x',
'default::test_global_def2': 'x',
},
)
def test_graphql_func_01(self):
Q = r'''query { FuncTest { fstr } }'''
for use_http_post in [True, False]:
self.assert_graphql_query_result(
Q,
{
"FuncTest": [{
'fstr': 'test',
}]
},
use_http_post=use_http_post,
)
class TestGraphQLInit(tb.GraphQLTestCase):
"""Test GraphQL initialization on an empty database."""
# GraphQL queries cannot run in a transaction
TRANSACTION_ISOLATION = False
def test_graphql_init_type_01(self):
# An empty database should still have an "Object" interface.
self.assert_graphql_query_result(r"""
query {
__type(name: "Object") {
__typename
name
kind
}
}
""", {
"__type": {
"kind": "INTERFACE",
"name": "Object",
"__typename": "__Type"
}
})