Skip to content

Commit 68cdf98

Browse files
test: Adds GraphQLOverHTTP error example tests
1 parent 16f4cf5 commit 68cdf98

3 files changed

Lines changed: 390 additions & 37 deletions

File tree

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import Foundation
2+
import GraphQL
3+
@testable import GraphQLHummingbird
4+
import GraphQLTransportWS
5+
import GraphQLWS
6+
import Hummingbird
7+
import HummingbirdTesting
8+
import NIOFoundationCompat
9+
import Testing
10+
11+
/// Validates status code behavior for the `application/graphql-response+json` media type.
12+
///
13+
/// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#applicationgraphql-responsejson
14+
@Suite
15+
struct HTTPStatusCodeGraphQLJSONTests {
16+
@Test func parsingFailureGivesBadRequest() async throws {
17+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#json-parsing-failure-1
18+
let router = Router()
19+
router.graphql(schema: helloWorldSchema) { _, _ in
20+
EmptyContext()
21+
}
22+
let app = Application(router: router)
23+
24+
try await app.test(.router) { client in
25+
try await client.execute(
26+
uri: "/graphql",
27+
method: .post,
28+
headers: jsonGraphQLHeaders,
29+
body: .init(data: #require(#"{"query":"#.data(using: .utf8)))
30+
) { response in
31+
#expect(response.status == .badRequest)
32+
}
33+
}
34+
}
35+
36+
@Test func invalidParametersGivesBadRequest() async throws {
37+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#invalid-parameters-1
38+
let router = Router()
39+
router.graphql(schema: helloWorldSchema) { _, _ in
40+
EmptyContext()
41+
}
42+
let app = Application(router: router)
43+
44+
try await app.test(.router) { client in
45+
try await client.execute(
46+
uri: "/graphql",
47+
method: .post,
48+
headers: jsonGraphQLHeaders,
49+
body: .init(data: #require(#"{"qeury": "{__typename}"}"#.data(using: .utf8)))
50+
) { response in
51+
#expect(response.status == .badRequest)
52+
}
53+
}
54+
}
55+
56+
@Test func documentParsingFailureGivesBadRequest() async throws {
57+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-parsing-failure-1
58+
let router = Router()
59+
router.graphql(schema: helloWorldSchema) { _, _ in
60+
EmptyContext()
61+
}
62+
let app = Application(router: router)
63+
64+
try await app.test(.router) { client in
65+
try await client.execute(
66+
uri: "/graphql",
67+
method: .post,
68+
headers: jsonGraphQLHeaders,
69+
body: .init(data: #require(#"{"query": "{"}"#.data(using: .utf8)))
70+
) { response in
71+
#expect(response.status == .badRequest)
72+
}
73+
}
74+
}
75+
76+
@Test func documentValidationFailureGivesBadRequest() async throws {
77+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation-failure-1
78+
let router = Router()
79+
router.graphql(schema: helloWorldSchema) { _, _ in
80+
EmptyContext()
81+
}
82+
let app = Application(router: router)
83+
84+
try await app.test(.router) { client in
85+
try await client.execute(
86+
uri: "/graphql",
87+
method: .post,
88+
headers: jsonGraphQLHeaders,
89+
// Fails "No Unused Variables" validation rule
90+
body: .init(data: #require(#"{"query": "query A($name: String) { hello }"}"#.data(using: .utf8)))
91+
) { response in
92+
#expect(response.status == .badRequest)
93+
}
94+
}
95+
}
96+
97+
@Test func operationCannotBeDeterminedGivesBadRequest() async throws {
98+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#operation-cannot-be-determined-1
99+
let router = Router()
100+
router.graphql(schema: helloWorldSchema) { _, _ in
101+
EmptyContext()
102+
}
103+
let app = Application(router: router)
104+
105+
try await app.test(.router) { client in
106+
try await client.execute(
107+
uri: "/graphql",
108+
method: .post,
109+
headers: jsonGraphQLHeaders,
110+
body: .init(data: #require(#"{"query": "abc { hello }"}"#.data(using: .utf8)))
111+
) { response in
112+
#expect(response.status == .badRequest)
113+
}
114+
}
115+
}
116+
117+
@Test func variableCoercionFailureGivesBadRequest() async throws {
118+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#variable-coercion-failure-1
119+
let schema = try GraphQLSchema(
120+
query: GraphQLObjectType(
121+
name: "Query",
122+
fields: [
123+
"get": GraphQLField(
124+
type: GraphQLString,
125+
args: [
126+
"name": GraphQLArgument(type: GraphQLString),
127+
],
128+
resolve: { _, args, _, _ in
129+
guard let name = args["name"].string else {
130+
throw GraphQLError(message: "Name arg is required")
131+
}
132+
return name
133+
}
134+
),
135+
]
136+
)
137+
)
138+
let router = Router()
139+
router.graphql(schema: schema) { _, _ in
140+
EmptyContext()
141+
}
142+
let app = Application(router: router)
143+
144+
try await app.test(.router) { client in
145+
try await client.execute(
146+
uri: "/graphql",
147+
method: .post,
148+
headers: jsonGraphQLHeaders,
149+
body: .init(data: #require(
150+
#"{"query": "query getName($name: String!) { get(name: $name) }", "variables": { "name": null }}"#.data(using: .utf8)
151+
))
152+
) { response in
153+
#expect(response.status == .badRequest)
154+
}
155+
}
156+
}
157+
158+
@Test func fieldErrorGivesOk() async throws {
159+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#field-errors-encountered-during-execution-1
160+
let schema = try GraphQLSchema(
161+
query: GraphQLObjectType(
162+
name: "Query",
163+
fields: [
164+
"error": GraphQLField(
165+
type: GraphQLString,
166+
resolve: { _, _, _, _ in
167+
throw GraphQLError(message: "Something went wrong")
168+
}
169+
),
170+
]
171+
)
172+
)
173+
let router = Router()
174+
router.graphql(schema: schema) { _, _ in
175+
EmptyContext()
176+
}
177+
let app = Application(router: router)
178+
179+
try await app.test(.router) { client in
180+
try await client.execute(
181+
uri: "/graphql",
182+
method: .post,
183+
headers: jsonGraphQLHeaders,
184+
body: .init(data: JSONEncoder().encode(GraphQLRequest(query: "{ error }")))
185+
) { response in
186+
#expect(response.status == .ok)
187+
#expect(response.headers[.contentType] == "application/graphql-response+json; charset=utf-8")
188+
189+
let result = try JSONDecoder().decode(GraphQLResult.self, from: response.body)
190+
#expect(!result.errors.isEmpty)
191+
#expect(result.errors.first?.message == "Something went wrong")
192+
}
193+
}
194+
}
195+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import Foundation
2+
import GraphQL
3+
@testable import GraphQLHummingbird
4+
import GraphQLTransportWS
5+
import GraphQLWS
6+
import Hummingbird
7+
import HummingbirdTesting
8+
import NIOFoundationCompat
9+
import Testing
10+
11+
/// Validates status code behavior for the `application/json` media type.
12+
///
13+
/// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#applicationjson
14+
@Suite
15+
struct HTTPStatusCodeJSONTests {
16+
@Test func parsingFailureGivesBadRequest() async throws {
17+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#json-parsing-failure
18+
let router = Router()
19+
router.graphql(schema: helloWorldSchema) { _, _ in
20+
EmptyContext()
21+
}
22+
let app = Application(router: router)
23+
24+
try await app.test(.router) { client in
25+
try await client.execute(
26+
uri: "/graphql",
27+
method: .post,
28+
headers: jsonHeaders,
29+
body: .init(data: #require(#"{"query":"#.data(using: .utf8)))
30+
) { response in
31+
#expect(response.status == .badRequest)
32+
}
33+
}
34+
}
35+
36+
@Test func invalidParametersGivesBadRequest() async throws {
37+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#invalid-parameters
38+
let router = Router()
39+
router.graphql(schema: helloWorldSchema) { _, _ in
40+
EmptyContext()
41+
}
42+
let app = Application(router: router)
43+
44+
try await app.test(.router) { client in
45+
try await client.execute(
46+
uri: "/graphql",
47+
method: .post,
48+
headers: jsonHeaders,
49+
body: .init(data: #require(#"{"qeury": "{__typename}"}"#.data(using: .utf8)))
50+
) { response in
51+
#expect(response.status == .badRequest)
52+
}
53+
}
54+
}
55+
56+
@Test func documentValidationFailureGivesOk() async throws {
57+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-validation-failure
58+
let router = Router()
59+
router.graphql(schema: helloWorldSchema) { _, _ in
60+
EmptyContext()
61+
}
62+
let app = Application(router: router)
63+
64+
try await app.test(.router) { client in
65+
try await client.execute(
66+
uri: "/graphql",
67+
method: .post,
68+
headers: jsonHeaders,
69+
// Fails "No Unused Variables" validation rule
70+
body: .init(data: #require(#"{"query": "query A($name: String) { hello }"}"#.data(using: .utf8)))
71+
) { response in
72+
#expect(response.status == .ok)
73+
}
74+
}
75+
}
76+
77+
@Test func documentParsingFailureGivesOk() async throws {
78+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#document-parsing-failure
79+
let router = Router()
80+
router.graphql(schema: helloWorldSchema) { _, _ in
81+
EmptyContext()
82+
}
83+
let app = Application(router: router)
84+
85+
try await app.test(.router) { client in
86+
try await client.execute(
87+
uri: "/graphql",
88+
method: .post,
89+
headers: jsonHeaders,
90+
body: .init(data: #require(#"{"query": "{"}"#.data(using: .utf8)))
91+
) { response in
92+
#expect(response.status == .ok)
93+
}
94+
}
95+
}
96+
97+
@Test func variableCoercionFailureGivesOk() async throws {
98+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#variable-coercion-failure
99+
let schema = try GraphQLSchema(
100+
query: GraphQLObjectType(
101+
name: "Query",
102+
fields: [
103+
"get": GraphQLField(
104+
type: GraphQLString,
105+
args: [
106+
"name": GraphQLArgument(type: GraphQLString),
107+
],
108+
resolve: { _, args, _, _ in
109+
guard let name = args["name"].string else {
110+
throw GraphQLError(message: "Name arg is required")
111+
}
112+
return name
113+
}
114+
),
115+
]
116+
)
117+
)
118+
let router = Router()
119+
router.graphql(schema: schema) { _, _ in
120+
EmptyContext()
121+
}
122+
let app = Application(router: router)
123+
124+
try await app.test(.router) { client in
125+
try await client.execute(
126+
uri: "/graphql",
127+
method: .post,
128+
headers: jsonHeaders,
129+
body: .init(data: #require(
130+
#"{"query": "query getName($name: String!) { get(name: $name) }", "variables": { "name": null }}"#.data(using: .utf8)
131+
))
132+
) { response in
133+
#expect(response.status == .ok)
134+
}
135+
}
136+
}
137+
138+
@Test func operationCannotBeDeterminedGivesOk() async throws {
139+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#operation-cannot-be-determined
140+
let router = Router()
141+
router.graphql(schema: helloWorldSchema) { _, _ in
142+
EmptyContext()
143+
}
144+
let app = Application(router: router)
145+
146+
try await app.test(.router) { client in
147+
try await client.execute(
148+
uri: "/graphql",
149+
method: .post,
150+
headers: jsonHeaders,
151+
body: .init(data: #require(#"{"query": "abc { hello }"}"#.data(using: .utf8)))
152+
) { response in
153+
#expect(response.status == .ok)
154+
}
155+
}
156+
}
157+
158+
@Test func fieldErrorGivesOk() async throws {
159+
// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#field-errors-encountered-during-execution
160+
let schema = try GraphQLSchema(
161+
query: GraphQLObjectType(
162+
name: "Query",
163+
fields: [
164+
"error": GraphQLField(
165+
type: GraphQLString,
166+
resolve: { _, _, _, _ in
167+
throw GraphQLError(message: "Something went wrong")
168+
}
169+
),
170+
]
171+
)
172+
)
173+
let router = Router()
174+
router.graphql(schema: schema) { _, _ in
175+
EmptyContext()
176+
}
177+
let app = Application(router: router)
178+
179+
try await app.test(.router) { client in
180+
try await client.execute(
181+
uri: "/graphql",
182+
method: .post,
183+
headers: jsonGraphQLHeaders,
184+
body: .init(data: JSONEncoder().encode(GraphQLRequest(query: "{ error }")))
185+
) { response in
186+
#expect(response.status == .ok)
187+
#expect(response.headers[.contentType] == "application/graphql-response+json; charset=utf-8")
188+
189+
let result = try JSONDecoder().decode(GraphQLResult.self, from: response.body)
190+
#expect(!result.errors.isEmpty)
191+
#expect(result.errors.first?.message == "Something went wrong")
192+
}
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)