mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-27 03:53:56 +03:00
fix: run the tests in python 3.12 and 3.13 and remove snapshottest
dependency (#1572)
* actually run the tests in python 3.12 and 3.13 * remove snapshottest from the example tests so that the tests pass in 3.12 and 3.13 again * remove the section about snapshot testing from the testing docs because the snapshottest package doesn't work on Python 3.12 and above * fix assertion for badly formed JSON input on Python 3.13 * fix deprecation warning about datetime.utcfromtimestamp()
This commit is contained in:
parent
dc3b2e49c1
commit
48678afba4
|
@ -69,43 +69,3 @@ You can also add extra keyword arguments to the ``execute`` method, such as
|
||||||
'hey': 'hello Peter!'
|
'hey': 'hello Peter!'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Snapshot testing
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
As our APIs evolve, we need to know when our changes introduce any breaking changes that might break
|
|
||||||
some of the clients of our GraphQL app.
|
|
||||||
|
|
||||||
However, writing tests and replicating the same response we expect from our GraphQL application can be a
|
|
||||||
tedious and repetitive task, and sometimes it's easier to skip this process.
|
|
||||||
|
|
||||||
Because of that, we recommend the usage of `SnapshotTest <https://github.com/syrusakbary/snapshottest/>`_.
|
|
||||||
|
|
||||||
SnapshotTest lets us write all these tests in a breeze, as it automatically creates the ``snapshots`` for us
|
|
||||||
the first time the test are executed.
|
|
||||||
|
|
||||||
|
|
||||||
Here is a simple example on how our tests will look if we use ``pytest``:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
def test_hey(snapshot):
|
|
||||||
client = Client(my_schema)
|
|
||||||
# This will create a snapshot dir and a snapshot file
|
|
||||||
# the first time the test is executed, with the response
|
|
||||||
# of the execution.
|
|
||||||
snapshot.assert_match(client.execute('''{ hey }'''))
|
|
||||||
|
|
||||||
|
|
||||||
If we are using ``unittest``:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from snapshottest import TestCase
|
|
||||||
|
|
||||||
class APITestCase(TestCase):
|
|
||||||
def test_api_me(self):
|
|
||||||
"""Testing the API for /me"""
|
|
||||||
client = Client(my_schema)
|
|
||||||
self.assertMatchSnapshot(client.execute('''{ hey }'''))
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# snapshottest: v1 - https://goo.gl/zC4yUc
|
|
||||||
from snapshottest import Snapshot
|
|
||||||
|
|
||||||
snapshots = Snapshot()
|
|
||||||
|
|
||||||
snapshots["test_hero_name_query 1"] = {"data": {"hero": {"name": "R2-D2"}}}
|
|
||||||
|
|
||||||
snapshots["test_hero_name_and_friends_query 1"] = {
|
|
||||||
"data": {
|
|
||||||
"hero": {
|
|
||||||
"id": "2001",
|
|
||||||
"name": "R2-D2",
|
|
||||||
"friends": [
|
|
||||||
{"name": "Luke Skywalker"},
|
|
||||||
{"name": "Han Solo"},
|
|
||||||
{"name": "Leia Organa"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_nested_query 1"] = {
|
|
||||||
"data": {
|
|
||||||
"hero": {
|
|
||||||
"name": "R2-D2",
|
|
||||||
"friends": [
|
|
||||||
{
|
|
||||||
"name": "Luke Skywalker",
|
|
||||||
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
|
||||||
"friends": [
|
|
||||||
{"name": "Han Solo"},
|
|
||||||
{"name": "Leia Organa"},
|
|
||||||
{"name": "C-3PO"},
|
|
||||||
{"name": "R2-D2"},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Han Solo",
|
|
||||||
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
|
||||||
"friends": [
|
|
||||||
{"name": "Luke Skywalker"},
|
|
||||||
{"name": "Leia Organa"},
|
|
||||||
{"name": "R2-D2"},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Leia Organa",
|
|
||||||
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
|
||||||
"friends": [
|
|
||||||
{"name": "Luke Skywalker"},
|
|
||||||
{"name": "Han Solo"},
|
|
||||||
{"name": "C-3PO"},
|
|
||||||
{"name": "R2-D2"},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_fetch_luke_query 1"] = {"data": {"human": {"name": "Luke Skywalker"}}}
|
|
||||||
|
|
||||||
snapshots["test_fetch_some_id_query 1"] = {
|
|
||||||
"data": {"human": {"name": "Luke Skywalker"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_fetch_some_id_query2 1"] = {"data": {"human": {"name": "Han Solo"}}}
|
|
||||||
|
|
||||||
snapshots["test_invalid_id_query 1"] = {"data": {"human": None}}
|
|
||||||
|
|
||||||
snapshots["test_fetch_luke_aliased 1"] = {"data": {"luke": {"name": "Luke Skywalker"}}}
|
|
||||||
|
|
||||||
snapshots["test_fetch_luke_and_leia_aliased 1"] = {
|
|
||||||
"data": {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_duplicate_fields 1"] = {
|
|
||||||
"data": {
|
|
||||||
"luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
|
|
||||||
"leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_use_fragment 1"] = {
|
|
||||||
"data": {
|
|
||||||
"luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
|
|
||||||
"leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_check_type_of_r2 1"] = {
|
|
||||||
"data": {"hero": {"__typename": "Droid", "name": "R2-D2"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_check_type_of_luke 1"] = {
|
|
||||||
"data": {"hero": {"__typename": "Human", "name": "Luke Skywalker"}}
|
|
||||||
}
|
|
|
@ -8,19 +8,19 @@ setup()
|
||||||
client = Client(schema)
|
client = Client(schema)
|
||||||
|
|
||||||
|
|
||||||
def test_hero_name_query(snapshot):
|
def test_hero_name_query():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query HeroNameQuery {
|
query HeroNameQuery {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {"data": {"hero": {"name": "R2-D2"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_hero_name_and_friends_query(snapshot):
|
def test_hero_name_and_friends_query():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query HeroNameAndFriendsQuery {
|
query HeroNameAndFriendsQuery {
|
||||||
hero {
|
hero {
|
||||||
id
|
id
|
||||||
|
@ -30,12 +30,24 @@ def test_hero_name_and_friends_query(snapshot):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"hero": {
|
||||||
|
"id": "2001",
|
||||||
|
"name": "R2-D2",
|
||||||
|
"friends": [
|
||||||
|
{"name": "Luke Skywalker"},
|
||||||
|
{"name": "Han Solo"},
|
||||||
|
{"name": "Leia Organa"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_nested_query(snapshot):
|
def test_nested_query():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query NestedQuery {
|
query NestedQuery {
|
||||||
hero {
|
hero {
|
||||||
name
|
name
|
||||||
|
@ -48,70 +60,113 @@ def test_nested_query(snapshot):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"hero": {
|
||||||
|
"name": "R2-D2",
|
||||||
|
"friends": [
|
||||||
|
{
|
||||||
|
"name": "Luke Skywalker",
|
||||||
|
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
||||||
|
"friends": [
|
||||||
|
{"name": "Han Solo"},
|
||||||
|
{"name": "Leia Organa"},
|
||||||
|
{"name": "C-3PO"},
|
||||||
|
{"name": "R2-D2"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Han Solo",
|
||||||
|
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
||||||
|
"friends": [
|
||||||
|
{"name": "Luke Skywalker"},
|
||||||
|
{"name": "Leia Organa"},
|
||||||
|
{"name": "R2-D2"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Leia Organa",
|
||||||
|
"appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
|
||||||
|
"friends": [
|
||||||
|
{"name": "Luke Skywalker"},
|
||||||
|
{"name": "Han Solo"},
|
||||||
|
{"name": "C-3PO"},
|
||||||
|
{"name": "R2-D2"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_luke_query(snapshot):
|
def test_fetch_luke_query():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query FetchLukeQuery {
|
query FetchLukeQuery {
|
||||||
human(id: "1000") {
|
human(id: "1000") {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {"data": {"human": {"name": "Luke Skywalker"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_some_id_query(snapshot):
|
def test_fetch_some_id_query():
|
||||||
query = """
|
result = client.execute(
|
||||||
|
"""
|
||||||
query FetchSomeIDQuery($someId: String!) {
|
query FetchSomeIDQuery($someId: String!) {
|
||||||
human(id: $someId) {
|
human(id: $someId) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""",
|
||||||
params = {"someId": "1000"}
|
variables={"someId": "1000"},
|
||||||
snapshot.assert_match(client.execute(query, variables=params))
|
)
|
||||||
|
assert result == {"data": {"human": {"name": "Luke Skywalker"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_some_id_query2(snapshot):
|
def test_fetch_some_id_query2():
|
||||||
query = """
|
result = client.execute(
|
||||||
|
"""
|
||||||
query FetchSomeIDQuery($someId: String!) {
|
query FetchSomeIDQuery($someId: String!) {
|
||||||
human(id: $someId) {
|
human(id: $someId) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""",
|
||||||
params = {"someId": "1002"}
|
variables={"someId": "1002"},
|
||||||
snapshot.assert_match(client.execute(query, variables=params))
|
)
|
||||||
|
assert result == {"data": {"human": {"name": "Han Solo"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_id_query(snapshot):
|
def test_invalid_id_query():
|
||||||
query = """
|
result = client.execute(
|
||||||
|
"""
|
||||||
query humanQuery($id: String!) {
|
query humanQuery($id: String!) {
|
||||||
human(id: $id) {
|
human(id: $id) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""",
|
||||||
params = {"id": "not a valid id"}
|
variables={"id": "not a valid id"},
|
||||||
snapshot.assert_match(client.execute(query, variables=params))
|
)
|
||||||
|
assert result == {"data": {"human": None}}
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_luke_aliased(snapshot):
|
def test_fetch_luke_aliased():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query FetchLukeAliased {
|
query FetchLukeAliased {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {"data": {"luke": {"name": "Luke Skywalker"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_luke_and_leia_aliased(snapshot):
|
def test_fetch_luke_and_leia_aliased():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query FetchLukeAndLeiaAliased {
|
query FetchLukeAndLeiaAliased {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
|
@ -120,12 +175,14 @@ def test_fetch_luke_and_leia_aliased(snapshot):
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_duplicate_fields(snapshot):
|
def test_duplicate_fields():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query DuplicateFields {
|
query DuplicateFields {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
name
|
name
|
||||||
|
@ -136,12 +193,17 @@ def test_duplicate_fields(snapshot):
|
||||||
homePlanet
|
homePlanet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
|
||||||
|
"leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_use_fragment(snapshot):
|
def test_use_fragment():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query UseFragment {
|
query UseFragment {
|
||||||
luke: human(id: "1000") {
|
luke: human(id: "1000") {
|
||||||
...HumanFragment
|
...HumanFragment
|
||||||
|
@ -154,29 +216,36 @@ def test_use_fragment(snapshot):
|
||||||
name
|
name
|
||||||
homePlanet
|
homePlanet
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
|
||||||
|
"leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_check_type_of_r2(snapshot):
|
def test_check_type_of_r2():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query CheckTypeOfR2 {
|
query CheckTypeOfR2 {
|
||||||
hero {
|
hero {
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {"data": {"hero": {"__typename": "Droid", "name": "R2-D2"}}}
|
||||||
|
|
||||||
|
|
||||||
def test_check_type_of_luke(snapshot):
|
def test_check_type_of_luke():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query CheckTypeOfLuke {
|
query CheckTypeOfLuke {
|
||||||
hero(episode: EMPIRE) {
|
hero(episode: EMPIRE) {
|
||||||
__typename
|
__typename
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {"hero": {"__typename": "Human", "name": "Luke Skywalker"}}
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# snapshottest: v1 - https://goo.gl/zC4yUc
|
|
||||||
from snapshottest import Snapshot
|
|
||||||
|
|
||||||
snapshots = Snapshot()
|
|
||||||
|
|
||||||
snapshots["test_correct_fetch_first_ship_rebels 1"] = {
|
|
||||||
"data": {
|
|
||||||
"rebels": {
|
|
||||||
"name": "Alliance to Restore the Republic",
|
|
||||||
"ships": {
|
|
||||||
"pageInfo": {
|
|
||||||
"startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
|
|
||||||
"endCursor": "YXJyYXljb25uZWN0aW9uOjA=",
|
|
||||||
"hasNextPage": True,
|
|
||||||
"hasPreviousPage": False,
|
|
||||||
},
|
|
||||||
"edges": [
|
|
||||||
{"cursor": "YXJyYXljb25uZWN0aW9uOjA=", "node": {"name": "X-Wing"}}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# snapshottest: v1 - https://goo.gl/zC4yUc
|
|
||||||
from snapshottest import Snapshot
|
|
||||||
|
|
||||||
snapshots = Snapshot()
|
|
||||||
|
|
||||||
snapshots["test_mutations 1"] = {
|
|
||||||
"data": {
|
|
||||||
"introduceShip": {
|
|
||||||
"ship": {"id": "U2hpcDo5", "name": "Peter"},
|
|
||||||
"faction": {
|
|
||||||
"name": "Alliance to Restore the Republic",
|
|
||||||
"ships": {
|
|
||||||
"edges": [
|
|
||||||
{"node": {"id": "U2hpcDox", "name": "X-Wing"}},
|
|
||||||
{"node": {"id": "U2hpcDoy", "name": "Y-Wing"}},
|
|
||||||
{"node": {"id": "U2hpcDoz", "name": "A-Wing"}},
|
|
||||||
{"node": {"id": "U2hpcDo0", "name": "Millennium Falcon"}},
|
|
||||||
{"node": {"id": "U2hpcDo1", "name": "Home One"}},
|
|
||||||
{"node": {"id": "U2hpcDo5", "name": "Peter"}},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# snapshottest: v1 - https://goo.gl/zC4yUc
|
|
||||||
from snapshottest import Snapshot
|
|
||||||
|
|
||||||
|
|
||||||
snapshots = Snapshot()
|
|
||||||
|
|
||||||
snapshots["test_correctly_fetches_id_name_rebels 1"] = {
|
|
||||||
"data": {
|
|
||||||
"rebels": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_correctly_refetches_rebels 1"] = {
|
|
||||||
"data": {"node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_correctly_fetches_id_name_empire 1"] = {
|
|
||||||
"data": {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_correctly_refetches_empire 1"] = {
|
|
||||||
"data": {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_correctly_refetches_xwing 1"] = {
|
|
||||||
"data": {"node": {"id": "U2hpcDox", "name": "X-Wing"}}
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshots["test_str_schema 1"] = '''type Query {
|
|
||||||
rebels: Faction
|
|
||||||
empire: Faction
|
|
||||||
node(
|
|
||||||
"""The ID of the object"""
|
|
||||||
id: ID!
|
|
||||||
): Node
|
|
||||||
}
|
|
||||||
|
|
||||||
"""A faction in the Star Wars saga"""
|
|
||||||
type Faction implements Node {
|
|
||||||
"""The ID of the object"""
|
|
||||||
id: ID!
|
|
||||||
|
|
||||||
"""The name of the faction."""
|
|
||||||
name: String
|
|
||||||
|
|
||||||
"""The ships used by the faction."""
|
|
||||||
ships(before: String, after: String, first: Int, last: Int): ShipConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
"""An object with an ID"""
|
|
||||||
interface Node {
|
|
||||||
"""The ID of the object"""
|
|
||||||
id: ID!
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShipConnection {
|
|
||||||
"""Pagination data for this connection."""
|
|
||||||
pageInfo: PageInfo!
|
|
||||||
|
|
||||||
"""Contains the nodes in this connection."""
|
|
||||||
edges: [ShipEdge]!
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
|
|
||||||
"""
|
|
||||||
type PageInfo {
|
|
||||||
"""When paginating forwards, are there more items?"""
|
|
||||||
hasNextPage: Boolean!
|
|
||||||
|
|
||||||
"""When paginating backwards, are there more items?"""
|
|
||||||
hasPreviousPage: Boolean!
|
|
||||||
|
|
||||||
"""When paginating backwards, the cursor to continue."""
|
|
||||||
startCursor: String
|
|
||||||
|
|
||||||
"""When paginating forwards, the cursor to continue."""
|
|
||||||
endCursor: String
|
|
||||||
}
|
|
||||||
|
|
||||||
"""A Relay edge containing a `Ship` and its cursor."""
|
|
||||||
type ShipEdge {
|
|
||||||
"""The item at the end of the edge"""
|
|
||||||
node: Ship
|
|
||||||
|
|
||||||
"""A cursor for use in pagination"""
|
|
||||||
cursor: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
"""A ship in the Star Wars saga"""
|
|
||||||
type Ship implements Node {
|
|
||||||
"""The ID of the object"""
|
|
||||||
id: ID!
|
|
||||||
|
|
||||||
"""The name of the ship."""
|
|
||||||
name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
|
||||||
}
|
|
||||||
|
|
||||||
type IntroduceShipPayload {
|
|
||||||
ship: Ship
|
|
||||||
faction: Faction
|
|
||||||
clientMutationId: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input IntroduceShipInput {
|
|
||||||
shipName: String!
|
|
||||||
factionId: String!
|
|
||||||
clientMutationId: String
|
|
||||||
}'''
|
|
|
@ -8,26 +8,46 @@ setup()
|
||||||
client = Client(schema)
|
client = Client(schema)
|
||||||
|
|
||||||
|
|
||||||
def test_correct_fetch_first_ship_rebels(snapshot):
|
def test_correct_fetch_first_ship_rebels():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query RebelsShipsQuery {
|
query RebelsShipsQuery {
|
||||||
rebels {
|
rebels {
|
||||||
name,
|
name,
|
||||||
ships(first: 1) {
|
ships(first: 1) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
startCursor
|
startCursor
|
||||||
endCursor
|
endCursor
|
||||||
hasNextPage
|
hasNextPage
|
||||||
hasPreviousPage
|
hasPreviousPage
|
||||||
}
|
}
|
||||||
edges {
|
edges {
|
||||||
cursor
|
cursor
|
||||||
node {
|
node {
|
||||||
name
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
""")
|
||||||
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"rebels": {
|
||||||
|
"name": "Alliance to Restore the Republic",
|
||||||
|
"ships": {
|
||||||
|
"pageInfo": {
|
||||||
|
"startCursor": "YXJyYXljb25uZWN0aW9uOjA=",
|
||||||
|
"endCursor": "YXJyYXljb25uZWN0aW9uOjA=",
|
||||||
|
"hasNextPage": True,
|
||||||
|
"hasPreviousPage": False,
|
||||||
|
},
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"cursor": "YXJyYXljb25uZWN0aW9uOjA=",
|
||||||
|
"node": {"name": "X-Wing"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
|
||||||
snapshot.assert_match(client.execute(query))
|
|
||||||
|
|
|
@ -8,26 +8,45 @@ setup()
|
||||||
client = Client(schema)
|
client = Client(schema)
|
||||||
|
|
||||||
|
|
||||||
def test_mutations(snapshot):
|
def test_mutations():
|
||||||
query = """
|
result = client.execute("""
|
||||||
mutation MyMutation {
|
mutation MyMutation {
|
||||||
introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) {
|
introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) {
|
||||||
ship {
|
ship {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
faction {
|
faction {
|
||||||
name
|
name
|
||||||
ships {
|
ships {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
""")
|
||||||
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"introduceShip": {
|
||||||
|
"ship": {"id": "U2hpcDo5", "name": "Peter"},
|
||||||
|
"faction": {
|
||||||
|
"name": "Alliance to Restore the Republic",
|
||||||
|
"ships": {
|
||||||
|
"edges": [
|
||||||
|
{"node": {"id": "U2hpcDox", "name": "X-Wing"}},
|
||||||
|
{"node": {"id": "U2hpcDoy", "name": "Y-Wing"}},
|
||||||
|
{"node": {"id": "U2hpcDoz", "name": "A-Wing"}},
|
||||||
|
{"node": {"id": "U2hpcDo0", "name": "Millennium Falcon"}},
|
||||||
|
{"node": {"id": "U2hpcDo1", "name": "Home One"}},
|
||||||
|
{"node": {"id": "U2hpcDo5", "name": "Peter"}},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
|
||||||
snapshot.assert_match(client.execute(query))
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import textwrap
|
||||||
|
|
||||||
from graphene.test import Client
|
from graphene.test import Client
|
||||||
|
|
||||||
from ..data import setup
|
from ..data import setup
|
||||||
|
@ -8,24 +10,115 @@ setup()
|
||||||
client = Client(schema)
|
client = Client(schema)
|
||||||
|
|
||||||
|
|
||||||
def test_str_schema(snapshot):
|
def test_str_schema():
|
||||||
snapshot.assert_match(str(schema).strip())
|
assert str(schema).strip() == textwrap.dedent(
|
||||||
|
'''\
|
||||||
|
type Query {
|
||||||
|
rebels: Faction
|
||||||
|
empire: Faction
|
||||||
|
node(
|
||||||
|
"""The ID of the object"""
|
||||||
|
id: ID!
|
||||||
|
): Node
|
||||||
|
}
|
||||||
|
|
||||||
|
"""A faction in the Star Wars saga"""
|
||||||
|
type Faction implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""The name of the faction."""
|
||||||
|
name: String
|
||||||
|
|
||||||
|
"""The ships used by the faction."""
|
||||||
|
ships(before: String, after: String, first: Int, last: Int): ShipConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
"""An object with an ID"""
|
||||||
|
interface Node {
|
||||||
|
"""The ID of the object"""
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShipConnection {
|
||||||
|
"""Pagination data for this connection."""
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
|
||||||
|
"""Contains the nodes in this connection."""
|
||||||
|
edges: [ShipEdge]!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
|
||||||
|
"""
|
||||||
|
type PageInfo {
|
||||||
|
"""When paginating forwards, are there more items?"""
|
||||||
|
hasNextPage: Boolean!
|
||||||
|
|
||||||
|
"""When paginating backwards, are there more items?"""
|
||||||
|
hasPreviousPage: Boolean!
|
||||||
|
|
||||||
|
"""When paginating backwards, the cursor to continue."""
|
||||||
|
startCursor: String
|
||||||
|
|
||||||
|
"""When paginating forwards, the cursor to continue."""
|
||||||
|
endCursor: String
|
||||||
|
}
|
||||||
|
|
||||||
|
"""A Relay edge containing a `Ship` and its cursor."""
|
||||||
|
type ShipEdge {
|
||||||
|
"""The item at the end of the edge"""
|
||||||
|
node: Ship
|
||||||
|
|
||||||
|
"""A cursor for use in pagination"""
|
||||||
|
cursor: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""A ship in the Star Wars saga"""
|
||||||
|
type Ship implements Node {
|
||||||
|
"""The ID of the object"""
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""The name of the ship."""
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
introduceShip(input: IntroduceShipInput!): IntroduceShipPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntroduceShipPayload {
|
||||||
|
ship: Ship
|
||||||
|
faction: Faction
|
||||||
|
clientMutationId: String
|
||||||
|
}
|
||||||
|
|
||||||
|
input IntroduceShipInput {
|
||||||
|
shipName: String!
|
||||||
|
factionId: String!
|
||||||
|
clientMutationId: String
|
||||||
|
}'''
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_correctly_fetches_id_name_rebels(snapshot):
|
def test_correctly_fetches_id_name_rebels():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query RebelsQuery {
|
query RebelsQuery {
|
||||||
rebels {
|
rebels {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"rebels": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_correctly_refetches_rebels(snapshot):
|
def test_correctly_refetches_rebels():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query RebelsRefetchQuery {
|
query RebelsRefetchQuery {
|
||||||
node(id: "RmFjdGlvbjox") {
|
node(id: "RmFjdGlvbjox") {
|
||||||
id
|
id
|
||||||
|
@ -34,24 +127,30 @@ def test_correctly_refetches_rebels(snapshot):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {
|
||||||
|
"node": {"id": "RmFjdGlvbjox", "name": "Alliance to Restore the Republic"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_correctly_fetches_id_name_empire(snapshot):
|
def test_correctly_fetches_id_name_empire():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query EmpireQuery {
|
query EmpireQuery {
|
||||||
empire {
|
empire {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_correctly_refetches_empire(snapshot):
|
def test_correctly_refetches_empire():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query EmpireRefetchQuery {
|
query EmpireRefetchQuery {
|
||||||
node(id: "RmFjdGlvbjoy") {
|
node(id: "RmFjdGlvbjoy") {
|
||||||
id
|
id
|
||||||
|
@ -60,12 +159,14 @@ def test_correctly_refetches_empire(snapshot):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {
|
||||||
|
"data": {"node": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_correctly_refetches_xwing(snapshot):
|
def test_correctly_refetches_xwing():
|
||||||
query = """
|
result = client.execute("""
|
||||||
query XWingRefetchQuery {
|
query XWingRefetchQuery {
|
||||||
node(id: "U2hpcDox") {
|
node(id: "U2hpcDox") {
|
||||||
id
|
id
|
||||||
|
@ -74,5 +175,5 @@ def test_correctly_refetches_xwing(snapshot):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
snapshot.assert_match(client.execute(query))
|
assert result == {"data": {"node": {"id": "U2hpcDox", "name": "X-Wing"}}}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# https://github.com/graphql-python/graphene/issues/1293
|
# https://github.com/graphql-python/graphene/issues/1293
|
||||||
|
|
||||||
import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphql.utilities import print_schema
|
from graphql.utilities import print_schema
|
||||||
|
@ -9,11 +9,11 @@ from graphql.utilities import print_schema
|
||||||
class Filters(graphene.InputObjectType):
|
class Filters(graphene.InputObjectType):
|
||||||
datetime_after = graphene.DateTime(
|
datetime_after = graphene.DateTime(
|
||||||
required=False,
|
required=False,
|
||||||
default_value=datetime.datetime.utcfromtimestamp(1434549820776 / 1000),
|
default_value=datetime.fromtimestamp(1434549820.776, timezone.utc),
|
||||||
)
|
)
|
||||||
datetime_before = graphene.DateTime(
|
datetime_before = graphene.DateTime(
|
||||||
required=False,
|
required=False,
|
||||||
default_value=datetime.datetime.utcfromtimestamp(1444549820776 / 1000),
|
default_value=datetime.fromtimestamp(1444549820.776, timezone.utc),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -51,35 +51,30 @@ def test_jsonstring_invalid_query():
|
||||||
Test that if an invalid type is provided we get an error
|
Test that if an invalid type is provided we get an error
|
||||||
"""
|
"""
|
||||||
result = schema.execute("{ json(input: 1) }")
|
result = schema.execute("{ json(input: 1) }")
|
||||||
assert result.errors
|
assert result.errors == [
|
||||||
assert len(result.errors) == 1
|
{"message": "Expected value of type 'JSONString', found 1."},
|
||||||
assert result.errors[0].message == "Expected value of type 'JSONString', found 1."
|
]
|
||||||
|
|
||||||
result = schema.execute("{ json(input: {}) }")
|
result = schema.execute("{ json(input: {}) }")
|
||||||
assert result.errors
|
assert result.errors == [
|
||||||
assert len(result.errors) == 1
|
{"message": "Expected value of type 'JSONString', found {}."},
|
||||||
assert result.errors[0].message == "Expected value of type 'JSONString', found {}."
|
]
|
||||||
|
|
||||||
result = schema.execute('{ json(input: "a") }')
|
result = schema.execute('{ json(input: "a") }')
|
||||||
assert result.errors
|
assert result.errors == [
|
||||||
assert len(result.errors) == 1
|
{
|
||||||
assert result.errors[0].message == (
|
"message": "Expected value of type 'JSONString', found \"a\"; "
|
||||||
"Expected value of type 'JSONString', found \"a\"; "
|
"Badly formed JSONString: Expecting value: line 1 column 1 (char 0)",
|
||||||
"Badly formed JSONString: Expecting value: line 1 column 1 (char 0)"
|
},
|
||||||
)
|
]
|
||||||
|
|
||||||
result = schema.execute("""{ json(input: "{\\'key\\': 0}") }""")
|
result = schema.execute("""{ json(input: "{\\'key\\': 0}") }""")
|
||||||
assert result.errors
|
assert result.errors == [
|
||||||
assert len(result.errors) == 1
|
{"message": "Syntax Error: Invalid character escape sequence: '\\''."},
|
||||||
assert (
|
]
|
||||||
result.errors[0].message
|
|
||||||
== "Syntax Error: Invalid character escape sequence: '\\''."
|
|
||||||
)
|
|
||||||
|
|
||||||
result = schema.execute("""{ json(input: "{\\"key\\": 0,}") }""")
|
result = schema.execute("""{ json(input: "{\\"key\\": 0,}") }""")
|
||||||
assert result.errors
|
|
||||||
assert len(result.errors) == 1
|
assert len(result.errors) == 1
|
||||||
assert result.errors[0].message == (
|
assert result.errors[0].message.startswith(
|
||||||
'Expected value of type \'JSONString\', found "{\\"key\\": 0,}"; '
|
'Expected value of type \'JSONString\', found "{\\"key\\": 0,}"; Badly formed JSONString:'
|
||||||
"Badly formed JSONString: Expecting property name enclosed in double quotes: line 1 column 11 (char 10)"
|
|
||||||
)
|
)
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -50,7 +50,6 @@ tests_require = [
|
||||||
"pytest-cov>=5,<6",
|
"pytest-cov>=5,<6",
|
||||||
"pytest-mock>=3,<4",
|
"pytest-mock>=3,<4",
|
||||||
"pytest-asyncio>=0.16,<2",
|
"pytest-asyncio>=0.16,<2",
|
||||||
"snapshottest>=0.6,<1",
|
|
||||||
"coveralls>=3.3,<5",
|
"coveralls>=3.3,<5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -5,10 +5,8 @@ skipsdist = true
|
||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
.[test]
|
.[test]
|
||||||
setenv =
|
|
||||||
PYTHONPATH = .:{envdir}
|
|
||||||
commands =
|
commands =
|
||||||
py{38,39,310,311,12,13}: pytest --cov=graphene graphene --cov-report=term --cov-report=xml examples {posargs}
|
pytest --cov=graphene graphene --cov-report=term --cov-report=xml examples {posargs}
|
||||||
|
|
||||||
[testenv:pre-commit]
|
[testenv:pre-commit]
|
||||||
basepython = python3.10
|
basepython = python3.10
|
||||||
|
|
Loading…
Reference in New Issue
Block a user