Skip to content

Commit 68b8ad5

Browse files
committed
Implement unit tests for subscriptions.
1 parent 450620d commit 68b8ad5

16 files changed

+299
-29
lines changed

graphql/core/execution/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ def get_operation_root_type(schema, operation):
142142
[operation]
143143
)
144144

145+
return subscription_type
146+
145147
raise GraphQLError(
146148
'Can only execute queries, mutations and subscriptions',
147149
[operation]

graphql/core/type/introspection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
'__Schema',
2121
description='A GraphQL Schema defines the capabilities of a GraphQL server. It '
2222
'exposes all available types and directives on the server, as well as '
23-
'the entry points for query and mutation operations.',
23+
'the entry points for query, mutation and subscription operations.',
2424
fields=lambda: OrderedDict([
2525
('types', GraphQLField(
2626
description='A list of all types supported by this server.',

tests/core_execution/test_executor.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class Data(object):
285285

286286

287287
def test_uses_the_query_schema_for_queries():
288-
doc = 'query Q { a } mutation M { c }'
288+
doc = 'query Q { a } mutation M { c } subscription S { a }'
289289

290290
class Data(object):
291291
a = 'b'
@@ -298,7 +298,10 @@ class Data(object):
298298
M = GraphQLObjectType('M', {
299299
'c': GraphQLField(GraphQLString)
300300
})
301-
result = execute(GraphQLSchema(Q, M), Data(), ast, 'Q')
301+
S = GraphQLObjectType('S', {
302+
'a': GraphQLField(GraphQLString)
303+
})
304+
result = execute(GraphQLSchema(Q, M, S), Data(), ast, 'Q')
302305
assert not result.errors
303306
assert result.data == {'a': 'b'}
304307

@@ -322,6 +325,25 @@ class Data(object):
322325
assert result.data == {'c': 'd'}
323326

324327

328+
def test_uses_the_subscription_schema_for_subscriptions():
329+
doc = 'query Q { a } subscription S { a }'
330+
331+
class Data(object):
332+
a = 'b'
333+
c = 'd'
334+
335+
ast = parse(doc)
336+
Q = GraphQLObjectType('Q', {
337+
'a': GraphQLField(GraphQLString)
338+
})
339+
S = GraphQLObjectType('S', {
340+
'a': GraphQLField(GraphQLString)
341+
})
342+
result = execute(GraphQLSchema(Q, subscription=S), Data(), ast, 'S')
343+
assert not result.errors
344+
assert result.data == {'a': 'b'}
345+
346+
325347
def test_avoids_recursion():
326348
doc = '''
327349
query Q {

tests/core_language/fixtures.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@
3535
}
3636
}
3737
38+
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
39+
storyLikeSubscribe(input: $input) {
40+
story {
41+
likers {
42+
count
43+
}
44+
likeSentence {
45+
text
46+
}
47+
}
48+
}
49+
}
50+
3851
fragment frag on Friend {
3952
foo(size: $size, bar: $b, obj: {key: "value"})
4053
}

tests/core_language/test_parser.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def tesst_allows_non_keywords_anywhere_a_name_is_allowed():
113113
'fragment',
114114
'query',
115115
'mutation',
116+
'subscription',
116117
'true',
117118
'false'
118119
]
@@ -139,22 +140,38 @@ def test_parses_kitchen_sink():
139140
parse(KITCHEN_SINK)
140141

141142

142-
def parses_experimental_subscription_feature():
143+
def test_parses_anonymous_mutation_operations():
143144
parse('''
144-
subscription Foo {
145-
subscriptionField
145+
mutation {
146+
mutationField
146147
}
147148
''')
148149

149150

150-
def test_parses_anonymous_operations():
151+
def test_parses_anonymous_subscription_operations():
151152
parse('''
152-
mutation {
153+
subscription {
154+
mutationField
155+
}
156+
''')
157+
158+
159+
def test_parses_named_mutation_operations():
160+
parse('''
161+
mutation Foo {
153162
mutationField
154163
}
155164
''')
156165

157166

167+
def test_parses_named_subscription_operations():
168+
parse('''
169+
subscription Foo {
170+
subscriptionField
171+
}
172+
''')
173+
174+
158175
def test_parse_creates_ast():
159176
source = Source("""{
160177
node(id: 4) {

tests/core_language/test_printer.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ def test_prints_kitchen_sink():
5757
}
5858
}
5959
60+
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
61+
storyLikeSubscribe(input: $input) {
62+
story {
63+
likers {
64+
count
65+
}
66+
likeSentence {
67+
text
68+
}
69+
}
70+
}
71+
}
72+
6073
fragment frag on Friend {
6174
foo(size: $size, bar: $b, obj: {key: "value"})
6275
}

tests/core_language/test_visitor.py

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ def leave(self, node, key, parent, *args):
168168
visited.append(['leave', type(node).__name__, key, kind])
169169

170170
visit(ast, TestVisitor())
171-
172171
assert visited == [
173172
['enter', 'Document', None, None],
174173
['enter', 'OperationDefinition', 0, None],
@@ -345,7 +344,63 @@ def leave(self, node, key, parent, *args):
345344
['leave', 'Field', 0, None],
346345
['leave', 'SelectionSet', 'selection_set', 'OperationDefinition'],
347346
['leave', 'OperationDefinition', 1, None],
348-
['enter', 'FragmentDefinition', 2, None],
347+
['enter', 'OperationDefinition', 2, None],
348+
['enter', 'Name', 'name', 'OperationDefinition'],
349+
['leave', 'Name', 'name', 'OperationDefinition'],
350+
['enter', 'VariableDefinition', 0, None],
351+
['enter', 'Variable', 'variable', 'VariableDefinition'],
352+
['enter', 'Name', 'name', 'Variable'],
353+
['leave', 'Name', 'name', 'Variable'],
354+
['leave', 'Variable', 'variable', 'VariableDefinition'],
355+
['enter', 'NamedType', 'type', 'VariableDefinition'],
356+
['enter', 'Name', 'name', 'NamedType'],
357+
['leave', 'Name', 'name', 'NamedType'],
358+
['leave', 'NamedType', 'type', 'VariableDefinition'],
359+
['leave', 'VariableDefinition', 0, None],
360+
['enter', 'SelectionSet', 'selection_set', 'OperationDefinition'],
361+
['enter', 'Field', 0, None],
362+
['enter', 'Name', 'name', 'Field'],
363+
['leave', 'Name', 'name', 'Field'],
364+
['enter', 'Argument', 0, None],
365+
['enter', 'Name', 'name', 'Argument'],
366+
['leave', 'Name', 'name', 'Argument'],
367+
['enter', 'Variable', 'value', 'Argument'],
368+
['enter', 'Name', 'name', 'Variable'],
369+
['leave', 'Name', 'name', 'Variable'],
370+
['leave', 'Variable', 'value', 'Argument'],
371+
['leave', 'Argument', 0, None],
372+
['enter', 'SelectionSet', 'selection_set', 'Field'],
373+
['enter', 'Field', 0, None],
374+
['enter', 'Name', 'name', 'Field'],
375+
['leave', 'Name', 'name', 'Field'],
376+
['enter', 'SelectionSet', 'selection_set', 'Field'],
377+
['enter', 'Field', 0, None],
378+
['enter', 'Name', 'name', 'Field'],
379+
['leave', 'Name', 'name', 'Field'],
380+
['enter', 'SelectionSet', 'selection_set', 'Field'],
381+
['enter', 'Field', 0, None],
382+
['enter', 'Name', 'name', 'Field'],
383+
['leave', 'Name', 'name', 'Field'],
384+
['leave', 'Field', 0, None],
385+
['leave', 'SelectionSet', 'selection_set', 'Field'],
386+
['leave', 'Field', 0, None],
387+
['enter', 'Field', 1, None],
388+
['enter', 'Name', 'name', 'Field'],
389+
['leave', 'Name', 'name', 'Field'],
390+
['enter', 'SelectionSet', 'selection_set', 'Field'],
391+
['enter', 'Field', 0, None],
392+
['enter', 'Name', 'name', 'Field'],
393+
['leave', 'Name', 'name', 'Field'],
394+
['leave', 'Field', 0, None],
395+
['leave', 'SelectionSet', 'selection_set', 'Field'],
396+
['leave', 'Field', 1, None],
397+
['leave', 'SelectionSet', 'selection_set', 'Field'],
398+
['leave', 'Field', 0, None],
399+
['leave', 'SelectionSet', 'selection_set', 'Field'],
400+
['leave', 'Field', 0, None],
401+
['leave', 'SelectionSet', 'selection_set', 'OperationDefinition'],
402+
['leave', 'OperationDefinition', 2, None],
403+
['enter', 'FragmentDefinition', 3, None],
349404
['enter', 'Name', 'name', 'FragmentDefinition'],
350405
['leave', 'Name', 'name', 'FragmentDefinition'],
351406
['enter', 'NamedType', 'type_condition', 'FragmentDefinition'],
@@ -386,8 +441,8 @@ def leave(self, node, key, parent, *args):
386441
['leave', 'Argument', 2, None],
387442
['leave', 'Field', 0, None],
388443
['leave', 'SelectionSet', 'selection_set', 'FragmentDefinition'],
389-
['leave', 'FragmentDefinition', 2, None],
390-
['enter', 'OperationDefinition', 3, None],
444+
['leave', 'FragmentDefinition', 3, None],
445+
['enter', 'OperationDefinition', 4, None],
391446
['enter', 'SelectionSet', 'selection_set', 'OperationDefinition'],
392447
['enter', 'Field', 0, None],
393448
['enter', 'Name', 'name', 'Field'],
@@ -410,6 +465,6 @@ def leave(self, node, key, parent, *args):
410465
['leave', 'Name', 'name', 'Field'],
411466
['leave', 'Field', 1, None],
412467
['leave', 'SelectionSet', 'selection_set', 'OperationDefinition'],
413-
['leave', 'OperationDefinition', 3, None],
468+
['leave', 'OperationDefinition', 4, None],
414469
['leave', 'Document', None, None]
415470
]

tests/core_type/test_definition.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@
5858
'writeArticle': GraphQLField(BlogArticle)
5959
})
6060

61+
BlogSubscription = GraphQLObjectType('Subscription', {
62+
'articleSubscribe': GraphQLField(
63+
args={'id': GraphQLArgument(GraphQLString)},
64+
type=BlogArticle
65+
)
66+
})
67+
6168
ObjectType = GraphQLObjectType('Object', {})
6269
InterfaceType = GraphQLInterfaceType('Interface')
6370
UnionType = GraphQLUnionType('Union', [ObjectType], resolve_type=lambda: None)
@@ -106,6 +113,20 @@ def test_defines_a_mutation_schema():
106113
assert write_mutation.name == 'writeArticle'
107114

108115

116+
def test_defines_a_subscription_schema():
117+
BlogSchema = GraphQLSchema(
118+
query=BlogQuery,
119+
subscription=BlogSubscription
120+
)
121+
122+
assert BlogSchema.get_subscription_type() == BlogSubscription
123+
124+
subscription = BlogSubscription.get_fields()['articleSubscribe']
125+
assert subscription.type == BlogArticle
126+
assert subscription.type.name == 'Article'
127+
assert subscription.name == 'articleSubscribe'
128+
129+
109130
def test_includes_nested_input_objects_in_the_map():
110131
NestedInputObject = GraphQLInputObjectType(
111132
name='NestedInputObject',
@@ -128,10 +149,22 @@ def test_includes_nested_input_objects_in_the_map():
128149
)
129150
}
130151
)
152+
SomeSubscription = GraphQLObjectType(
153+
name='SomeSubscription',
154+
fields={
155+
'subscribeToSomething': GraphQLField(
156+
type=BlogArticle,
157+
args={
158+
'input': GraphQLArgument(SomeInputObject)
159+
}
160+
)
161+
}
162+
)
131163

132164
schema = GraphQLSchema(
133165
query=BlogQuery,
134-
mutation=SomeMutation
166+
mutation=SomeMutation,
167+
subscription=SomeSubscription
135168
)
136169

137170
assert schema.get_type_map()['NestedInputObject'] is NestedInputObject

tests/core_type/test_enum_type.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
GraphQLString,
1111
GraphQLSchema
1212
)
13-
1413
from graphql.core import graphql
1514

1615
ColorType = GraphQLEnumType(
@@ -67,7 +66,20 @@ def get_first(args, *keys):
6766
}
6867
)
6968

70-
Schema = GraphQLSchema(query=QueryType, mutation=MutationType)
69+
SubscriptionType = GraphQLObjectType(
70+
name='Subscription',
71+
fields={
72+
'subscribeToEnum': GraphQLField(
73+
type=ColorType,
74+
args={
75+
'color': GraphQLArgument(ColorType)
76+
},
77+
resolver=lambda value, args, info: args.get('color')
78+
)
79+
}
80+
)
81+
82+
Schema = GraphQLSchema(query=QueryType, mutation=MutationType, subscription=SubscriptionType)
7183

7284

7385
def test_accepts_enum_literals_as_input():
@@ -131,6 +143,12 @@ def test_accepts_enum_literals_as_input_arguments_to_mutations():
131143
assert result.data == {'favoriteEnum': 'GREEN'}
132144

133145

146+
def test_accepts_enum_literals_as_input_arguments_to_subscriptions():
147+
result = graphql(Schema, 'subscription x($color: Color!) { subscribeToEnum(color: $color) }', None, {'color': 'GREEN'})
148+
assert not result.errors
149+
assert result.data == {'subscribeToEnum': 'GREEN'}
150+
151+
134152
def test_does_not_accept_internal_value_as_enum_variable():
135153
result = graphql(Schema, 'query test($color: Color!) { colorEnum(fromEnum: $color) }', None, {'color': 2})
136154
assert not result.data

tests/core_type/test_introspection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def test_executes_an_introspection_query():
8080
'name': u'String',
8181
'possibleTypes': None},
8282
{
83-
'description': u'A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query and mutation operations.',
83+
'description': u'A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation and subscription operations.',
8484
'enumValues': None,
8585
'fields': [{'args': [],
8686
'deprecationReason': None,
@@ -746,7 +746,7 @@ def test_exposes_descriptions_on_types_and_fields():
746746
'description': 'A GraphQL Schema defines the capabilities of a ' +
747747
'GraphQL server. It exposes all available types and ' +
748748
'directives on the server, as well as the entry ' +
749-
'points for query and mutation operations.',
749+
'points for query, mutation and subscription operations.',
750750
'fields': [
751751
{
752752
'name': 'types',

0 commit comments

Comments
 (0)