1pub mod graphiql;
4pub mod playground;
5
6use serde::{
7 Deserialize, Serialize, de,
8 ser::{self, SerializeMap},
9};
10
11use crate::{
12 FieldError, GraphQLError, GraphQLSubscriptionType, GraphQLType, GraphQLTypeAsync, RootNode,
13 Value, Variables,
14 ast::InputValue,
15 executor::{ExecutionError, ValuesStream},
16 value::{DefaultScalarValue, ScalarValue},
17};
18
19#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
27pub struct GraphQLRequest<S = DefaultScalarValue>
28where
29 S: ScalarValue,
30{
31 pub query: String,
33
34 #[serde(rename = "operationName")]
36 pub operation_name: Option<String>,
37
38 #[serde(bound(
41 deserialize = "InputValue<S>: Deserialize<'de>",
42 serialize = "InputValue<S>: Serialize",
43 ))]
44 pub variables: Option<InputValue<S>>,
45}
46
47impl<S> GraphQLRequest<S>
48where
49 S: ScalarValue,
50{
51 #[deprecated(since = "0.16.0", note = "Use the direct field access instead.")]
54 pub fn operation_name(&self) -> Option<&str> {
55 self.operation_name.as_deref()
56 }
57
58 pub fn variables(&self) -> Variables<S> {
60 self.variables
61 .as_ref()
62 .and_then(|iv| {
63 iv.to_object_value()
64 .map(|o| o.into_iter().map(|(k, v)| (k.into(), v.clone())).collect())
65 })
66 .unwrap_or_default()
67 }
68
69 pub fn new(
71 query: String,
72 operation_name: Option<String>,
73 variables: Option<InputValue<S>>,
74 ) -> Self {
75 Self {
76 query,
77 operation_name,
78 variables,
79 }
80 }
81
82 pub fn execute_sync<QueryT, MutationT, SubscriptionT>(
87 &self,
88 root_node: &RootNode<QueryT, MutationT, SubscriptionT, S>,
89 context: &QueryT::Context,
90 ) -> GraphQLResponse<S>
91 where
92 S: ScalarValue,
93 QueryT: GraphQLType<S>,
94 MutationT: GraphQLType<S, Context = QueryT::Context>,
95 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
96 {
97 GraphQLResponse(crate::execute_sync(
98 &self.query,
99 self.operation_name.as_deref(),
100 root_node,
101 &self.variables(),
102 context,
103 ))
104 }
105
106 pub async fn execute<'a, QueryT, MutationT, SubscriptionT>(
111 &'a self,
112 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
113 context: &'a QueryT::Context,
114 ) -> GraphQLResponse<S>
115 where
116 QueryT: GraphQLTypeAsync<S>,
117 QueryT::TypeInfo: Sync,
118 QueryT::Context: Sync,
119 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
120 MutationT::TypeInfo: Sync,
121 SubscriptionT: GraphQLType<S, Context = QueryT::Context> + Sync,
122 SubscriptionT::TypeInfo: Sync,
123 S: ScalarValue + Send + Sync,
124 {
125 let op = self.operation_name.as_deref();
126 let vars = &self.variables();
127 let res = crate::execute(&self.query, op, root_node, vars, context).await;
128 GraphQLResponse(res)
129 }
130}
131
132pub async fn resolve_into_stream<'req, 'rn, 'ctx, 'a, QueryT, MutationT, SubscriptionT, S>(
137 req: &'req GraphQLRequest<S>,
138 root_node: &'rn RootNode<QueryT, MutationT, SubscriptionT, S>,
139 context: &'ctx QueryT::Context,
140) -> Result<(Value<ValuesStream<'a, S>>, Vec<ExecutionError<S>>), GraphQLError>
141where
142 'req: 'a,
143 'rn: 'a,
144 'ctx: 'a,
145 QueryT: GraphQLTypeAsync<S>,
146 QueryT::TypeInfo: Sync,
147 QueryT::Context: Sync,
148 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
149 MutationT::TypeInfo: Sync,
150 SubscriptionT: GraphQLSubscriptionType<S, Context = QueryT::Context>,
151 SubscriptionT::TypeInfo: Sync,
152 S: ScalarValue + Send + Sync,
153{
154 let op = req.operation_name.as_deref();
155 let vars = req.variables();
156
157 crate::resolve_into_stream(&req.query, op, root_node, &vars, context).await
158}
159
160#[derive(Clone, Debug, PartialEq)]
166pub struct GraphQLResponse<S = DefaultScalarValue>(
167 Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>,
168);
169
170impl<S> GraphQLResponse<S>
171where
172 S: ScalarValue,
173{
174 #[must_use]
176 pub fn from_result(r: Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>) -> Self {
177 Self(r)
178 }
179
180 pub fn into_result(self) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError> {
182 self.0
183 }
184
185 #[must_use]
187 pub fn error(error: FieldError<S>) -> Self {
188 Self(Ok((Value::null(), vec![ExecutionError::at_origin(error)])))
189 }
190
191 #[must_use]
196 pub fn is_ok(&self) -> bool {
197 self.0.is_ok()
198 }
199}
200
201impl<T> Serialize for GraphQLResponse<T>
202where
203 T: Serialize + ScalarValue,
204 Value<T>: Serialize,
205 ExecutionError<T>: Serialize,
206{
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: ser::Serializer,
210 {
211 match self.0 {
212 Ok((ref res, ref err)) => {
213 let mut map = serializer.serialize_map(None)?;
214
215 map.serialize_key("data")?;
216 map.serialize_value(res)?;
217
218 if !err.is_empty() {
219 map.serialize_key("errors")?;
220 map.serialize_value(err)?;
221 }
222
223 map.end()
224 }
225 Err(ref err) => {
226 let mut map = serializer.serialize_map(Some(1))?;
227 map.serialize_key("errors")?;
228 map.serialize_value(err)?;
229 map.end()
230 }
231 }
232 }
233}
234
235#[derive(Debug, Deserialize, PartialEq)]
237#[serde(untagged)]
238#[serde(bound = "InputValue<S>: Deserialize<'de>")]
239pub enum GraphQLBatchRequest<S = DefaultScalarValue>
240where
241 S: ScalarValue,
242{
243 Single(GraphQLRequest<S>),
245
246 #[serde(deserialize_with = "deserialize_non_empty_batch")]
250 Batch(Vec<GraphQLRequest<S>>),
251}
252
253fn deserialize_non_empty_batch<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
254where
255 D: de::Deserializer<'de>,
256 T: Deserialize<'de>,
257{
258 use de::Error as _;
259
260 let v = Vec::<T>::deserialize(deserializer)?;
261 if v.is_empty() {
262 Err(D::Error::invalid_length(
263 0,
264 &"non-empty batch of GraphQL requests",
265 ))
266 } else {
267 Ok(v)
268 }
269}
270
271impl<S> GraphQLBatchRequest<S>
272where
273 S: ScalarValue,
274{
275 pub fn execute_sync<'a, QueryT, MutationT, SubscriptionT>(
279 &'a self,
280 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
281 context: &QueryT::Context,
282 ) -> GraphQLBatchResponse<S>
283 where
284 QueryT: GraphQLType<S>,
285 MutationT: GraphQLType<S, Context = QueryT::Context>,
286 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
287 {
288 match *self {
289 Self::Single(ref req) => {
290 GraphQLBatchResponse::Single(req.execute_sync(root_node, context))
291 }
292 Self::Batch(ref reqs) => GraphQLBatchResponse::Batch(
293 reqs.iter()
294 .map(|req| req.execute_sync(root_node, context))
295 .collect(),
296 ),
297 }
298 }
299
300 pub async fn execute<'a, QueryT, MutationT, SubscriptionT>(
305 &'a self,
306 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
307 context: &'a QueryT::Context,
308 ) -> GraphQLBatchResponse<S>
309 where
310 QueryT: GraphQLTypeAsync<S>,
311 QueryT::TypeInfo: Sync,
312 QueryT::Context: Sync,
313 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
314 MutationT::TypeInfo: Sync,
315 SubscriptionT: GraphQLSubscriptionType<S, Context = QueryT::Context>,
316 SubscriptionT::TypeInfo: Sync,
317 S: Send + Sync,
318 {
319 match self {
320 Self::Single(req) => {
321 let resp = req.execute(root_node, context).await;
322 GraphQLBatchResponse::Single(resp)
323 }
324 Self::Batch(reqs) => {
325 let resps = futures::future::join_all(
326 reqs.iter().map(|req| req.execute(root_node, context)),
327 )
328 .await;
329 GraphQLBatchResponse::Batch(resps)
330 }
331 }
332 }
333
334 pub fn operation_names(&self) -> Vec<Option<&str>> {
336 match self {
337 Self::Single(req) => vec![req.operation_name.as_deref()],
338 Self::Batch(reqs) => reqs.iter().map(|r| r.operation_name.as_deref()).collect(),
339 }
340 }
341}
342
343#[derive(Serialize)]
349#[serde(untagged)]
350pub enum GraphQLBatchResponse<S = DefaultScalarValue>
351where
352 S: ScalarValue,
353{
354 Single(GraphQLResponse<S>),
356 Batch(Vec<GraphQLResponse<S>>),
358}
359
360impl<S: ScalarValue> GraphQLBatchResponse<S> {
361 pub fn is_ok(&self) -> bool {
364 match self {
365 Self::Single(resp) => resp.is_ok(),
366 Self::Batch(resps) => resps.iter().all(GraphQLResponse::is_ok),
367 }
368 }
369}
370
371#[cfg(feature = "expose-test-schema")]
372pub mod tests {
373 use std::time::Duration;
376
377 use serde_json::Value as Json;
378
379 #[derive(Debug)]
382 pub struct TestResponse {
383 pub status_code: i32,
385
386 pub body: Option<String>,
388
389 pub content_type: String,
391 }
392
393 pub trait HttpIntegration {
395 fn get(&self, url: &str) -> TestResponse;
398
399 fn post_json(&self, url: &str, body: &str) -> TestResponse;
402
403 fn post_graphql(&self, url: &str, body: &str) -> TestResponse;
406 }
407
408 pub fn run_http_test_suite<T: HttpIntegration>(integration: &T) {
410 println!("Running HTTP Test suite for integration");
411
412 println!(" - test_simple_get");
413 test_simple_get(integration);
414
415 println!(" - test_encoded_get");
416 test_encoded_get(integration);
417
418 println!(" - test_get_with_variables");
419 test_get_with_variables(integration);
420
421 println!(" - test_post_with_variables");
422 test_post_with_variables(integration);
423
424 println!(" - test_simple_post");
425 test_simple_post(integration);
426
427 println!(" - test_batched_post");
428 test_batched_post(integration);
429
430 println!(" - test_empty_batched_post");
431 test_empty_batched_post(integration);
432
433 println!(" - test_invalid_json");
434 test_invalid_json(integration);
435
436 println!(" - test_invalid_field");
437 test_invalid_field(integration);
438
439 println!(" - test_duplicate_keys");
440 test_duplicate_keys(integration);
441
442 println!(" - test_graphql_post");
443 test_graphql_post(integration);
444
445 println!(" - test_invalid_graphql_post");
446 test_invalid_graphql_post(integration);
447 }
448
449 fn unwrap_json_response(response: &TestResponse) -> Json {
450 serde_json::from_str::<Json>(
451 response
452 .body
453 .as_ref()
454 .expect("No data returned from request"),
455 )
456 .expect("Could not parse JSON object")
457 }
458
459 fn test_simple_get<T: HttpIntegration>(integration: &T) {
460 let response = integration.get("/?query=%7Bhero%7Bname%7D%7D");
462
463 assert_eq!(response.status_code, 200);
464 assert_eq!(response.content_type.as_str(), "application/json");
465
466 assert_eq!(
467 unwrap_json_response(&response),
468 serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
469 .expect("Invalid JSON constant in test")
470 );
471 }
472
473 fn test_encoded_get<T: HttpIntegration>(integration: &T) {
474 let response = integration.get(
476 "/?query=query%20%7B%20human(id%3A%20%221000%22)%20%7B%20id%2C%20name%2C%20appearsIn%2C%20homePlanet%20%7D%20%7D");
477
478 assert_eq!(response.status_code, 200);
479 assert_eq!(response.content_type.as_str(), "application/json");
480
481 assert_eq!(
482 unwrap_json_response(&response),
483 serde_json::from_str::<Json>(
484 r#"{
485 "data": {
486 "human": {
487 "appearsIn": [
488 "NEW_HOPE",
489 "EMPIRE",
490 "JEDI"
491 ],
492 "homePlanet": "Tatooine",
493 "name": "Luke Skywalker",
494 "id": "1000"
495 }
496 }
497 }"#
498 )
499 .expect("Invalid JSON constant in test")
500 );
501 }
502
503 fn test_get_with_variables<T: HttpIntegration>(integration: &T) {
504 let response = integration.get(
507 "/?query=query(%24id%3A%20String!)%20%7B%20human(id%3A%20%24id)%20%7B%20id%2C%20name%2C%20appearsIn%2C%20homePlanet%20%7D%20%7D&variables=%7B%20%22id%22%3A%20%221000%22%20%7D");
508
509 assert_eq!(response.status_code, 200);
510 assert_eq!(response.content_type, "application/json");
511
512 assert_eq!(
513 unwrap_json_response(&response),
514 serde_json::from_str::<Json>(
515 r#"{
516 "data": {
517 "human": {
518 "appearsIn": [
519 "NEW_HOPE",
520 "EMPIRE",
521 "JEDI"
522 ],
523 "homePlanet": "Tatooine",
524 "name": "Luke Skywalker",
525 "id": "1000"
526 }
527 }
528 }"#
529 )
530 .expect("Invalid JSON constant in test")
531 );
532 }
533
534 fn test_post_with_variables<T: HttpIntegration>(integration: &T) {
535 let response = integration.post_json(
536 "/",
537 r#"{
538 "query":
539 "query($id: String!) { human(id: $id) { id, name, appearsIn, homePlanet } }",
540 "variables": {"id": "1000"}
541 }"#,
542 );
543
544 assert_eq!(response.status_code, 200);
545 assert_eq!(response.content_type, "application/json");
546
547 assert_eq!(
548 unwrap_json_response(&response),
549 serde_json::from_str::<Json>(
550 r#"{
551 "data": {
552 "human": {
553 "appearsIn": [
554 "NEW_HOPE",
555 "EMPIRE",
556 "JEDI"
557 ],
558 "homePlanet": "Tatooine",
559 "name": "Luke Skywalker",
560 "id": "1000"
561 }
562 }
563 }"#
564 )
565 .expect("Invalid JSON constant in test")
566 );
567 }
568
569 fn test_simple_post<T: HttpIntegration>(integration: &T) {
570 let response = integration.post_json("/", r#"{"query": "{hero{name}}"}"#);
571
572 assert_eq!(response.status_code, 200);
573 assert_eq!(response.content_type, "application/json");
574
575 assert_eq!(
576 unwrap_json_response(&response),
577 serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
578 .expect("Invalid JSON constant in test"),
579 );
580 }
581
582 fn test_batched_post<T: HttpIntegration>(integration: &T) {
583 let response = integration.post_json(
584 "/",
585 r#"[{"query": "{hero{name}}"}, {"query": "{hero{name}}"}]"#,
586 );
587
588 assert_eq!(response.status_code, 200);
589 assert_eq!(response.content_type, "application/json");
590
591 assert_eq!(
592 unwrap_json_response(&response),
593 serde_json::from_str::<Json>(
594 r#"[{"data": {"hero": {"name": "R2-D2"}}}, {"data": {"hero": {"name": "R2-D2"}}}]"#,
595 )
596 .expect("Invalid JSON constant in test"),
597 );
598 }
599
600 fn test_empty_batched_post<T: HttpIntegration>(integration: &T) {
601 let response = integration.post_json("/", "[]");
602 assert_eq!(response.status_code, 400);
603 }
604
605 fn test_invalid_json<T: HttpIntegration>(integration: &T) {
606 let response = integration.get("/?query=blah");
607 assert_eq!(response.status_code, 400);
608 let response = integration.post_json("/", r#"blah"#);
609 assert_eq!(response.status_code, 400);
610 }
611
612 fn test_invalid_field<T: HttpIntegration>(integration: &T) {
613 let response = integration.get("/?query=%7Bhero%7Bblah%7D%7D");
615 assert_eq!(response.status_code, 400);
616 let response = integration.post_json("/", r#"{"query": "{hero{blah}}"}"#);
617 assert_eq!(response.status_code, 400);
618 }
619
620 fn test_duplicate_keys<T: HttpIntegration>(integration: &T) {
621 let response = integration.get("/?query=%7B%22query%22%3A%20%22%7Bhero%7Bname%7D%7D%22%2C%20%22query%22%3A%20%22%7Bhero%7Bname%7D%7D%22%7D");
623 assert_eq!(response.status_code, 400);
624 let response =
625 integration.post_json("/", r#"{"query": "{hero{name}}", "query": "{hero{name}}"}"#);
626 assert_eq!(response.status_code, 400);
627 }
628
629 fn test_graphql_post<T: HttpIntegration>(integration: &T) {
630 let resp = integration.post_graphql("/", r#"{hero{name}}"#);
631
632 assert_eq!(resp.status_code, 200);
633 assert_eq!(resp.content_type, "application/json");
634
635 assert_eq!(
636 unwrap_json_response(&resp),
637 serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
638 .expect("Invalid JSON constant in test"),
639 );
640 }
641
642 fn test_invalid_graphql_post<T: HttpIntegration>(integration: &T) {
643 let resp = integration.post_graphql("/", r#"{hero{name}"#);
644
645 assert_eq!(resp.status_code, 400);
646 }
647
648 pub trait WsIntegration {
650 fn run(
652 &self,
653 messages: Vec<WsIntegrationMessage>,
654 ) -> impl Future<Output = Result<(), anyhow::Error>>;
655 }
656
657 pub enum WsIntegrationMessage {
659 Send(Json),
661
662 Expect(Json, Duration),
664 }
665
666 pub const WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT: Duration = Duration::from_millis(100);
668
669 pub mod graphql_ws {
673 use serde_json::json;
674
675 use super::{WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT, WsIntegration, WsIntegrationMessage};
676
677 pub async fn run_test_suite<T: WsIntegration>(integration: &T) {
682 println!("Running `graphql-ws` test suite for integration");
683
684 println!(" - graphql_ws::test_simple_subscription");
685 test_simple_subscription(integration).await;
686
687 println!(" - graphql_ws::test_invalid_json");
688 test_invalid_json(integration).await;
689
690 println!(" - graphql_ws::test_invalid_query");
691 test_invalid_query(integration).await;
692 }
693
694 async fn test_simple_subscription<T: WsIntegration>(integration: &T) {
695 let messages = vec![
696 WsIntegrationMessage::Send(json!({
697 "type": "connection_init",
698 "payload": {},
699 })),
700 WsIntegrationMessage::Expect(
701 json!({"type": "connection_ack"}),
702 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
703 ),
704 WsIntegrationMessage::Expect(
705 json!({"type": "ka"}),
706 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
707 ),
708 WsIntegrationMessage::Send(json!({
709 "id": "1",
710 "type": "start",
711 "payload": {
712 "variables": {},
713 "extensions": {},
714 "operationName": null,
715 "query": "subscription { asyncHuman { id, name, homePlanet } }",
716 },
717 })),
718 WsIntegrationMessage::Expect(
719 json!({
720 "type": "data",
721 "id": "1",
722 "payload": {
723 "data": {
724 "asyncHuman": {
725 "id": "1000",
726 "name": "Luke Skywalker",
727 "homePlanet": "Tatooine",
728 },
729 },
730 },
731 }),
732 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
733 ),
734 ];
735
736 integration.run(messages).await.unwrap();
737 }
738
739 async fn test_invalid_json<T: WsIntegration>(integration: &T) {
740 let messages = vec![
741 WsIntegrationMessage::Send(json!({"whatever": "invalid value"})),
742 WsIntegrationMessage::Expect(
743 json!({
744 "type": "connection_error",
745 "payload": {
746 "message": "`serde` error: missing field `type` at line 1 column 28",
747 },
748 }),
749 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
750 ),
751 ];
752
753 integration.run(messages).await.unwrap();
754 }
755
756 async fn test_invalid_query<T: WsIntegration>(integration: &T) {
757 let messages = vec![
758 WsIntegrationMessage::Send(json!({
759 "type": "connection_init",
760 "payload": {},
761 })),
762 WsIntegrationMessage::Expect(
763 json!({"type": "connection_ack"}),
764 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
765 ),
766 WsIntegrationMessage::Expect(
767 json!({"type": "ka"}),
768 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
769 ),
770 WsIntegrationMessage::Send(json!({
771 "id": "1",
772 "type": "start",
773 "payload": {
774 "variables": {},
775 "extensions": {},
776 "operationName": null,
777 "query": "subscription { asyncHuman }",
778 },
779 })),
780 WsIntegrationMessage::Expect(
781 json!({
782 "type": "error",
783 "id": "1",
784 "payload": [{
785 "message": "Field \"asyncHuman\" of type \"Human!\" must have a selection \
786 of subfields. Did you mean \"asyncHuman { ... }\"?",
787 "locations": [{
788 "line": 1,
789 "column": 16,
790 }],
791 }],
792 }),
793 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
794 ),
795 ];
796
797 integration.run(messages).await.unwrap();
798 }
799 }
800
801 pub mod graphql_transport_ws {
805 use serde_json::json;
806
807 use super::{WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT, WsIntegration, WsIntegrationMessage};
808
809 pub async fn run_test_suite<T: WsIntegration>(integration: &T) {
814 println!("Running `graphql-transport-ws` test suite for integration");
815
816 println!(" - graphql_ws::test_simple_subscription");
817 test_simple_subscription(integration).await;
818
819 println!(" - graphql_ws::test_invalid_json");
820 test_invalid_json(integration).await;
821
822 println!(" - graphql_ws::test_invalid_query");
823 test_invalid_query(integration).await;
824 }
825
826 async fn test_simple_subscription<T: WsIntegration>(integration: &T) {
827 let messages = vec![
828 WsIntegrationMessage::Send(json!({
829 "type": "connection_init",
830 "payload": {},
831 })),
832 WsIntegrationMessage::Expect(
833 json!({"type": "connection_ack"}),
834 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
835 ),
836 WsIntegrationMessage::Expect(
837 json!({"type": "pong"}),
838 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
839 ),
840 WsIntegrationMessage::Send(json!({"type": "ping"})),
841 WsIntegrationMessage::Expect(
842 json!({"type": "pong"}),
843 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
844 ),
845 WsIntegrationMessage::Send(json!({
846 "id": "1",
847 "type": "subscribe",
848 "payload": {
849 "variables": {},
850 "extensions": {},
851 "operationName": null,
852 "query": "subscription { asyncHuman { id, name, homePlanet } }",
853 },
854 })),
855 WsIntegrationMessage::Expect(
856 json!({
857 "id": "1",
858 "type": "next",
859 "payload": {
860 "data": {
861 "asyncHuman": {
862 "id": "1000",
863 "name": "Luke Skywalker",
864 "homePlanet": "Tatooine",
865 },
866 },
867 },
868 }),
869 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
870 ),
871 ];
872
873 integration.run(messages).await.unwrap();
874 }
875
876 async fn test_invalid_json<T: WsIntegration>(integration: &T) {
877 let messages = vec![
878 WsIntegrationMessage::Send(json!({"whatever": "invalid value"})),
879 WsIntegrationMessage::Expect(
880 json!({
881 "code": 4400,
882 "description": "`serde` error: missing field `type` at line 1 column 28",
883 }),
884 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
885 ),
886 ];
887
888 integration.run(messages).await.unwrap();
889 }
890
891 async fn test_invalid_query<T: WsIntegration>(integration: &T) {
892 let messages = vec![
893 WsIntegrationMessage::Send(json!({
894 "type": "connection_init",
895 "payload": {},
896 })),
897 WsIntegrationMessage::Expect(
898 json!({"type": "connection_ack"}),
899 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
900 ),
901 WsIntegrationMessage::Expect(
902 json!({"type": "pong"}),
903 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
904 ),
905 WsIntegrationMessage::Send(json!({"type": "ping"})),
906 WsIntegrationMessage::Expect(
907 json!({"type": "pong"}),
908 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
909 ),
910 WsIntegrationMessage::Send(json!({
911 "id": "1",
912 "type": "subscribe",
913 "payload": {
914 "variables": {},
915 "extensions": {},
916 "operationName": null,
917 "query": "subscription { asyncHuman }",
918 },
919 })),
920 WsIntegrationMessage::Expect(
921 json!({
922 "type": "error",
923 "id": "1",
924 "payload": [{
925 "message": "Field \"asyncHuman\" of type \"Human!\" must have a selection \
926 of subfields. Did you mean \"asyncHuman { ... }\"?",
927 "locations": [{
928 "line": 1,
929 "column": 16,
930 }],
931 }],
932 }),
933 WS_INTEGRATION_EXPECT_DEFAULT_TIMEOUT,
934 ),
935 ];
936
937 integration.run(messages).await.unwrap();
938 }
939 }
940}