this repo has no description
1use std::sync::Arc;
2use gigabrain::{Graph, GigabrainError};
3use gigabrain::cypher::{parse_cypher, QueryExecutor};
4
5type TestResult = Result<(), Box<dyn std::error::Error>>;
6
7/// Test basic Cypher query execution without REST API
8/// This should catch core query execution bugs that the CLI depends on
9#[tokio::test]
10async fn test_basic_create_and_match() -> TestResult {
11 let graph = Arc::new(Graph::new());
12 let executor = QueryExecutor::new(graph.clone());
13
14 // Test CREATE query
15 let create_query = parse_cypher("CREATE (alice:Person {name: 'Alice', age: 30})")?;
16 let create_result = executor.execute_query(&create_query).await?;
17
18 // Should return result indicating nodes were created
19 assert!(!create_result.rows.is_empty(), "CREATE should return result indicating nodes created");
20 assert_eq!(create_result.columns.len(), 1, "CREATE should return one column");
21 assert_eq!(create_result.columns[0], "nodes_created", "CREATE should return 'nodes_created' column");
22
23 // Verify node was actually created in graph
24 let all_nodes = graph.get_all_nodes();
25 assert_eq!(all_nodes.len(), 1, "Graph should contain exactly 1 node after CREATE");
26
27 // Test MATCH query to find the created node
28 let match_query = parse_cypher("MATCH (n) RETURN n")?;
29 let match_result = executor.execute_query(&match_query).await?;
30
31 // This is the critical test that's currently failing
32 assert!(!match_result.rows.is_empty(), "MATCH should find the created node");
33 assert_eq!(match_result.rows.len(), 1, "MATCH should return exactly 1 node");
34
35 Ok(())
36}
37
38#[tokio::test]
39async fn test_multiple_node_creation_and_retrieval() -> TestResult {
40 let graph = Arc::new(Graph::new());
41 let executor = QueryExecutor::new(graph.clone());
42
43 // Create multiple nodes
44 let queries = vec![
45 "CREATE (alice:Person {name: 'Alice', age: 30})",
46 "CREATE (bob:Person {name: 'Bob', age: 25})",
47 "CREATE (charlie:Person {name: 'Charlie', age: 35})",
48 ];
49
50 for query_str in queries {
51 let query = parse_cypher(query_str)?;
52 executor.execute_query(&query).await?;
53 }
54
55 // Verify all nodes were created
56 let all_nodes = graph.get_all_nodes();
57 assert_eq!(all_nodes.len(), 3, "Graph should contain exactly 3 nodes");
58
59 // Test MATCH query finds all nodes
60 let match_query = parse_cypher("MATCH (n) RETURN n")?;
61 let match_result = executor.execute_query(&match_query).await?;
62
63 assert_eq!(match_result.rows.len(), 3, "MATCH should return all 3 nodes");
64 assert!(!match_result.columns.is_empty(), "MATCH should return columns");
65
66 Ok(())
67}
68
69#[tokio::test]
70async fn test_label_based_matching() -> TestResult {
71 let graph = Arc::new(Graph::new());
72 let executor = QueryExecutor::new(graph.clone());
73
74 // Create nodes with different labels
75 let create_person = parse_cypher("CREATE (alice:Person {name: 'Alice'})")?;
76 let create_company = parse_cypher("CREATE (acme:Company {name: 'ACME Corp'})")?;
77
78 executor.execute_query(&create_person).await?;
79 executor.execute_query(&create_company).await?;
80
81 // Test matching by label (when implemented)
82 let match_person = parse_cypher("MATCH (p:Person) RETURN p")?;
83 let person_result = executor.execute_query(&match_person).await?;
84
85 // This might not work yet, but should be tested
86 // For now, just ensure it doesn't crash
87 let _ = person_result;
88
89 Ok(())
90}
91
92#[tokio::test]
93async fn test_query_parsing_and_execution() -> TestResult {
94 let graph = Arc::new(Graph::new());
95 let executor = QueryExecutor::new(graph.clone());
96
97 // Test that queries parse correctly and don't crash
98 let test_queries = vec![
99 "CREATE (n:Person {name: 'Test'})",
100 "MATCH (n) RETURN n",
101 "CREATE (a:Person {name: 'A'}) CREATE (b:Person {name: 'B'})",
102 ];
103
104 for query_str in test_queries {
105 let query = parse_cypher(query_str)?;
106 // Should not crash, even if results aren't perfect yet
107 let _result = executor.execute_query(&query).await?;
108 }
109
110 Ok(())
111}
112
113#[tokio::test]
114async fn test_where_clause_parsing() -> TestResult {
115 let graph = Arc::new(Graph::new());
116 let executor = QueryExecutor::new(graph.clone());
117
118 // Create test data
119 let create_alice = parse_cypher("CREATE (alice:Person {name: 'Alice', age: 30})")?;
120 let create_bob = parse_cypher("CREATE (bob:Person {name: 'Bob', age: 25})")?;
121
122 executor.execute_query(&create_alice).await?;
123 executor.execute_query(&create_bob).await?;
124
125 // Test WHERE clause with property access
126 let where_query = parse_cypher("MATCH (p:Person) WHERE p.name = 'Alice' RETURN p")?;
127 // For now, just ensure it parses without error
128 let _result = executor.execute_query(&where_query).await?;
129
130 // Test compound WHERE with AND
131 let compound_where = parse_cypher(
132 "MATCH (a:Person), (b:Person) WHERE a.name = 'Alice' AND b.name = 'Bob' RETURN a, b"
133 )?;
134 // For now, just ensure it parses without error
135 let _result = executor.execute_query(&compound_where).await?;
136
137 Ok(())
138}
139
140/// Test WHERE clause filtering functionality
141#[tokio::test]
142async fn test_where_clause_filtering() -> TestResult {
143 let graph = Arc::new(Graph::new());
144 let executor = QueryExecutor::new(graph.clone());
145
146 // Create test data with different properties
147 let create_alice = parse_cypher("CREATE (alice:Person {name: 'Alice', age: 30})")?;
148 let create_bob = parse_cypher("CREATE (bob:Person {name: 'Bob', age: 25})")?;
149 let create_charlie = parse_cypher("CREATE (charlie:Person {name: 'Charlie', age: 35})")?;
150
151 executor.execute_query(&create_alice).await?;
152 executor.execute_query(&create_bob).await?;
153 executor.execute_query(&create_charlie).await?;
154
155 // Verify all nodes were created
156 let all_nodes = graph.get_all_nodes();
157 assert_eq!(all_nodes.len(), 3, "Should have created 3 nodes");
158
159 // Test basic property equality filtering
160 let where_query = parse_cypher("MATCH (p) WHERE p.name = 'Alice' RETURN p")?;
161 let result = executor.execute_query(&where_query).await?;
162
163 // Should only return Alice
164 assert_eq!(result.rows.len(), 1, "WHERE clause should filter to 1 result");
165
166 // Test AND condition (skip comparison operators for now due to parser limitations)
167 let and_query = parse_cypher("MATCH (p) WHERE p.name = 'Alice' AND p.age = 30 RETURN p")?;
168 let and_result = executor.execute_query(&and_query).await?;
169
170 // Should only return Alice
171 assert_eq!(and_result.rows.len(), 1, "AND condition should return 1 result");
172
173 Ok(())
174}
175
176/// Test WHERE clause with multiple variables
177#[tokio::test]
178async fn test_where_clause_multiple_variables() -> TestResult {
179 let graph = Arc::new(Graph::new());
180 let executor = QueryExecutor::new(graph.clone());
181
182 // Create test data
183 let create_alice = parse_cypher("CREATE (alice:Person {name: 'Alice', age: 30})")?;
184 let create_bob = parse_cypher("CREATE (bob:Person {name: 'Bob', age: 25})")?;
185 let create_charlie = parse_cypher("CREATE (charlie:Person {name: 'Charlie', age: 35})")?;
186
187 executor.execute_query(&create_alice).await?;
188 executor.execute_query(&create_bob).await?;
189 executor.execute_query(&create_charlie).await?;
190
191 // Test filtering with multiple variables (cross product)
192 let multi_var_query = parse_cypher(
193 "MATCH (a), (b) WHERE a.name = 'Alice' AND b.name = 'Bob' RETURN a, b"
194 )?;
195 let multi_result = executor.execute_query(&multi_var_query).await?;
196
197 // Should return exactly one combination: Alice and Bob
198 assert_eq!(multi_result.rows.len(), 1, "Should find exactly one valid combination");
199 assert_eq!(multi_result.columns.len(), 2, "Should return 2 columns (a, b)");
200
201 Ok(())
202}
203
204/// Test WHERE clause with different comparison operators
205#[tokio::test]
206async fn test_where_clause_comparisons() -> TestResult {
207 let graph = Arc::new(Graph::new());
208 let executor = QueryExecutor::new(graph.clone());
209
210 // Create test data with numeric properties
211 let create_young = parse_cypher("CREATE (young:Person {name: 'Young', age: 20})")?;
212 let create_middle = parse_cypher("CREATE (middle:Person {name: 'Middle', age: 30})")?;
213 let create_old = parse_cypher("CREATE (old:Person {name: 'Old', age: 40})")?;
214
215 executor.execute_query(&create_young).await?;
216 executor.execute_query(&create_middle).await?;
217 executor.execute_query(&create_old).await?;
218
219 // For now, test basic equality and not equal (comparison operators like < > have parser issues)
220 let eq_query = parse_cypher("MATCH (p) WHERE p.age = 30 RETURN p")?;
221 let eq_result = executor.execute_query(&eq_query).await?;
222 assert_eq!(eq_result.rows.len(), 1, "Should find 1 person with age = 30");
223
224 // Test string-based filtering
225 let name_query = parse_cypher("MATCH (p) WHERE p.name = 'Young' RETURN p")?;
226 let name_result = executor.execute_query(&name_query).await?;
227 assert_eq!(name_result.rows.len(), 1, "Should find 1 person named Young");
228
229 Ok(())
230}
231
232/// Test relationship creation and querying
233#[tokio::test]
234async fn test_relationship_creation_and_matching() -> TestResult {
235 let graph = Arc::new(Graph::new());
236 let executor = QueryExecutor::new(graph.clone());
237
238 // Test CREATE with relationship pattern
239 let create_query = parse_cypher("CREATE (alice:Person {name: 'Alice'})-[:KNOWS]->(bob:Person {name: 'Bob'})")?;
240 let create_result = executor.execute_query(&create_query).await?;
241
242 // Should return result indicating nodes and relationships were created
243 assert!(!create_result.rows.is_empty(), "CREATE should return result");
244 assert!(create_result.columns.contains(&"nodes_created".to_string()), "Should create nodes");
245 assert!(create_result.columns.contains(&"relationships_created".to_string()), "Should create relationships");
246
247 // Verify nodes were created
248 let all_nodes = graph.get_all_nodes();
249 assert_eq!(all_nodes.len(), 2, "Should create 2 nodes");
250
251 // Test MATCH with relationship pattern (simplified for now)
252 let match_query = parse_cypher("MATCH (a)-[r]->(b) RETURN a, r, b")?;
253 let match_result = executor.execute_query(&match_query).await?;
254
255 // For now, just ensure it doesn't crash
256 let _ = match_result;
257
258 Ok(())
259}
260
261/// Test CLI integration specifically
262#[tokio::test]
263async fn test_cli_query_execution() -> TestResult {
264 use gigabrain::cli::GigaBrainCli;
265
266 let graph = Arc::new(Graph::new());
267 let mut cli = GigaBrainCli::new(graph.clone());
268
269 // Test CREATE through CLI
270 let create_result = cli.execute_command("CREATE (alice:Person {name: 'Alice', age: 30})").await;
271
272 match create_result {
273 gigabrain::cli::CommandResult::Success(output) => {
274 assert!(output.is_some(), "CREATE should return output");
275 let output_str = output.unwrap();
276 assert!(output_str.contains("nodes_created"), "Output should mention nodes_created");
277 },
278 _ => panic!("CREATE command should succeed"),
279 }
280
281 // Test MATCH through CLI - this is the failing case
282 let match_result = cli.execute_command("MATCH (n) RETURN n").await;
283
284 match match_result {
285 gigabrain::cli::CommandResult::Success(output) => {
286 assert!(output.is_some(), "MATCH should return output");
287 let output_str = output.unwrap();
288 // This will fail until we fix the compound query execution
289 assert!(!output_str.contains("0 rows returned"),
290 "MATCH should find the created node, not return 0 rows. Output: {}", output_str);
291 },
292 _ => panic!("MATCH command should succeed"),
293 }
294
295 Ok(())
296}
297
298/// Test relationship creation through CLI
299#[tokio::test]
300async fn test_cli_relationship_creation() -> TestResult {
301 use gigabrain::cli::GigaBrainCli;
302
303 let graph = Arc::new(Graph::new());
304 let mut cli = GigaBrainCli::new(graph.clone());
305
306 // Test CREATE with relationship through CLI
307 let create_result = cli.execute_command("CREATE (alice:Person {name: 'Alice'})-[:KNOWS]->(bob:Person {name: 'Bob'})").await;
308
309 match create_result {
310 gigabrain::cli::CommandResult::Success(output) => {
311 assert!(output.is_some(), "CREATE with relationship should return output");
312 let output_str = output.unwrap();
313 assert!(output_str.contains("nodes_created") || output_str.contains("relationships_created"),
314 "Output should mention nodes_created or relationships_created");
315 },
316 _ => panic!("CREATE relationship command should succeed"),
317 }
318
319 // Verify nodes were actually created
320 let all_nodes = graph.get_all_nodes();
321 assert_eq!(all_nodes.len(), 2, "Should create 2 nodes for relationship pattern");
322
323 Ok(())
324}