fraiseql_cli/commands/
cost.rs1use anyhow::Result;
6#[allow(unused_imports)]
8use fraiseql_core::graphql::complexity;
9use fraiseql_core::graphql::{complexity::ComplexityAnalyzer, parse_query};
10use serde::Serialize;
11
12use crate::output::CommandResult;
13
14#[derive(Debug, Serialize)]
16pub struct CostResponse {
17 pub query: String,
19 pub complexity_score: usize,
21 pub estimated_cost: usize,
23 pub depth: usize,
25 pub field_count: usize,
27}
28
29pub fn run(query: &str) -> Result<CommandResult> {
31 let _parsed = parse_query(query)?;
33
34 let analyzer = ComplexityAnalyzer::new();
36 let (depth, field_count, score) = analyzer.analyze_complexity(query);
37
38 let response = CostResponse {
39 query: query.to_string(),
40 complexity_score: score,
41 estimated_cost: depth * 25, depth,
43 field_count,
44 };
45
46 Ok(CommandResult::success("cost", serde_json::to_value(&response)?))
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn test_cost_simple_query() {
55 let query = "query { users { id } }";
56 let result = run(query);
57
58 assert!(result.is_ok());
59 let cmd_result = result.unwrap();
60 assert_eq!(cmd_result.status, "success");
61 }
62
63 #[test]
64 fn test_cost_invalid_query_fails() {
65 let query = "query { invalid {";
66 let result = run(query);
67
68 assert!(result.is_err());
69 }
70
71 #[test]
72 fn test_cost_provides_score() {
73 let query = "query { users { id name } }";
74 let result = run(query);
75
76 assert!(result.is_ok());
77 let cmd_result = result.unwrap();
78 if let Some(data) = cmd_result.data {
79 assert!(data["complexity_score"].is_number());
80 }
81 }
82
83 #[test]
84 fn test_cost_more_fields_higher_score() {
85 let few_fields = run("query { users { id } }").unwrap();
86 let many_fields = run("query { users { id name email phone address } }").unwrap();
87
88 let few_score = few_fields
89 .data
90 .as_ref()
91 .and_then(|d| d["complexity_score"].as_u64())
92 .unwrap_or(0);
93 let many_score = many_fields
94 .data
95 .as_ref()
96 .and_then(|d| d["complexity_score"].as_u64())
97 .unwrap_or(0);
98
99 assert!(many_score >= few_score);
100 }
101
102 #[test]
103 fn test_cost_nested_has_higher_score() {
104 let shallow = run("query { users { id } }").unwrap();
105 let deep = run("query { users { posts { comments { author } } } }").unwrap();
106
107 let shallow_score =
108 shallow.data.as_ref().and_then(|d| d["complexity_score"].as_u64()).unwrap_or(0);
109 let deep_score =
110 deep.data.as_ref().and_then(|d| d["complexity_score"].as_u64()).unwrap_or(0);
111
112 assert!(deep_score > shallow_score);
113 }
114}