mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-01 16:37:29 +03:00 
			
		
		
		
	Merge branch 'master' into lint-ruff-pre-commit
This commit is contained in:
		
						commit
						b831947e4d
					
				|  | @ -69,43 +69,3 @@ You can also add extra keyword arguments to the ``execute`` method, such as | |||
|                 '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) | ||||
| 
 | ||||
| 
 | ||||
| def test_hero_name_query(snapshot): | ||||
|     query = """ | ||||
| def test_hero_name_query(): | ||||
|     result = client.execute(""" | ||||
|         query HeroNameQuery { | ||||
|           hero { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     assert result == {"data": {"hero": {"name": "R2-D2"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_hero_name_and_friends_query(snapshot): | ||||
|     query = """ | ||||
| def test_hero_name_and_friends_query(): | ||||
|     result = client.execute(""" | ||||
|         query HeroNameAndFriendsQuery { | ||||
|           hero { | ||||
|             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): | ||||
|     query = """ | ||||
| def test_nested_query(): | ||||
|     result = client.execute(""" | ||||
|         query NestedQuery { | ||||
|           hero { | ||||
|             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): | ||||
|     query = """ | ||||
| def test_fetch_luke_query(): | ||||
|     result = client.execute(""" | ||||
|         query FetchLukeQuery { | ||||
|           human(id: "1000") { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """) | ||||
|     assert result == {"data": {"human": {"name": "Luke Skywalker"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_some_id_query(): | ||||
|     result = client.execute( | ||||
|         """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_some_id_query(snapshot): | ||||
|     query = """ | ||||
|         query FetchSomeIDQuery($someId: String!) { | ||||
|           human(id: $someId) { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """, | ||||
|         variables={"someId": "1000"}, | ||||
|     ) | ||||
|     assert result == {"data": {"human": {"name": "Luke Skywalker"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_some_id_query2(): | ||||
|     result = client.execute( | ||||
|         """ | ||||
|     params = {"someId": "1000"} | ||||
|     snapshot.assert_match(client.execute(query, variables=params)) | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_some_id_query2(snapshot): | ||||
|     query = """ | ||||
|         query FetchSomeIDQuery($someId: String!) { | ||||
|           human(id: $someId) { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """, | ||||
|         variables={"someId": "1002"}, | ||||
|     ) | ||||
|     assert result == {"data": {"human": {"name": "Han Solo"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_invalid_id_query(): | ||||
|     result = client.execute( | ||||
|         """ | ||||
|     params = {"someId": "1002"} | ||||
|     snapshot.assert_match(client.execute(query, variables=params)) | ||||
| 
 | ||||
| 
 | ||||
| def test_invalid_id_query(snapshot): | ||||
|     query = """ | ||||
|         query humanQuery($id: String!) { | ||||
|           human(id: $id) { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     params = {"id": "not a valid id"} | ||||
|     snapshot.assert_match(client.execute(query, variables=params)) | ||||
|     """, | ||||
|         variables={"id": "not a valid id"}, | ||||
|     ) | ||||
|     assert result == {"data": {"human": None}} | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_luke_aliased(snapshot): | ||||
|     query = """ | ||||
| def test_fetch_luke_aliased(): | ||||
|     result = client.execute(""" | ||||
|         query FetchLukeAliased { | ||||
|           luke: human(id: "1000") { | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     assert result == {"data": {"luke": {"name": "Luke Skywalker"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_fetch_luke_and_leia_aliased(snapshot): | ||||
|     query = """ | ||||
| def test_fetch_luke_and_leia_aliased(): | ||||
|     result = client.execute(""" | ||||
|         query FetchLukeAndLeiaAliased { | ||||
|           luke: human(id: "1000") { | ||||
|             name | ||||
|  | @ -120,12 +175,14 @@ def test_fetch_luke_and_leia_aliased(snapshot): | |||
|             name | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     assert result == { | ||||
|         "data": {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}} | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| def test_duplicate_fields(snapshot): | ||||
|     query = """ | ||||
| def test_duplicate_fields(): | ||||
|     result = client.execute(""" | ||||
|         query DuplicateFields { | ||||
|           luke: human(id: "1000") { | ||||
|             name | ||||
|  | @ -136,12 +193,17 @@ def test_duplicate_fields(snapshot): | |||
|             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): | ||||
|     query = """ | ||||
| def test_use_fragment(): | ||||
|     result = client.execute(""" | ||||
|         query UseFragment { | ||||
|           luke: human(id: "1000") { | ||||
|             ...HumanFragment | ||||
|  | @ -154,29 +216,36 @@ def test_use_fragment(snapshot): | |||
|           name | ||||
|           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): | ||||
|     query = """ | ||||
| def test_check_type_of_r2(): | ||||
|     result = client.execute(""" | ||||
|         query CheckTypeOfR2 { | ||||
|           hero { | ||||
|             __typename | ||||
|             name | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     assert result == {"data": {"hero": {"__typename": "Droid", "name": "R2-D2"}}} | ||||
| 
 | ||||
| 
 | ||||
| def test_check_type_of_luke(snapshot): | ||||
|     query = """ | ||||
| def test_check_type_of_luke(): | ||||
|     result = client.execute(""" | ||||
|         query CheckTypeOfLuke { | ||||
|           hero(episode: EMPIRE) { | ||||
|             __typename | ||||
|             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,8 +8,8 @@ setup() | |||
| client = Client(schema) | ||||
| 
 | ||||
| 
 | ||||
| def test_correct_fetch_first_ship_rebels(snapshot): | ||||
|     query = """ | ||||
| def test_correct_fetch_first_ship_rebels(): | ||||
|     result = client.execute(""" | ||||
|         query RebelsShipsQuery { | ||||
|           rebels { | ||||
|             name, | ||||
|  | @ -29,5 +29,25 @@ def test_correct_fetch_first_ship_rebels(snapshot): | |||
|             } | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     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"}, | ||||
|                         } | ||||
|                     ], | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -8,8 +8,8 @@ setup() | |||
| client = Client(schema) | ||||
| 
 | ||||
| 
 | ||||
| def test_mutations(snapshot): | ||||
|     query = """ | ||||
| def test_mutations(): | ||||
|     result = client.execute(""" | ||||
|         mutation MyMutation { | ||||
|           introduceShip(input:{clientMutationId:"abc", shipName: "Peter", factionId: "1"}) { | ||||
|             ship { | ||||
|  | @ -29,5 +29,24 @@ def test_mutations(snapshot): | |||
|             } | ||||
|           } | ||||
|         } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     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"}}, | ||||
|                         ] | ||||
|                     }, | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| import textwrap | ||||
| 
 | ||||
| from graphene.test import Client | ||||
| 
 | ||||
| from ..data import setup | ||||
|  | @ -8,24 +10,115 @@ setup() | |||
| client = Client(schema) | ||||
| 
 | ||||
| 
 | ||||
| def test_str_schema(snapshot): | ||||
|     snapshot.assert_match(str(schema).strip()) | ||||
| def test_str_schema(): | ||||
|     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): | ||||
|     query = """ | ||||
| def test_correctly_fetches_id_name_rebels(): | ||||
|     result = client.execute(""" | ||||
|       query RebelsQuery { | ||||
|         rebels { | ||||
|           id | ||||
|           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): | ||||
|     query = """ | ||||
| def test_correctly_refetches_rebels(): | ||||
|     result = client.execute(""" | ||||
|       query RebelsRefetchQuery { | ||||
|         node(id: "RmFjdGlvbjox") { | ||||
|           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): | ||||
|     query = """ | ||||
| def test_correctly_fetches_id_name_empire(): | ||||
|     result = client.execute(""" | ||||
|       query EmpireQuery { | ||||
|         empire { | ||||
|           id | ||||
|           name | ||||
|         } | ||||
|       } | ||||
|     """ | ||||
|     snapshot.assert_match(client.execute(query)) | ||||
|     """) | ||||
|     assert result == { | ||||
|         "data": {"empire": {"id": "RmFjdGlvbjoy", "name": "Galactic Empire"}} | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| def test_correctly_refetches_empire(snapshot): | ||||
|     query = """ | ||||
| def test_correctly_refetches_empire(): | ||||
|     result = client.execute(""" | ||||
|       query EmpireRefetchQuery { | ||||
|         node(id: "RmFjdGlvbjoy") { | ||||
|           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): | ||||
|     query = """ | ||||
| def test_correctly_refetches_xwing(): | ||||
|     result = client.execute(""" | ||||
|       query XWingRefetchQuery { | ||||
|         node(id: "U2hpcDox") { | ||||
|           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 | ||||
| 
 | ||||
| import datetime | ||||
| from datetime import datetime, timezone | ||||
| 
 | ||||
| import graphene | ||||
| from graphql.utilities import print_schema | ||||
|  | @ -9,11 +9,11 @@ from graphql.utilities import print_schema | |||
| class Filters(graphene.InputObjectType): | ||||
|     datetime_after = graphene.DateTime( | ||||
|         required=False, | ||||
|         default_value=datetime.datetime.utcfromtimestamp(1434549820776 / 1000), | ||||
|         default_value=datetime.fromtimestamp(1434549820.776, timezone.utc), | ||||
|     ) | ||||
|     datetime_before = graphene.DateTime( | ||||
|         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 | ||||
|     """ | ||||
|     result = schema.execute("{ json(input: 1) }") | ||||
|     assert result.errors | ||||
|     assert len(result.errors) == 1 | ||||
|     assert result.errors[0].message == "Expected value of type 'JSONString', found 1." | ||||
|     assert result.errors == [ | ||||
|         {"message": "Expected value of type 'JSONString', found 1."}, | ||||
|     ] | ||||
| 
 | ||||
|     result = schema.execute("{ json(input: {}) }") | ||||
|     assert result.errors | ||||
|     assert len(result.errors) == 1 | ||||
|     assert result.errors[0].message == "Expected value of type 'JSONString', found {}." | ||||
|     assert result.errors == [ | ||||
|         {"message": "Expected value of type 'JSONString', found {}."}, | ||||
|     ] | ||||
| 
 | ||||
|     result = schema.execute('{ json(input: "a") }') | ||||
|     assert result.errors | ||||
|     assert len(result.errors) == 1 | ||||
|     assert result.errors[0].message == ( | ||||
|         "Expected value of type 'JSONString', found \"a\"; " | ||||
|         "Badly formed JSONString: Expecting value: line 1 column 1 (char 0)" | ||||
|     ) | ||||
|     assert result.errors == [ | ||||
|         { | ||||
|             "message": "Expected value of type 'JSONString', found \"a\"; " | ||||
|             "Badly formed JSONString: Expecting value: line 1 column 1 (char 0)", | ||||
|         }, | ||||
|     ] | ||||
| 
 | ||||
|     result = schema.execute("""{ json(input: "{\\'key\\': 0}") }""") | ||||
|     assert result.errors | ||||
|     assert len(result.errors) == 1 | ||||
|     assert ( | ||||
|         result.errors[0].message | ||||
|         == "Syntax Error: Invalid character escape sequence: '\\''." | ||||
|     ) | ||||
|     assert result.errors == [ | ||||
|         {"message": "Syntax Error: Invalid character escape sequence: '\\''."}, | ||||
|     ] | ||||
| 
 | ||||
|     result = schema.execute("""{ json(input: "{\\"key\\": 0,}") }""") | ||||
|     assert result.errors | ||||
|     assert len(result.errors) == 1 | ||||
|     assert result.errors[0].message == ( | ||||
|         'Expected value of type \'JSONString\', found "{\\"key\\": 0,}"; ' | ||||
|         "Badly formed JSONString: Expecting property name enclosed in double quotes: line 1 column 11 (char 10)" | ||||
|     assert result.errors[0].message.startswith( | ||||
|         'Expected value of type \'JSONString\', found "{\\"key\\": 0,}"; Badly formed JSONString:' | ||||
|     ) | ||||
|  |  | |||
|  | @ -1,67 +1,5 @@ | |||
| import functools | ||||
| import inspect | ||||
| import warnings | ||||
| 
 | ||||
| string_types = (type(b""), type("")) | ||||
| from warnings import warn | ||||
| 
 | ||||
| 
 | ||||
| def warn_deprecation(text): | ||||
|     warnings.warn(text, category=DeprecationWarning, stacklevel=2) | ||||
| 
 | ||||
| 
 | ||||
| def deprecated(reason): | ||||
|     """ | ||||
|     This is a decorator which can be used to mark functions | ||||
|     as deprecated. It will result in a warning being emitted | ||||
|     when the function is used. | ||||
|     """ | ||||
| 
 | ||||
|     if isinstance(reason, string_types): | ||||
|         # The @deprecated is used with a 'reason'. | ||||
|         # | ||||
|         # .. code-block:: python | ||||
|         # | ||||
|         #    @deprecated("please, use another function") | ||||
|         #    def old_function(x, y): | ||||
|         #      pass | ||||
| 
 | ||||
|         def decorator(func1): | ||||
|             if inspect.isclass(func1): | ||||
|                 fmt1 = f"Call to deprecated class {func1.__name__} ({reason})." | ||||
|             else: | ||||
|                 fmt1 = f"Call to deprecated function {func1.__name__} ({reason})." | ||||
| 
 | ||||
|             @functools.wraps(func1) | ||||
|             def new_func1(*args, **kwargs): | ||||
|                 warn_deprecation(fmt1) | ||||
|                 return func1(*args, **kwargs) | ||||
| 
 | ||||
|             return new_func1 | ||||
| 
 | ||||
|         return decorator | ||||
| 
 | ||||
|     elif inspect.isclass(reason) or inspect.isfunction(reason): | ||||
|         # The @deprecated is used without any 'reason'. | ||||
|         # | ||||
|         # .. code-block:: python | ||||
|         # | ||||
|         #    @deprecated | ||||
|         #    def old_function(x, y): | ||||
|         #      pass | ||||
| 
 | ||||
|         func2 = reason | ||||
| 
 | ||||
|         if inspect.isclass(func2): | ||||
|             fmt2 = f"Call to deprecated class {func2.__name__}." | ||||
|         else: | ||||
|             fmt2 = f"Call to deprecated function {func2.__name__}." | ||||
| 
 | ||||
|         @functools.wraps(func2) | ||||
|         def new_func2(*args, **kwargs): | ||||
|             warn_deprecation(fmt2) | ||||
|             return func2(*args, **kwargs) | ||||
| 
 | ||||
|         return new_func2 | ||||
| 
 | ||||
|     else: | ||||
|         raise TypeError(repr(type(reason))) | ||||
| def warn_deprecation(text: str): | ||||
|     warn(text, category=DeprecationWarning, stacklevel=2) | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| from functools import wraps | ||||
| 
 | ||||
| from .deprecated import deprecated | ||||
| from typing_extensions import deprecated | ||||
| 
 | ||||
| 
 | ||||
| @deprecated("This function is deprecated") | ||||
|  |  | |||
|  | @ -1,75 +1,9 @@ | |||
| from pytest import raises | ||||
| 
 | ||||
| from .. import deprecated | ||||
| from ..deprecated import deprecated as deprecated_decorator | ||||
| from ..deprecated import warn_deprecation | ||||
| 
 | ||||
| 
 | ||||
| def test_warn_deprecation(mocker): | ||||
|     mocker.patch.object(deprecated.warnings, "warn") | ||||
|     mocker.patch.object(deprecated, "warn") | ||||
| 
 | ||||
|     warn_deprecation("OH!") | ||||
|     deprecated.warnings.warn.assert_called_with( | ||||
|         "OH!", stacklevel=2, category=DeprecationWarning | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def test_deprecated_decorator(mocker): | ||||
|     mocker.patch.object(deprecated, "warn_deprecation") | ||||
| 
 | ||||
|     @deprecated_decorator | ||||
|     def my_func(): | ||||
|         return True | ||||
| 
 | ||||
|     result = my_func() | ||||
|     assert result | ||||
|     deprecated.warn_deprecation.assert_called_with( | ||||
|         "Call to deprecated function my_func." | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def test_deprecated_class(mocker): | ||||
|     mocker.patch.object(deprecated, "warn_deprecation") | ||||
| 
 | ||||
|     @deprecated_decorator | ||||
|     class X: | ||||
|         pass | ||||
| 
 | ||||
|     result = X() | ||||
|     assert result | ||||
|     deprecated.warn_deprecation.assert_called_with("Call to deprecated class X.") | ||||
| 
 | ||||
| 
 | ||||
| def test_deprecated_decorator_text(mocker): | ||||
|     mocker.patch.object(deprecated, "warn_deprecation") | ||||
| 
 | ||||
|     @deprecated_decorator("Deprecation text") | ||||
|     def my_func(): | ||||
|         return True | ||||
| 
 | ||||
|     result = my_func() | ||||
|     assert result | ||||
|     deprecated.warn_deprecation.assert_called_with( | ||||
|         "Call to deprecated function my_func (Deprecation text)." | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def test_deprecated_class_text(mocker): | ||||
|     mocker.patch.object(deprecated, "warn_deprecation") | ||||
| 
 | ||||
|     @deprecated_decorator("Deprecation text") | ||||
|     class X: | ||||
|         pass | ||||
| 
 | ||||
|     result = X() | ||||
|     assert result | ||||
|     deprecated.warn_deprecation.assert_called_with( | ||||
|         "Call to deprecated class X (Deprecation text)." | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def test_deprecated_other_object(mocker): | ||||
|     mocker.patch.object(deprecated, "warn_deprecation") | ||||
| 
 | ||||
|     with raises(TypeError): | ||||
|         deprecated_decorator({}) | ||||
|     deprecated.warn.assert_called_with("OH!", stacklevel=2, category=DeprecationWarning) | ||||
|  |  | |||
|  | @ -9,6 +9,5 @@ def test_resolve_only_args(mocker): | |||
|         return root, args | ||||
| 
 | ||||
|     wrapped_resolver = resolve_only_args(resolver) | ||||
|     assert deprecated.warn_deprecation.called | ||||
|     result = wrapped_resolver(1, 2, a=3) | ||||
|     assert result == (1, {"a": 3}) | ||||
|  |  | |||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							|  | @ -50,7 +50,6 @@ tests_require = [ | |||
|     "pytest-cov>=5,<6", | ||||
|     "pytest-mock>=3,<4", | ||||
|     "pytest-asyncio>=0.16,<2", | ||||
|     "snapshottest>=0.6,<1", | ||||
|     "coveralls>=3.3,<5", | ||||
| ] | ||||
| 
 | ||||
|  | @ -84,6 +83,7 @@ setup( | |||
|     install_requires=[ | ||||
|         "graphql-core>=3.1,<3.3", | ||||
|         "graphql-relay>=3.1,<3.3", | ||||
|         "typing-extensions>=4.7.1,<5", | ||||
|     ], | ||||
|     tests_require=tests_require, | ||||
|     extras_require={"test": tests_require, "dev": dev_requires}, | ||||
|  |  | |||
							
								
								
									
										8
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								tox.ini
									
									
									
									
									
								
							|  | @ -5,15 +5,13 @@ skipsdist = true | |||
| [testenv] | ||||
| deps = | ||||
|     .[test] | ||||
| setenv = | ||||
|      PYTHONPATH = .:{envdir} | ||||
| 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] | ||||
| basepython = python3.10 | ||||
| deps = | ||||
|     pre-commit>=2.16,<3 | ||||
|     pre-commit>=3.7,<4 | ||||
| setenv = | ||||
|     LC_CTYPE=en_US.UTF-8 | ||||
| commands = | ||||
|  | @ -22,7 +20,7 @@ commands = | |||
| [testenv:mypy] | ||||
| basepython = python3.10 | ||||
| deps = | ||||
|     mypy>=0.950,<1 | ||||
|     mypy>=1.10,<2 | ||||
| commands = | ||||
|     mypy graphene | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user