this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 324 lines 13 kB view raw
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}