Skip to content

Commit 513c473

Browse files
williambaileypaulofaria
authored andcommitted
Fix bug where fragment definition type stack was not correctly set (#16)
* Fix bug where fragment definition type stack was not correctly set * Add some validation tests for FieldsOnCorrectType rule
1 parent 312c571 commit 513c473

File tree

5 files changed

+437
-2
lines changed

5 files changed

+437
-2
lines changed

Sources/GraphQL/Utilities/TypeInfo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ final class TypeInfo {
9595
typeStack.append(outputType as? GraphQLOutputType)
9696

9797
case let node as FragmentDefinition:
98-
let typeConditionAST = node.typeCondition
99-
typeStack.append(typeConditionAST as? GraphQLOutputType)
98+
let outputType = typeFromAST(schema: schema, inputTypeAST: node.typeCondition)
99+
typeStack.append(outputType as? GraphQLOutputType)
100100

101101
case let node as VariableDefinition:
102102
let inputType = typeFromAST(schema: schema, inputTypeAST: node.type)
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
@testable import GraphQL
2+
3+
//
4+
// enum DogCommand { SIT, DOWN, HEEL }
5+
//
6+
let ValidationExampleDogCommand = try! GraphQLEnumType(
7+
name: "DogCommand",
8+
values: [
9+
"SIT": GraphQLEnumValue(
10+
value: "SIT"
11+
),
12+
"DOWN": GraphQLEnumValue(
13+
value: "DOWN"
14+
),
15+
"HEEL": GraphQLEnumValue(
16+
value: "HEEL"
17+
),
18+
]
19+
)
20+
21+
// interface Sentient {
22+
// name: String!
23+
// }
24+
let ValidationExampleSentient = try! GraphQLInterfaceType(
25+
name: "Sentient",
26+
fields: [
27+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
28+
],
29+
resolveType: { _, _, info in
30+
return "Unknown"
31+
}
32+
)
33+
34+
// type Alien implements Sentient {
35+
// name: String!
36+
// homePlanet: String
37+
// }
38+
let ValidationExampleAlien = try! GraphQLObjectType(
39+
name: "Alien",
40+
fields: [
41+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
42+
"homePlanet": GraphQLField(type: GraphQLString),
43+
],
44+
interfaces: [ValidationExampleSentient]
45+
)
46+
47+
// type Human implements Sentient {
48+
// name: String!
49+
// pets: [Pet!]!
50+
// }
51+
let ValidationExampleHuman = try! GraphQLObjectType(
52+
name: "Human",
53+
fields: [
54+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
55+
"pets": GraphQLField(type: GraphQLNonNull(GraphQLList(GraphQLNonNull(ValidationExamplePet)))),
56+
],
57+
interfaces: [ValidationExampleSentient]
58+
)
59+
60+
// interface Pet {
61+
// name: String!
62+
// }
63+
let ValidationExamplePet = try! GraphQLInterfaceType(
64+
name: "Pet",
65+
fields: [
66+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
67+
],
68+
resolveType: { _, _, _ in
69+
return "Unknown"
70+
}
71+
)
72+
73+
// type Dog implements Pet {
74+
// name: String!
75+
// nickname: String
76+
// barkVolume: Int
77+
// doesKnowCommand(dogCommand: DogCommand!): Boolean!
78+
// isHousetrained(atOtherHomes: Boolean): Boolean!
79+
// owner: Human
80+
// }
81+
let ValidationExampleDog = try! GraphQLObjectType(
82+
name: "Dog",
83+
fields: [
84+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
85+
"nickname": GraphQLField(type: GraphQLString),
86+
"barkVolume": GraphQLField(type: GraphQLInt),
87+
"doesKnowCommand": GraphQLField(
88+
type: GraphQLNonNull(GraphQLBoolean),
89+
args: [
90+
"dogCommand": GraphQLArgument(type: GraphQLNonNull(ValidationExampleDogCommand))
91+
]
92+
),
93+
"isHousetrained": GraphQLField(
94+
type: GraphQLNonNull(GraphQLBoolean),
95+
args: [
96+
"atOtherHomes": GraphQLArgument(type: GraphQLBoolean)
97+
]
98+
),
99+
"owner": GraphQLField(type: ValidationExampleHuman),
100+
],
101+
interfaces: [ValidationExamplePet]
102+
)
103+
104+
// enum CatCommand { JUMP }
105+
let ValidationExampleCatCommand = try! GraphQLEnumType(
106+
name: "CatCommand",
107+
values: [
108+
"JUMP": GraphQLEnumValue(
109+
value: "JUMP"
110+
),
111+
]
112+
)
113+
114+
// type Cat implements Pet {
115+
// name: String!
116+
// nickname: String
117+
// doesKnowCommand(catCommand: CatCommand!): Boolean!
118+
// meowVolume: Int
119+
// }
120+
let ValidationExampleCat = try! GraphQLObjectType(
121+
name: "Cat",
122+
fields: [
123+
"name": GraphQLField(type: GraphQLNonNull(GraphQLString)),
124+
"nickname": GraphQLField(type: GraphQLString),
125+
"doesKnowCommand": GraphQLField(
126+
type: GraphQLNonNull(GraphQLBoolean),
127+
args: [
128+
"catCommand": GraphQLArgument(type: GraphQLNonNull(ValidationExampleCatCommand))
129+
]
130+
),
131+
"meowVolume": GraphQLField(type: GraphQLInt),
132+
],
133+
interfaces: [ValidationExamplePet]
134+
)
135+
136+
// union CatOrDog = Cat | Dog
137+
let ValidationExampleCatOrDog = try! GraphQLUnionType(
138+
name: "CatOrDog",
139+
types: [ValidationExampleCat, ValidationExampleDog]
140+
)
141+
142+
// union DogOrHuman = Dog | Human
143+
let ValidationExampleDogOrHuman = try! GraphQLUnionType(
144+
name: "DogOrHuman",
145+
types: [ValidationExampleDog, ValidationExampleHuman]
146+
)
147+
148+
// union HumanOrAlien = Human | Alien
149+
let ValidationExampleHumanOrAlien = try! GraphQLUnionType(
150+
name: "HumanOrAlien",
151+
types: [ValidationExampleHuman, ValidationExampleAlien]
152+
)
153+
154+
// type QueryRoot {
155+
// dog: Dog
156+
// }
157+
let ValidationExampleQueryRoot = try! GraphQLObjectType(
158+
name: "QueryRoot",
159+
fields: [
160+
"dog": GraphQLField(type: ValidationExampleDog),
161+
]
162+
)
163+
164+
let ValidationExampleSchema = try! GraphQLSchema(
165+
query: ValidationExampleQueryRoot,
166+
types: [
167+
ValidationExampleDog,
168+
]
169+
)
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class FieldsOnCorrectTypeTests : ValidationTestCase {
5+
6+
override func setUp() {
7+
rule = FieldsOnCorrectType
8+
}
9+
10+
func testValidWithObjectFieldSelection() throws {
11+
try assertValid(
12+
"fragment objectFieldSelection on Dog { __typename name }"
13+
)
14+
}
15+
16+
func testValidWithAliasedObjectFieldSelection() throws {
17+
try assertValid(
18+
"fragment aliasedObjectFieldSelection on Dog { tn : __typename otherName : name }"
19+
)
20+
}
21+
22+
func testValidWithInterfaceFieldSelection() throws {
23+
try assertValid(
24+
"fragment interfaceFieldSelection on Pet { __typename name }"
25+
)
26+
}
27+
28+
func testValidWithAliasedInterfaceFieldSelection() throws {
29+
try assertValid(
30+
"fragment aliasedInterfaceFieldSelection on Pet { otherName : name }"
31+
)
32+
}
33+
34+
func testValidWithLyingAliasSelection() throws {
35+
try assertValid(
36+
"fragment lyingAliasSelection on Dog { name : nickname }"
37+
)
38+
}
39+
40+
func testValidWithInlineFragment() throws {
41+
try assertValid(
42+
"fragment inlineFragment on Pet { ... on Dog { name } ... { name } }"
43+
)
44+
}
45+
46+
func testValidWhenMetaFieldSelectionOnUnion() throws {
47+
try assertValid(
48+
"fragment metaFieldSelectionOnUnion on CatOrDog { __typename }"
49+
)
50+
}
51+
52+
func testValidWithIgnoresFieldsOnUnknownType() throws {
53+
try assertValid(
54+
"fragment ignoresFieldsOnUnknownType on UnknownType { unknownField }"
55+
)
56+
}
57+
58+
func testInvalidWhenTypeKnownAgain() throws {
59+
let errors = try assertInvalid(
60+
errorCount: 1,
61+
query: "fragment typeKnownAgain on Pet { unknown_pet_field { ... on Cat { unknown_cat_field } } }"
62+
)
63+
try assertValidationError(
64+
error: errors.first, line: 1, column: 34,
65+
message: "Cannot query field \"unknown_pet_field\" on type \"Pet\"."
66+
)
67+
}
68+
69+
func testInvalidWhenFieldNotDefined() throws {
70+
let errors = try assertInvalid(
71+
errorCount: 1,
72+
query: "fragment fieldNotDefined on Dog { meowVolume }"
73+
)
74+
try assertValidationError(
75+
error: errors.first, line: 1, column: 35,
76+
message: "Cannot query field \"meowVolume\" on type \"Dog\". Did you mean \"barkVolume\"?"
77+
)
78+
}
79+
80+
func testInvalidWhenDeepFieldNotDefined() throws {
81+
let errors = try assertInvalid(
82+
errorCount: 1,
83+
query: "fragment deepFieldNotDefined on Dog { unknown_field { deeper_unknown_field }}"
84+
)
85+
try assertValidationError(
86+
error: errors.first, line: 1, column: 39,
87+
message: "Cannot query field \"unknown_field\" on type \"Dog\"."
88+
)
89+
}
90+
91+
func testInvalidWhenSubFieldNotDefined() throws {
92+
let errors = try assertInvalid(
93+
errorCount: 1,
94+
query: "fragment subFieldNotDefined on Human { pets { unknown_field } }"
95+
)
96+
try assertValidationError(
97+
error: errors.first, line: 1, column: 47,
98+
message: "Cannot query field \"unknown_field\" on type \"Pet\"."
99+
)
100+
}
101+
102+
func testInvalidWhenFieldNotDefinedOnInlineFragment() throws {
103+
let errors = try assertInvalid(
104+
errorCount: 1,
105+
query: "fragment fieldNotDefinedOnInlineFragment on Pet { ... on Dog { meowVolume } }"
106+
)
107+
try assertValidationError(
108+
error: errors.first, line: 1, column: 64,
109+
message: "Cannot query field \"meowVolume\" on type \"Dog\". Did you mean \"barkVolume\"?"
110+
)
111+
}
112+
113+
func testInvalidWhenAliasedFieldTargetNotDefined() throws {
114+
let errors = try assertInvalid(
115+
errorCount: 1,
116+
query: "fragment aliasedFieldTargetNotDefined on Dog { volume : mooVolume }"
117+
)
118+
try assertValidationError(
119+
error: errors.first, line: 1, column: 48,
120+
message: "Cannot query field \"mooVolume\" on type \"Dog\". Did you mean \"barkVolume\"?"
121+
)
122+
}
123+
124+
func testInvalidWhenAliasedLyingFieldTargetNotDefined() throws {
125+
let errors = try assertInvalid(
126+
errorCount: 1,
127+
query: "fragment aliasedLyingFieldTargetNotDefined on Dog { barkVolume : kawVolume }"
128+
)
129+
try assertValidationError(
130+
error: errors.first, line: 1, column: 53,
131+
message: "Cannot query field \"kawVolume\" on type \"Dog\". Did you mean \"barkVolume\"?"
132+
)
133+
}
134+
135+
func testInvalidWhenNotDefinedOnInterface() throws {
136+
let errors = try assertInvalid(
137+
errorCount: 1,
138+
query: "fragment notDefinedOnInterface on Pet { tailLength }"
139+
)
140+
try assertValidationError(
141+
error: errors.first, line: 1, column: 41,
142+
message: "Cannot query field \"tailLength\" on type \"Pet\"."
143+
)
144+
}
145+
146+
func testInvalidWhenDefinedOnImplementorsButNotInterface() throws {
147+
let errors = try assertInvalid(
148+
errorCount: 1,
149+
query: "fragment definedOnImplementorsButNotInterface on Pet { nickname }"
150+
)
151+
try assertValidationError(
152+
error: errors.first, line: 1, column: 56,
153+
message: "Cannot query field \"nickname\" on type \"Pet\". Did you mean \"name\"?"
154+
)
155+
}
156+
157+
/*
158+
func testInvalidWhenDirectFieldSelectionOnUnion() throws {
159+
let errors = try assertInvalid(
160+
errorCount: 1,
161+
query: "fragment directFieldSelectionOnUnion on CatOrDog { directField }"
162+
)
163+
try assertValidationError(
164+
error: errors.first, line: 1, column: 0,
165+
message: ""
166+
)
167+
}
168+
169+
func testInvalidWhenDefinedOnImplementorsQueriedOnUnion() throws {
170+
let errors = try assertInvalid(
171+
errorCount: 1,
172+
query: "fragment definedOnImplementorsQueriedOnUnion on CatOrDog { name }"
173+
)
174+
try assertValidationError(
175+
error: errors.first, line: 1, column: 0,
176+
message: ""
177+
)
178+
}
179+
*/
180+
181+
}
182+
183+
extension FieldsOnCorrectTypeTests {
184+
static var allTests: [(String, (FieldsOnCorrectTypeTests) -> () throws -> Void)] {
185+
return [
186+
("testValidWithObjectFieldSelection", testValidWithObjectFieldSelection),
187+
("testValidWithAliasedObjectFieldSelection", testValidWithAliasedObjectFieldSelection),
188+
("testValidWithInterfaceFieldSelection", testValidWithInterfaceFieldSelection),
189+
("testValidWithAliasedInterfaceFieldSelection", testValidWithAliasedInterfaceFieldSelection),
190+
("testValidWithLyingAliasSelection", testValidWithLyingAliasSelection),
191+
("testValidWithInlineFragment", testValidWithInlineFragment),
192+
("testValidWhenMetaFieldSelectionOnUnion", testValidWhenMetaFieldSelectionOnUnion),
193+
("testValidWithIgnoresFieldsOnUnknownType", testValidWithIgnoresFieldsOnUnknownType),
194+
("testInvalidWhenTypeKnownAgain", testInvalidWhenTypeKnownAgain),
195+
("testInvalidWhenFieldNotDefined", testInvalidWhenFieldNotDefined),
196+
("testInvalidWhenDeepFieldNotDefined", testInvalidWhenDeepFieldNotDefined),
197+
("testInvalidWhenSubFieldNotDefined", testInvalidWhenSubFieldNotDefined),
198+
("testInvalidWhenFieldNotDefinedOnInlineFragment", testInvalidWhenFieldNotDefinedOnInlineFragment),
199+
("testInvalidWhenAliasedFieldTargetNotDefined", testInvalidWhenAliasedFieldTargetNotDefined),
200+
("testInvalidWhenAliasedLyingFieldTargetNotDefined", testInvalidWhenAliasedLyingFieldTargetNotDefined),
201+
("testInvalidWhenNotDefinedOnInterface", testInvalidWhenNotDefinedOnInterface),
202+
("testInvalidWhenDefinedOnImplementorsButNotInterface", testInvalidWhenDefinedOnImplementorsButNotInterface),
203+
/*
204+
("testInvalidWhenDirectFieldSelectionOnUnion", testInvalidWhenDirectFieldSelectionOnUnion),
205+
("testInvalidWhenDefinedOnImplementorsQueriedOnUnion", testInvalidWhenDefinedOnImplementorsQueriedOnUnion),
206+
*/
207+
]
208+
}
209+
}

0 commit comments

Comments
 (0)