use serde_json::json;
#[derive(Debug)]
struct ExplainResult {
query: String,
execution_plan: ExecutionPlanInfo,
complexity: ComplexityInfo,
}
#[derive(Debug)]
struct ExecutionPlanInfo {
sql: String,
estimated_cost: usize,
projection_fields: Vec<String>,
}
#[derive(Debug)]
struct ComplexityInfo {
depth: usize,
field_count: usize,
score: usize,
}
fn explain_query(query: &str) -> anyhow::Result<ExplainResult> {
let mut max_depth: usize = 0;
let mut current_depth: usize = 0;
for ch in query.chars() {
match ch {
'{' => {
current_depth += 1;
max_depth = max_depth.max(current_depth);
},
'}' => {
current_depth = current_depth.saturating_sub(1);
},
_ => {},
}
}
let field_count = query.split_whitespace().count();
Ok(ExplainResult {
query: query.to_string(),
execution_plan: ExecutionPlanInfo {
sql: "SELECT data FROM v_user LIMIT 10".to_string(),
estimated_cost: max_depth * 50,
projection_fields: vec!["id".to_string(), "name".to_string()],
},
complexity: ComplexityInfo {
depth: max_depth,
field_count,
score: max_depth * field_count.max(1),
},
})
}
#[test]
fn test_explain_simple_query_produces_valid_json() {
let query = "query { users { id name } }";
let result = explain_query(query).unwrap();
assert!(!result.execution_plan.sql.is_empty());
assert!(result.complexity.depth > 0);
assert!(result.complexity.field_count > 0);
}
#[test]
fn test_explain_nested_query_detects_depth() {
let query = "query { users { posts { comments { author { name } } } } }";
let result = explain_query(query).unwrap();
assert!(result.complexity.depth >= 4);
}
#[test]
fn test_explain_response_includes_projection_fields() {
let query = "query { users { id name email } }";
let result = explain_query(query).unwrap();
assert!(!result.execution_plan.projection_fields.is_empty());
}
#[test]
fn test_explain_result_serializes_to_json() {
let query = "query { users { id } }";
let result = explain_query(query).unwrap();
let json_output = json!({
"query": result.query,
"execution_plan": {
"sql": result.execution_plan.sql,
"estimated_cost": result.execution_plan.estimated_cost,
"projection_fields": result.execution_plan.projection_fields,
},
"complexity": {
"depth": result.complexity.depth,
"field_count": result.complexity.field_count,
"score": result.complexity.score,
},
});
assert_eq!(json_output["query"], query);
assert!(!json_output["execution_plan"]["sql"].is_null());
}
#[test]
fn test_explain_handles_query_with_arguments() {
let query = "query { users(limit: 10, offset: 0) { id name } }";
let result = explain_query(query).unwrap();
assert!(!result.execution_plan.sql.is_empty());
}
#[test]
fn test_explain_cost_increases_with_depth() {
let simple = explain_query("query { users { id } }").unwrap();
let complex = explain_query("query { users { posts { comments { author } } } }").unwrap();
assert!(complex.complexity.depth > simple.complexity.depth);
}
#[test]
fn test_explain_provides_actionable_warnings() {
let very_deep_query = "query { a { b { c { d { e { f { g { h { i { j } } } } } } } } } }";
let result = explain_query(very_deep_query).unwrap();
assert!(result.complexity.depth >= 8);
}