1use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Instant;
8
9use super::{
10 GraphQLConfig, GraphQLSchema, SqlGenerator, QueryValidator,
11 GraphQLMetrics, ExecutionContext, ErrorCode, OperationType,
12 QueryPlan, Selection, Filter,
13};
14use super::sql_generator::FilterOperator;
15
16#[derive(Debug, Clone)]
18pub struct GraphQLRequest {
19 pub query: String,
21 pub operation_name: Option<String>,
23 pub variables: Option<HashMap<String, serde_json::Value>>,
25 pub extensions: Option<HashMap<String, serde_json::Value>>,
27}
28
29impl GraphQLRequest {
30 pub fn new(query: impl Into<String>) -> Self {
32 Self {
33 query: query.into(),
34 operation_name: None,
35 variables: None,
36 extensions: None,
37 }
38 }
39
40 pub fn with_operation(mut self, name: impl Into<String>) -> Self {
42 self.operation_name = Some(name.into());
43 self
44 }
45
46 pub fn with_variables(mut self, vars: HashMap<String, serde_json::Value>) -> Self {
48 self.variables = Some(vars);
49 self
50 }
51
52 pub fn var(mut self, name: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
54 let vars = self.variables.get_or_insert_with(HashMap::new);
55 vars.insert(name.into(), value.into());
56 self
57 }
58}
59
60#[derive(Debug, Clone)]
62pub struct GraphQLResponse {
63 pub data: Option<serde_json::Value>,
65 pub errors: Option<Vec<GraphQLError>>,
67 pub extensions: Option<HashMap<String, serde_json::Value>>,
69}
70
71impl GraphQLResponse {
72 pub fn success(data: serde_json::Value) -> Self {
74 Self {
75 data: Some(data),
76 errors: None,
77 extensions: None,
78 }
79 }
80
81 pub fn error(error: GraphQLError) -> Self {
83 Self {
84 data: None,
85 errors: Some(vec![error]),
86 extensions: None,
87 }
88 }
89
90 pub fn errors(errors: Vec<GraphQLError>) -> Self {
92 Self {
93 data: None,
94 errors: Some(errors),
95 extensions: None,
96 }
97 }
98
99 pub fn with_extension(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
101 let extensions = self.extensions.get_or_insert_with(HashMap::new);
102 extensions.insert(key.into(), value);
103 self
104 }
105
106 pub fn has_errors(&self) -> bool {
108 self.errors.as_ref().map(|e| !e.is_empty()).unwrap_or(false)
109 }
110
111 pub fn to_json(&self) -> serde_json::Value {
113 let mut result = serde_json::Map::new();
114
115 if let Some(ref data) = self.data {
116 result.insert("data".to_string(), data.clone());
117 }
118
119 if let Some(ref errors) = self.errors {
120 let error_array: Vec<_> = errors.iter().map(|e| e.to_json()).collect();
121 result.insert("errors".to_string(), serde_json::Value::Array(error_array));
122 }
123
124 if let Some(ref extensions) = self.extensions {
125 result.insert(
126 "extensions".to_string(),
127 serde_json::Value::Object(
128 extensions.iter()
129 .map(|(k, v)| (k.clone(), v.clone()))
130 .collect(),
131 ),
132 );
133 }
134
135 serde_json::Value::Object(result)
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct GraphQLError {
142 pub message: String,
144 pub locations: Option<Vec<ErrorLocation>>,
146 pub path: Option<Vec<PathSegment>>,
148 pub extensions: Option<HashMap<String, serde_json::Value>>,
150 pub code: ErrorCode,
152}
153
154impl GraphQLError {
155 pub fn new(message: impl Into<String>, code: ErrorCode) -> Self {
157 Self {
158 message: message.into(),
159 locations: None,
160 path: None,
161 extensions: None,
162 code,
163 }
164 }
165
166 pub fn parse_error(message: impl Into<String>) -> Self {
168 Self::new(message, ErrorCode::ParseError)
169 }
170
171 pub fn validation_error(message: impl Into<String>) -> Self {
173 Self::new(message, ErrorCode::ValidationError)
174 }
175
176 pub fn unauthorized(message: impl Into<String>) -> Self {
178 Self::new(message, ErrorCode::Unauthorized)
179 }
180
181 pub fn not_found(message: impl Into<String>) -> Self {
183 Self::new(message, ErrorCode::NotFound)
184 }
185
186 pub fn internal(message: impl Into<String>) -> Self {
188 Self::new(message, ErrorCode::InternalError)
189 }
190
191 pub fn with_location(mut self, line: u32, column: u32) -> Self {
193 self.locations = Some(vec![ErrorLocation { line, column }]);
194 self
195 }
196
197 pub fn with_path(mut self, path: Vec<PathSegment>) -> Self {
199 self.path = Some(path);
200 self
201 }
202
203 pub fn with_extension(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
205 let extensions = self.extensions.get_or_insert_with(HashMap::new);
206 extensions.insert(key.into(), value);
207 self
208 }
209
210 pub fn to_json(&self) -> serde_json::Value {
212 let mut result = serde_json::Map::new();
213 result.insert("message".to_string(), serde_json::Value::String(self.message.clone()));
214
215 if let Some(ref locations) = self.locations {
216 let loc_array: Vec<_> = locations.iter().map(|l| {
217 let mut loc = serde_json::Map::new();
218 loc.insert("line".to_string(), serde_json::Value::Number(l.line.into()));
219 loc.insert("column".to_string(), serde_json::Value::Number(l.column.into()));
220 serde_json::Value::Object(loc)
221 }).collect();
222 result.insert("locations".to_string(), serde_json::Value::Array(loc_array));
223 }
224
225 if let Some(ref path) = self.path {
226 let path_array: Vec<_> = path.iter().map(|s| s.to_json()).collect();
227 result.insert("path".to_string(), serde_json::Value::Array(path_array));
228 }
229
230 let mut extensions = self.extensions.clone().unwrap_or_default();
231 extensions.insert(
232 "code".to_string(),
233 serde_json::Value::String(format!("{:?}", self.code)),
234 );
235 result.insert(
236 "extensions".to_string(),
237 serde_json::Value::Object(extensions.into_iter().collect()),
238 );
239
240 serde_json::Value::Object(result)
241 }
242}
243
244impl std::fmt::Display for GraphQLError {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 write!(f, "{}", self.message)
247 }
248}
249
250impl std::error::Error for GraphQLError {}
251
252#[derive(Debug, Clone, Copy)]
254pub struct ErrorLocation {
255 pub line: u32,
257 pub column: u32,
259}
260
261#[derive(Debug, Clone)]
263pub enum PathSegment {
264 Field(String),
266 Index(usize),
268}
269
270impl PathSegment {
271 pub fn to_json(&self) -> serde_json::Value {
273 match self {
274 PathSegment::Field(name) => serde_json::Value::String(name.clone()),
275 PathSegment::Index(idx) => serde_json::Value::Number((*idx).into()),
276 }
277 }
278}
279
280#[derive(Debug, Clone)]
282pub struct ParsedDocument {
283 pub operation_type: OperationType,
285 pub operation_name: Option<String>,
287 pub selections: Vec<ParsedSelection>,
289 pub variable_definitions: Vec<VariableDefinition>,
291 pub fragments: HashMap<String, FragmentDefinition>,
293}
294
295#[derive(Debug, Clone)]
297pub struct ParsedSelection {
298 pub name: String,
300 pub alias: Option<String>,
302 pub arguments: HashMap<String, serde_json::Value>,
304 pub selections: Vec<ParsedSelection>,
306 pub directives: Vec<Directive>,
308}
309
310impl ParsedSelection {
311 pub fn response_key(&self) -> &str {
313 self.alias.as_deref().unwrap_or(&self.name)
314 }
315}
316
317#[derive(Debug, Clone)]
319pub struct VariableDefinition {
320 pub name: String,
322 pub var_type: String,
324 pub default_value: Option<serde_json::Value>,
326}
327
328#[derive(Debug, Clone)]
330pub struct FragmentDefinition {
331 pub name: String,
333 pub type_condition: String,
335 pub selections: Vec<ParsedSelection>,
337}
338
339#[derive(Debug, Clone)]
341pub struct Directive {
342 pub name: String,
344 pub arguments: HashMap<String, serde_json::Value>,
346}
347
348#[derive(Debug)]
352pub struct GraphQLEngine {
353 config: Arc<GraphQLConfig>,
355 schema: Arc<GraphQLSchema>,
357 sql_generator: Arc<SqlGenerator>,
359 validator: QueryValidator,
361 metrics: Arc<GraphQLMetrics>,
363}
364
365impl GraphQLEngine {
366 pub fn new(config: GraphQLConfig, schema: GraphQLSchema) -> Self {
368 let config = Arc::new(config);
369 let schema = Arc::new(schema);
370
371 Self {
372 sql_generator: Arc::new(SqlGenerator::new(schema.clone())),
373 validator: QueryValidator::new(config.clone()),
374 metrics: Arc::new(GraphQLMetrics::new()),
375 config,
376 schema,
377 }
378 }
379
380 pub async fn execute(&self, request: GraphQLRequest) -> GraphQLResponse {
382 self.execute_with_context(request, ExecutionContext::default()).await
383 }
384
385 pub async fn execute_with_context(
387 &self,
388 request: GraphQLRequest,
389 context: ExecutionContext,
390 ) -> GraphQLResponse {
391 let start = Instant::now();
392
393 let document = match self.parse(&request.query) {
395 Ok(doc) => doc,
396 Err(e) => {
397 self.metrics.record_error(&e);
398 return GraphQLResponse::error(e);
399 }
400 };
401
402 if let Err(e) = self.validate(&document) {
404 self.metrics.record_error(&e);
405 return GraphQLResponse::error(e);
406 }
407
408 if let Err(e) = self.authorize(&document, &context) {
410 self.metrics.record_error(&e);
411 return GraphQLResponse::error(e);
412 }
413
414 let plan = match self.plan(&document, &request.variables) {
416 Ok(p) => p,
417 Err(e) => {
418 self.metrics.record_error(&e);
419 return GraphQLResponse::error(e);
420 }
421 };
422
423 let sql_queries = match self.sql_generator.generate(&plan) {
425 Ok(queries) => queries,
426 Err(e) => {
427 let error = GraphQLError::internal(format!("SQL generation failed: {}", e));
428 self.metrics.record_error(&error);
429 return GraphQLResponse::error(error);
430 }
431 };
432
433 let results = match self.execute_queries(&sql_queries, &context).await {
435 Ok(r) => r,
436 Err(e) => {
437 self.metrics.record_error(&e);
438 return GraphQLResponse::error(e);
439 }
440 };
441
442 let data = match self.shape_response(&document, &results) {
444 Ok(d) => d,
445 Err(e) => {
446 self.metrics.record_error(&e);
447 return GraphQLResponse::error(e);
448 }
449 };
450
451 let elapsed = start.elapsed();
452 self.metrics.record_query(elapsed, document.operation_type);
453
454 GraphQLResponse::success(data)
455 .with_extension("timing", serde_json::json!({
456 "durationMs": elapsed.as_millis()
457 }))
458 }
459
460 fn parse(&self, query: &str) -> Result<ParsedDocument, GraphQLError> {
462 let query = query.trim();
465
466 let (operation_type, remaining) = if query.starts_with("mutation") {
468 (OperationType::Mutation, query.strip_prefix("mutation").unwrap_or(query))
469 } else if query.starts_with("subscription") {
470 (OperationType::Subscription, query.strip_prefix("subscription").unwrap_or(query))
471 } else if query.starts_with("query") {
472 (OperationType::Query, query.strip_prefix("query").unwrap_or(query))
473 } else if query.starts_with("{") {
474 (OperationType::Query, query)
475 } else {
476 return Err(GraphQLError::parse_error("Invalid query format"));
477 };
478
479 let remaining = remaining.trim();
481 let (operation_name, remaining) = if remaining.starts_with('{') {
482 (None, remaining)
483 } else if let Some(brace_pos) = remaining.find('{') {
484 let name_part = remaining[..brace_pos].trim();
485 let name = name_part.split('(').next().unwrap_or(name_part).trim();
487 if name.is_empty() {
488 (None, &remaining[brace_pos..])
489 } else {
490 (Some(name.to_string()), &remaining[brace_pos..])
491 }
492 } else {
493 return Err(GraphQLError::parse_error("Missing selection set"));
494 };
495
496 let selections = self.parse_selection_set(remaining)?;
498
499 Ok(ParsedDocument {
500 operation_type,
501 operation_name,
502 selections,
503 variable_definitions: Vec::new(),
504 fragments: HashMap::new(),
505 })
506 }
507
508 fn parse_selection_set(&self, input: &str) -> Result<Vec<ParsedSelection>, GraphQLError> {
510 let input = input.trim();
511
512 if !input.starts_with('{') {
513 return Err(GraphQLError::parse_error("Expected '{'"));
514 }
515
516 let mut depth = 0;
518 let mut end_pos = 0;
519 for (i, c) in input.chars().enumerate() {
520 match c {
521 '{' => depth += 1,
522 '}' => {
523 depth -= 1;
524 if depth == 0 {
525 end_pos = i;
526 break;
527 }
528 }
529 _ => {}
530 }
531 }
532
533 if depth != 0 {
534 return Err(GraphQLError::parse_error("Unmatched braces"));
535 }
536
537 let inner = &input[1..end_pos].trim();
538 self.parse_fields(inner)
539 }
540
541 fn parse_fields(&self, input: &str) -> Result<Vec<ParsedSelection>, GraphQLError> {
543 let mut selections = Vec::new();
544 let mut current_pos = 0;
545 let chars: Vec<char> = input.chars().collect();
546
547 while current_pos < chars.len() {
548 while current_pos < chars.len() && chars[current_pos].is_whitespace() {
550 current_pos += 1;
551 }
552
553 if current_pos >= chars.len() {
554 break;
555 }
556
557 let field_start = current_pos;
559 while current_pos < chars.len()
560 && (chars[current_pos].is_alphanumeric() || chars[current_pos] == '_')
561 {
562 current_pos += 1;
563 }
564
565 if current_pos == field_start {
566 current_pos += 1;
567 continue;
568 }
569
570 let mut field_name: String = chars[field_start..current_pos].iter().collect();
571 let mut alias = None;
572
573 while current_pos < chars.len() && chars[current_pos].is_whitespace() {
575 current_pos += 1;
576 }
577
578 if current_pos < chars.len() && chars[current_pos] == ':' {
579 alias = Some(field_name);
580 current_pos += 1;
581
582 while current_pos < chars.len() && chars[current_pos].is_whitespace() {
584 current_pos += 1;
585 }
586
587 let name_start = current_pos;
589 while current_pos < chars.len()
590 && (chars[current_pos].is_alphanumeric() || chars[current_pos] == '_')
591 {
592 current_pos += 1;
593 }
594 field_name = chars[name_start..current_pos].iter().collect();
595 }
596
597 while current_pos < chars.len() && chars[current_pos].is_whitespace() {
599 current_pos += 1;
600 }
601
602 let mut arguments = HashMap::new();
604 if current_pos < chars.len() && chars[current_pos] == '(' {
605 let args_start = current_pos;
606 let mut depth = 1;
607 current_pos += 1;
608
609 while current_pos < chars.len() && depth > 0 {
610 match chars[current_pos] {
611 '(' => depth += 1,
612 ')' => depth -= 1,
613 _ => {}
614 }
615 current_pos += 1;
616 }
617
618 let args_str: String = chars[args_start + 1..current_pos - 1].iter().collect();
619 arguments = self.parse_arguments(&args_str)?;
620 }
621
622 while current_pos < chars.len() && chars[current_pos].is_whitespace() {
624 current_pos += 1;
625 }
626
627 let nested_selections = if current_pos < chars.len() && chars[current_pos] == '{' {
629 let nested_start = current_pos;
630 let mut depth = 1;
631 current_pos += 1;
632
633 while current_pos < chars.len() && depth > 0 {
634 match chars[current_pos] {
635 '{' => depth += 1,
636 '}' => depth -= 1,
637 _ => {}
638 }
639 current_pos += 1;
640 }
641
642 let nested_str: String = chars[nested_start..current_pos].iter().collect();
643 self.parse_selection_set(&nested_str)?
644 } else {
645 Vec::new()
646 };
647
648 selections.push(ParsedSelection {
649 name: field_name,
650 alias,
651 arguments,
652 selections: nested_selections,
653 directives: Vec::new(),
654 });
655 }
656
657 Ok(selections)
658 }
659
660 fn parse_arguments(&self, input: &str) -> Result<HashMap<String, serde_json::Value>, GraphQLError> {
662 let mut arguments = HashMap::new();
663
664 for part in input.split(',') {
665 let part = part.trim();
666 if part.is_empty() {
667 continue;
668 }
669
670 if let Some(colon_pos) = part.find(':') {
671 let key = part[..colon_pos].trim().to_string();
672 let value_str = part[colon_pos + 1..].trim();
673
674 let value = self.parse_value(value_str)?;
675 arguments.insert(key, value);
676 }
677 }
678
679 Ok(arguments)
680 }
681
682 fn parse_value(&self, input: &str) -> Result<serde_json::Value, GraphQLError> {
684 let input = input.trim();
685
686 if input == "null" {
687 Ok(serde_json::Value::Null)
688 } else if input == "true" {
689 Ok(serde_json::Value::Bool(true))
690 } else if input == "false" {
691 Ok(serde_json::Value::Bool(false))
692 } else if input.starts_with('"') && input.ends_with('"') {
693 Ok(serde_json::Value::String(input[1..input.len() - 1].to_string()))
694 } else if let Ok(n) = input.parse::<i64>() {
695 Ok(serde_json::Value::Number(n.into()))
696 } else if let Ok(n) = input.parse::<f64>() {
697 Ok(serde_json::json!(n))
698 } else {
699 Ok(serde_json::Value::String(input.to_string()))
701 }
702 }
703
704 fn validate(&self, document: &ParsedDocument) -> Result<(), GraphQLError> {
706 self.validator.validate(document, &self.schema)
707 }
708
709 fn authorize(&self, _document: &ParsedDocument, _context: &ExecutionContext) -> Result<(), GraphQLError> {
711 Ok(())
714 }
715
716 fn plan(
718 &self,
719 document: &ParsedDocument,
720 _variables: &Option<HashMap<String, serde_json::Value>>,
721 ) -> Result<QueryPlan, GraphQLError> {
722 let selections: Vec<_> = document.selections.iter()
724 .map(|s| self.selection_to_plan(s))
725 .collect();
726
727 Ok(QueryPlan::Multiple { plans: selections })
728 }
729
730 fn selection_to_plan(&self, selection: &ParsedSelection) -> QueryPlan {
732 let filters = self.extract_filters(&selection.arguments);
734
735 let limit = selection.arguments.get("limit")
737 .and_then(|v| v.as_u64())
738 .map(|v| v as u32);
739 let offset = selection.arguments.get("offset")
740 .and_then(|v| v.as_u64())
741 .map(|v| v as u32);
742
743 let sel = Selection {
745 table_name: super::to_snake_case(&selection.name),
746 fields: selection.selections.iter()
747 .filter(|s| s.selections.is_empty())
748 .map(|s| s.name.clone())
749 .collect(),
750 relationships: selection.selections.iter()
751 .filter(|s| !s.selections.is_empty())
752 .map(|s| (s.name.clone(), self.selection_to_plan(s)))
753 .collect(),
754 };
755
756 QueryPlan::Single {
757 selection: sel,
758 filters,
759 limit,
760 offset,
761 }
762 }
763
764 fn extract_filters(&self, arguments: &HashMap<String, serde_json::Value>) -> Vec<Filter> {
766 let mut filters = Vec::new();
767
768 if let Some(id) = arguments.get("id") {
770 filters.push(Filter {
771 field: "id".to_string(),
772 operator: FilterOperator::Eq,
773 value: id.clone(),
774 });
775 }
776
777 if let Some(where_obj) = arguments.get("where") {
779 if let Some(obj) = where_obj.as_object() {
780 for (field, condition) in obj {
781 if let Some(cond_obj) = condition.as_object() {
782 for (op, value) in cond_obj {
783 let operator = match op.as_str() {
784 "eq" => FilterOperator::Eq,
785 "ne" => FilterOperator::Ne,
786 "gt" => FilterOperator::Gt,
787 "gte" => FilterOperator::Gte,
788 "lt" => FilterOperator::Lt,
789 "lte" => FilterOperator::Lte,
790 "contains" => FilterOperator::Contains,
791 "startsWith" => FilterOperator::StartsWith,
792 "endsWith" => FilterOperator::EndsWith,
793 "in" => FilterOperator::In,
794 _ => continue,
795 };
796
797 filters.push(Filter {
798 field: field.clone(),
799 operator,
800 value: value.clone(),
801 });
802 }
803 }
804 }
805 }
806 }
807
808 filters
809 }
810
811 async fn execute_queries(
813 &self,
814 queries: &[super::SqlQuery],
815 _context: &ExecutionContext,
816 ) -> Result<Vec<Vec<serde_json::Value>>, GraphQLError> {
817 Ok(queries.iter().map(|_| Vec::new()).collect())
820 }
821
822 fn shape_response(
824 &self,
825 document: &ParsedDocument,
826 _results: &[Vec<serde_json::Value>],
827 ) -> Result<serde_json::Value, GraphQLError> {
828 let mut data = serde_json::Map::new();
830
831 for selection in &document.selections {
832 let key = selection.response_key().to_string();
833 data.insert(key, serde_json::Value::Null);
836 }
837
838 Ok(serde_json::Value::Object(data))
839 }
840
841 pub fn schema(&self) -> &GraphQLSchema {
843 &self.schema
844 }
845
846 pub fn config(&self) -> &GraphQLConfig {
848 &self.config
849 }
850
851 pub fn metrics(&self) -> &GraphQLMetrics {
853 &self.metrics
854 }
855
856 pub fn generate_sdl(&self) -> String {
858 self.schema.to_sdl()
859 }
860}
861
862impl Clone for GraphQLEngine {
863 fn clone(&self) -> Self {
864 Self {
865 config: self.config.clone(),
866 schema: self.schema.clone(),
867 sql_generator: self.sql_generator.clone(),
868 validator: QueryValidator::new(self.config.clone()),
869 metrics: self.metrics.clone(),
870 }
871 }
872}
873
874#[cfg(test)]
875mod tests {
876 use super::*;
877 use crate::graphql::introspector::GraphQLSchema;
878
879 fn create_test_engine() -> GraphQLEngine {
880 let config = GraphQLConfig::default();
881 let schema = GraphQLSchema::new();
882 GraphQLEngine::new(config, schema)
883 }
884
885 #[test]
886 fn test_parse_simple_query() {
887 let engine = create_test_engine();
888 let query = "query { users { id name } }";
889
890 let doc = engine.parse(query).unwrap();
891 assert_eq!(doc.operation_type, OperationType::Query);
892 assert_eq!(doc.selections.len(), 1);
893 assert_eq!(doc.selections[0].name, "users");
894 assert_eq!(doc.selections[0].selections.len(), 2);
895 }
896
897 #[test]
898 fn test_parse_named_query() {
899 let engine = create_test_engine();
900 let query = "query GetUsers { users { id } }";
901
902 let doc = engine.parse(query).unwrap();
903 assert_eq!(doc.operation_name, Some("GetUsers".to_string()));
904 }
905
906 #[test]
907 fn test_parse_mutation() {
908 let engine = create_test_engine();
909 let query = "mutation { createUser(name: \"test\") { id } }";
910
911 let doc = engine.parse(query).unwrap();
912 assert_eq!(doc.operation_type, OperationType::Mutation);
913 }
914
915 #[test]
916 fn test_parse_with_arguments() {
917 let engine = create_test_engine();
918 let query = "{ user(id: \"123\") { name } }";
919
920 let doc = engine.parse(query).unwrap();
921 let user_selection = &doc.selections[0];
922 assert_eq!(user_selection.name, "user");
923 assert!(user_selection.arguments.contains_key("id"));
924 }
925
926 #[test]
927 fn test_parse_with_alias() {
928 let engine = create_test_engine();
929 let query = "{ myUser: user(id: \"123\") { name } }";
930
931 let doc = engine.parse(query).unwrap();
932 let selection = &doc.selections[0];
933 assert_eq!(selection.alias, Some("myUser".to_string()));
934 assert_eq!(selection.name, "user");
935 assert_eq!(selection.response_key(), "myUser");
936 }
937
938 #[test]
939 fn test_graphql_request_builder() {
940 let request = GraphQLRequest::new("{ users { id } }")
941 .with_operation("GetUsers")
942 .var("limit", 10);
943
944 assert_eq!(request.query, "{ users { id } }");
945 assert_eq!(request.operation_name, Some("GetUsers".to_string()));
946 assert!(request.variables.unwrap().contains_key("limit"));
947 }
948
949 #[test]
950 fn test_graphql_response_success() {
951 let response = GraphQLResponse::success(serde_json::json!({"users": []}));
952
953 assert!(response.data.is_some());
954 assert!(!response.has_errors());
955 }
956
957 #[test]
958 fn test_graphql_response_error() {
959 let error = GraphQLError::parse_error("Syntax error");
960 let response = GraphQLResponse::error(error);
961
962 assert!(response.data.is_none());
963 assert!(response.has_errors());
964 }
965
966 #[test]
967 fn test_graphql_error_to_json() {
968 let error = GraphQLError::validation_error("Field not found")
969 .with_location(1, 10)
970 .with_path(vec![PathSegment::Field("users".to_string())]);
971
972 let json = error.to_json();
973 assert_eq!(json["message"], "Field not found");
974 assert!(json["locations"].is_array());
975 assert!(json["path"].is_array());
976 }
977
978 #[tokio::test]
979 async fn test_execute_simple_query() {
980 let engine = create_test_engine();
981 let request = GraphQLRequest::new("{ users { id name } }");
982
983 let response = engine.execute(request).await;
984
985 assert!(!response.has_errors());
987 assert!(response.data.is_some());
988 }
989
990 #[test]
991 fn test_parse_nested_selections() {
992 let engine = create_test_engine();
993 let query = "{ users { id posts { title comments { content } } } }";
994
995 let doc = engine.parse(query).unwrap();
996 let users = &doc.selections[0];
997 assert_eq!(users.selections.len(), 2); let posts = &users.selections[1];
1000 assert_eq!(posts.name, "posts");
1001 assert_eq!(posts.selections.len(), 2); }
1003
1004 #[test]
1005 fn test_parse_value() {
1006 let engine = create_test_engine();
1007
1008 assert_eq!(engine.parse_value("null").unwrap(), serde_json::Value::Null);
1009 assert_eq!(engine.parse_value("true").unwrap(), serde_json::Value::Bool(true));
1010 assert_eq!(engine.parse_value("false").unwrap(), serde_json::Value::Bool(false));
1011 assert_eq!(engine.parse_value("\"hello\"").unwrap(), serde_json::Value::String("hello".to_string()));
1012 assert_eq!(engine.parse_value("42").unwrap(), serde_json::json!(42));
1013 }
1014}