this repo has no description
0
fork

Configure Feed

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

at main 1044 lines 43 kB view raw
1use std::sync::Arc; 2use std::time::Duration; 3use tokio::time::sleep; 4use serde_json::{json, Value}; 5use reqwest::Client; 6use gigabrain::Graph; 7use gigabrain::server::{ServerConfig, rest::RestServer}; 8use std::collections::HashMap; 9 10/// End-to-end scenario testing suite 11/// This module tests complete user workflows and business scenarios that span 12/// multiple API calls and represent real-world usage patterns. 13 14#[derive(Clone)] 15pub struct E2ETestClient { 16 client: Client, 17 base_url: String, 18} 19 20impl E2ETestClient { 21 pub fn new(base_url: &str) -> Self { 22 Self { 23 client: Client::new(), 24 base_url: base_url.to_string(), 25 } 26 } 27 28 pub async fn post_json(&self, path: &str, body: Value) -> Result<reqwest::Response, reqwest::Error> { 29 self.client 30 .post(&format!("{}{}", self.base_url, path)) 31 .header("Content-Type", "application/json") 32 .json(&body) 33 .send() 34 .await 35 } 36 37 pub async fn get(&self, path: &str) -> Result<reqwest::Response, reqwest::Error> { 38 self.client 39 .get(&format!("{}{}", self.base_url, path)) 40 .send() 41 .await 42 } 43 44 pub async fn put_json(&self, path: &str, body: Value) -> Result<reqwest::Response, reqwest::Error> { 45 self.client 46 .put(&format!("{}{}", self.base_url, path)) 47 .header("Content-Type", "application/json") 48 .json(&body) 49 .send() 50 .await 51 } 52 53 pub async fn delete(&self, path: &str) -> Result<reqwest::Response, reqwest::Error> { 54 self.client 55 .delete(&format!("{}{}", self.base_url, path)) 56 .send() 57 .await 58 } 59} 60 61pub struct E2ETestServer { 62 _handle: tokio::task::JoinHandle<()>, 63 base_url: String, 64} 65 66impl E2ETestServer { 67 pub async fn start() -> Result<Self, Box<dyn std::error::Error>> { 68 use std::net::TcpListener; 69 70 let listener = TcpListener::bind("127.0.0.1:0")?; 71 let port = listener.local_addr()?.port(); 72 drop(listener); 73 74 let graph = Arc::new(Graph::new()); 75 let config = ServerConfig::default(); 76 let server = RestServer::new(graph, config); 77 78 let handle = tokio::spawn(async move { 79 if let Err(e) = server.serve(port).await { 80 eprintln!("E2E test server error: {}", e); 81 } 82 }); 83 84 sleep(Duration::from_millis(150)).await; 85 86 let base_url = format!("http://localhost:{}", port); 87 88 Ok(Self { 89 _handle: handle, 90 base_url, 91 }) 92 } 93 94 pub fn client(&self) -> E2ETestClient { 95 E2ETestClient::new(&self.base_url) 96 } 97} 98 99#[cfg(test)] 100mod tests { 101 use super::*; 102 103 async fn setup() -> E2ETestServer { 104 E2ETestServer::start().await.expect("Failed to start test server") 105 } 106 107 #[tokio::test] 108 async fn test_complete_social_network_scenario() { 109 let server = setup().await; 110 let client = server.client(); 111 112 // Scenario: Build a small social network with friends and interests 113 114 // Step 1: Create schema constraints for data integrity 115 let constraints = vec![ 116 json!({ 117 "constraint_type": "required", 118 "label": "Person", 119 "property": "name" 120 }), 121 json!({ 122 "constraint_type": "type", 123 "label": "Person", 124 "property": "age", 125 "type_value": "integer" 126 }), 127 json!({ 128 "constraint_type": "range", 129 "label": "Person", 130 "property": "age", 131 "min_value": 0, 132 "max_value": 150 133 }), 134 ]; 135 136 for constraint in constraints { 137 let response = client.post_json("/api/v1/constraints", constraint).await.expect("Constraint creation failed"); 138 assert_eq!(response.status(), 200, "All constraints should be created successfully"); 139 } 140 141 // Step 2: Create people in the social network 142 let people = vec![ 143 ("Alice", 28, "Software Engineer"), 144 ("Bob", 32, "Data Scientist"), 145 ("Charlie", 25, "Designer"), 146 ("Diana", 30, "Product Manager"), 147 ("Eve", 27, "DevOps Engineer"), 148 ]; 149 150 let mut person_ids = HashMap::new(); 151 152 for (name, age, job) in people { 153 let person = json!({ 154 "labels": ["Person"], 155 "properties": { 156 "name": name, 157 "age": age, 158 "job": job, 159 "joined": "2024" 160 } 161 }); 162 163 let response = client.post_json("/api/v1/nodes", person).await.expect("Person creation failed"); 164 assert_eq!(response.status(), 200, "Person should be created successfully"); 165 166 let body: Value = response.json().await.expect("Response parsing failed"); 167 let person_id = body["node_id"].as_u64().expect("person_id should be present"); 168 person_ids.insert(name, person_id); 169 } 170 171 // Step 3: Create interests as separate nodes 172 let interests = vec!["Rust", "Machine Learning", "UI/UX", "Kubernetes", "Startups"]; 173 let mut interest_ids = HashMap::new(); 174 175 for interest in interests { 176 let interest_node = json!({ 177 "labels": ["Interest"], 178 "properties": { 179 "name": interest, 180 "category": "Technology" 181 } 182 }); 183 184 let response = client.post_json("/api/v1/nodes", interest_node).await.expect("Interest creation failed"); 185 assert_eq!(response.status(), 200, "Interest should be created successfully"); 186 187 let body: Value = response.json().await.expect("Response parsing failed"); 188 let interest_id = body["node_id"].as_u64().expect("interest_id should be present"); 189 interest_ids.insert(interest, interest_id); 190 } 191 192 // Step 4: Create friendship relationships 193 let friendships = vec![ 194 ("Alice", "Bob"), 195 ("Alice", "Charlie"), 196 ("Bob", "Diana"), 197 ("Charlie", "Eve"), 198 ("Diana", "Eve"), 199 ("Alice", "Diana"), 200 ]; 201 202 let mut friendship_ids = Vec::new(); 203 204 for (person1, person2) in friendships { 205 let person1_id = person_ids[person1]; 206 let person2_id = person_ids[person2]; 207 208 let friendship = json!({ 209 "start_node": person1_id, 210 "end_node": person2_id, 211 "rel_type": "FRIEND", 212 "properties": { 213 "since": "2024", 214 "strength": 8 215 } 216 }); 217 218 let response = client.post_json("/api/v1/relationships", friendship).await.expect("Friendship creation failed"); 219 assert_eq!(response.status(), 200, "Friendship should be created successfully"); 220 221 let body: Value = response.json().await.expect("Response parsing failed"); 222 let friendship_id = body["relationship_id"].as_u64().expect("friendship_id should be present"); 223 friendship_ids.push(friendship_id); 224 } 225 226 // Step 5: Create interest relationships 227 let person_interests = vec![ 228 ("Alice", "Rust"), 229 ("Alice", "Startups"), 230 ("Bob", "Machine Learning"), 231 ("Bob", "Rust"), 232 ("Charlie", "UI/UX"), 233 ("Diana", "Startups"), 234 ("Diana", "Machine Learning"), 235 ("Eve", "Kubernetes"), 236 ("Eve", "Rust"), 237 ]; 238 239 for (person, interest) in person_interests { 240 let person_id = person_ids[person]; 241 let interest_id = interest_ids[interest]; 242 243 let interest_rel = json!({ 244 "start_node": person_id, 245 "end_node": interest_id, 246 "rel_type": "INTERESTED_IN", 247 "properties": { 248 "level": "High", 249 "years": 3 250 } 251 }); 252 253 let response = client.post_json("/api/v1/relationships", interest_rel).await.expect("Interest relationship creation failed"); 254 assert_eq!(response.status(), 200, "Interest relationship should be created successfully"); 255 } 256 257 // Step 6: Verify the social network structure 258 // Check that all people exist 259 for (name, &person_id) in &person_ids { 260 let response = client.get(&format!("/api/v1/nodes/{}", person_id)).await.expect("Person retrieval failed"); 261 assert_eq!(response.status(), 200, "Person should be retrievable"); 262 263 let body: Value = response.json().await.expect("Response parsing failed"); 264 assert_eq!(body["properties"]["name"], json!(name), "Person name should match"); 265 assert!(body["labels"].as_array().unwrap().contains(&json!("Person")), "Should have Person label"); 266 } 267 268 // Step 7: Test social network queries 269 // Get statistics about the network 270 let response = client.get("/api/v1/stats").await.expect("Stats failed"); 271 let stats: Value = response.json().await.expect("Stats parsing failed"); 272 let node_count = stats["node_count"].as_u64().unwrap(); 273 274 // We created 5 people + 5 interests = 10 nodes minimum 275 assert!(node_count >= 10, "Should have at least 10 nodes in social network"); 276 277 let labels = stats["labels"].as_array().unwrap(); 278 assert!(labels.contains(&json!("Person")), "Stats should show Person label"); 279 assert!(labels.contains(&json!("Interest")), "Stats should show Interest label"); 280 281 // Step 8: Update someone's profile (realistic workflow) 282 let alice_id = person_ids["Alice"]; 283 let updated_alice = json!({ 284 "labels": ["Person", "TeamLead"], 285 "properties": { 286 "name": "Alice Smith", 287 "age": 29, 288 "job": "Senior Software Engineer", 289 "team_size": 4 290 } 291 }); 292 293 let response = client.put_json(&format!("/api/v1/nodes/{}", alice_id), updated_alice).await.expect("Alice update failed"); 294 assert_eq!(response.status(), 200, "Alice profile should be updated"); 295 296 // Verify the update 297 let response = client.get(&format!("/api/v1/nodes/{}", alice_id)).await.expect("Alice retrieval failed"); 298 let body: Value = response.json().await.expect("Response parsing failed"); 299 assert_eq!(body["properties"]["name"], "Alice Smith", "Name should be updated"); 300 assert_eq!(body["properties"]["age"], 29, "Age should be updated"); 301 assert!(body["labels"].as_array().unwrap().contains(&json!("TeamLead")), "Should have TeamLead label"); 302 303 println!("✅ Complete social network scenario executed successfully!"); 304 println!(" - Created {} people with constraints", person_ids.len()); 305 println!(" - Created {} interests", interest_ids.len()); 306 println!(" - Created {} friendships", friendship_ids.len()); 307 println!(" - Updated profiles and verified data integrity"); 308 } 309 310 #[tokio::test] 311 async fn test_e_commerce_product_catalog_scenario() { 312 let server = setup().await; 313 let client = server.client(); 314 315 // Scenario: Build an e-commerce product catalog with categories and reviews 316 317 // Step 1: Create schema for product data 318 let product_constraints = vec![ 319 json!({ 320 "constraint_type": "required", 321 "label": "Product", 322 "property": "name" 323 }), 324 json!({ 325 "constraint_type": "required", 326 "label": "Product", 327 "property": "price" 328 }), 329 json!({ 330 "constraint_type": "type", 331 "label": "Product", 332 "property": "price", 333 "type_value": "float" 334 }), 335 json!({ 336 "constraint_type": "range", 337 "label": "Review", 338 "property": "rating", 339 "min_value": 1, 340 "max_value": 5 341 }), 342 ]; 343 344 for constraint in product_constraints { 345 let response = client.post_json("/api/v1/constraints", constraint).await.expect("Constraint creation failed"); 346 assert_eq!(response.status(), 200); 347 } 348 349 // Step 2: Create product categories 350 let categories = vec!["Electronics", "Books", "Clothing", "Home & Garden"]; 351 let mut category_ids = HashMap::new(); 352 353 for category in categories { 354 let category_node = json!({ 355 "labels": ["Category"], 356 "properties": { 357 "name": category, 358 "active": true 359 } 360 }); 361 362 let response = client.post_json("/api/v1/nodes", category_node).await.expect("Category creation failed"); 363 let body: Value = response.json().await.expect("Response parsing failed"); 364 let category_id = body["node_id"].as_u64().expect("category_id should be present"); 365 category_ids.insert(category, category_id); 366 } 367 368 // Step 3: Create products 369 let products = vec![ 370 ("iPhone 15", 999.99, "Electronics", "Latest smartphone from Apple"), 371 ("Rust Programming Book", 39.99, "Books", "Learn Rust programming language"), 372 ("Winter Jacket", 129.50, "Clothing", "Warm jacket for winter"), 373 ("Garden Tools Set", 75.00, "Home & Garden", "Complete set of garden tools"), 374 ("MacBook Pro", 2399.99, "Electronics", "Professional laptop"), 375 ("Design Patterns Book", 45.99, "Books", "Classic software design book"), 376 ]; 377 378 let mut product_ids = HashMap::new(); 379 380 for (name, price, category, description) in products { 381 let product = json!({ 382 "labels": ["Product"], 383 "properties": { 384 "name": name, 385 "price": price, 386 "description": description, 387 "in_stock": true, 388 "created_date": "2024-01-15" 389 } 390 }); 391 392 let response = client.post_json("/api/v1/nodes", product).await.expect("Product creation failed"); 393 assert_eq!(response.status(), 200); 394 395 let body: Value = response.json().await.expect("Response parsing failed"); 396 let product_id = body["node_id"].as_u64().expect("product_id should be present"); 397 product_ids.insert(name, product_id); 398 399 // Create category relationship 400 let category_id = category_ids[category]; 401 let belongs_to = json!({ 402 "start_node": product_id, 403 "end_node": category_id, 404 "rel_type": "BELONGS_TO", 405 "properties": { 406 "primary": true 407 } 408 }); 409 410 let response = client.post_json("/api/v1/relationships", belongs_to).await.expect("Category relationship failed"); 411 assert_eq!(response.status(), 200); 412 } 413 414 // Step 4: Create customers 415 let customers = vec![ 416 ("john_doe", "John Doe", "john@example.com"), 417 ("jane_smith", "Jane Smith", "jane@example.com"), 418 ("bob_wilson", "Bob Wilson", "bob@example.com"), 419 ]; 420 421 let mut customer_ids = HashMap::new(); 422 423 for (username, full_name, email) in customers { 424 let customer = json!({ 425 "labels": ["Customer"], 426 "properties": { 427 "username": username, 428 "full_name": full_name, 429 "email": email, 430 "member_since": "2024-01-01" 431 } 432 }); 433 434 let response = client.post_json("/api/v1/nodes", customer).await.expect("Customer creation failed"); 435 let body: Value = response.json().await.expect("Response parsing failed"); 436 let customer_id = body["node_id"].as_u64().expect("customer_id should be present"); 437 customer_ids.insert(username, customer_id); 438 } 439 440 // Step 5: Create reviews and purchases 441 let reviews = vec![ 442 ("john_doe", "iPhone 15", 5, "Amazing phone, great camera!"), 443 ("jane_smith", "iPhone 15", 4, "Good phone but expensive"), 444 ("bob_wilson", "Rust Programming Book", 5, "Excellent book for learning Rust"), 445 ("john_doe", "MacBook Pro", 5, "Perfect for development work"), 446 ("jane_smith", "Winter Jacket", 4, "Warm and comfortable"), 447 ]; 448 449 for (customer, product, rating, comment) in reviews { 450 let customer_id = customer_ids[customer]; 451 let product_id = product_ids[product]; 452 453 // Create review node 454 let review = json!({ 455 "labels": ["Review"], 456 "properties": { 457 "rating": rating, 458 "comment": comment, 459 "date": "2024-01-20", 460 "verified_purchase": true 461 } 462 }); 463 464 let response = client.post_json("/api/v1/nodes", review).await.expect("Review creation failed"); 465 let review_body: Value = response.json().await.expect("Response parsing failed"); 466 let review_id = review_body["node_id"].as_u64().expect("review_id should be present"); 467 468 // Create customer->review relationship 469 let wrote_review = json!({ 470 "start_node": customer_id, 471 "end_node": review_id, 472 "rel_type": "WROTE", 473 "properties": {} 474 }); 475 476 let response = client.post_json("/api/v1/relationships", wrote_review).await.expect("Wrote relationship failed"); 477 assert_eq!(response.status(), 200); 478 479 // Create review->product relationship 480 let reviews_product = json!({ 481 "start_node": review_id, 482 "end_node": product_id, 483 "rel_type": "REVIEWS", 484 "properties": {} 485 }); 486 487 let response = client.post_json("/api/v1/relationships", reviews_product).await.expect("Reviews relationship failed"); 488 assert_eq!(response.status(), 200); 489 490 // Create purchase relationship 491 let purchased = json!({ 492 "start_node": customer_id, 493 "end_node": product_id, 494 "rel_type": "PURCHASED", 495 "properties": { 496 "date": "2024-01-18", 497 "quantity": 1, 498 "price_paid": product_ids.get(product).map(|_| { 499 match product { 500 "iPhone 15" => 999.99, 501 "Rust Programming Book" => 39.99, 502 "MacBook Pro" => 2399.99, 503 "Winter Jacket" => 129.50, 504 _ => 0.0 505 } 506 }).unwrap_or(0.0) 507 } 508 }); 509 510 let response = client.post_json("/api/v1/relationships", purchased).await.expect("Purchase relationship failed"); 511 assert_eq!(response.status(), 200); 512 } 513 514 // Step 6: Test product catalog queries 515 // Verify all products exist 516 for (product_name, &product_id) in &product_ids { 517 let response = client.get(&format!("/api/v1/nodes/{}", product_id)).await.expect("Product retrieval failed"); 518 assert_eq!(response.status(), 200); 519 520 let body: Value = response.json().await.expect("Response parsing failed"); 521 assert_eq!(body["properties"]["name"], json!(product_name)); 522 assert!(body["properties"]["price"].is_number()); 523 } 524 525 // Step 7: Simulate inventory update 526 let iphone_id = product_ids["iPhone 15"]; 527 let inventory_update = json!({ 528 "labels": ["Product"], 529 "properties": { 530 "name": "iPhone 15", 531 "price": 949.99, // Price drop 532 "description": "Latest smartphone from Apple - Now on sale!", 533 "in_stock": true, 534 "sale_price": 949.99, 535 "original_price": 999.99 536 } 537 }); 538 539 let response = client.put_json(&format!("/api/v1/nodes/{}", iphone_id), inventory_update).await.expect("Inventory update failed"); 540 assert_eq!(response.status(), 200); 541 542 // Verify the price update 543 let response = client.get(&format!("/api/v1/nodes/{}", iphone_id)).await.expect("Product retrieval failed"); 544 let body: Value = response.json().await.expect("Response parsing failed"); 545 assert_eq!(body["properties"]["price"], 949.99, "Price should be updated"); 546 assert!(body["properties"]["sale_price"].is_number(), "Sale price should be set"); 547 548 // Step 8: Get overall catalog statistics 549 let response = client.get("/api/v1/stats").await.expect("Stats failed"); 550 let stats: Value = response.json().await.expect("Stats parsing failed"); 551 let total_nodes = stats["node_count"].as_u64().unwrap(); 552 553 // Products + Categories + Customers + Reviews = substantial catalog 554 assert!(total_nodes >= 15, "Should have substantial e-commerce catalog"); 555 556 let labels = stats["labels"].as_array().unwrap(); 557 assert!(labels.contains(&json!("Product")), "Should have Product label"); 558 assert!(labels.contains(&json!("Category")), "Should have Category label"); 559 assert!(labels.contains(&json!("Customer")), "Should have Customer label"); 560 assert!(labels.contains(&json!("Review")), "Should have Review label"); 561 562 println!("✅ E-commerce product catalog scenario executed successfully!"); 563 println!(" - Created {} categories", category_ids.len()); 564 println!(" - Created {} products with pricing", product_ids.len()); 565 println!(" - Created {} customers", customer_ids.len()); 566 println!(" - Created reviews and purchase relationships"); 567 println!(" - Updated inventory and pricing"); 568 } 569 570 #[tokio::test] 571 async fn test_knowledge_graph_research_scenario() { 572 let server = setup().await; 573 let client = server.client(); 574 575 // Scenario: Build a research knowledge graph with papers, authors, and citations 576 577 // Step 1: Create research domain constraints 578 let research_constraints = vec![ 579 json!({ 580 "constraint_type": "required", 581 "label": "Paper", 582 "property": "title" 583 }), 584 json!({ 585 "constraint_type": "required", 586 "label": "Author", 587 "property": "name" 588 }), 589 json!({ 590 "constraint_type": "type", 591 "label": "Paper", 592 "property": "year", 593 "type_value": "integer" 594 }), 595 json!({ 596 "constraint_type": "range", 597 "label": "Paper", 598 "property": "year", 599 "min_value": 1900, 600 "max_value": 2024 601 }), 602 ]; 603 604 for constraint in research_constraints { 605 let response = client.post_json("/api/v1/constraints", constraint).await.expect("Constraint creation failed"); 606 assert_eq!(response.status(), 200); 607 } 608 609 // Step 2: Create research fields 610 let fields = vec!["Machine Learning", "Distributed Systems", "Computer Graphics", "Cybersecurity"]; 611 let mut field_ids = HashMap::new(); 612 613 for field in fields { 614 let field_node = json!({ 615 "labels": ["ResearchField"], 616 "properties": { 617 "name": field, 618 "established": "1960s" 619 } 620 }); 621 622 let response = client.post_json("/api/v1/nodes", field_node).await.expect("Field creation failed"); 623 let body: Value = response.json().await.expect("Response parsing failed"); 624 let field_id = body["node_id"].as_u64().expect("field_id should be present"); 625 field_ids.insert(field, field_id); 626 } 627 628 // Step 3: Create authors 629 let authors = vec![ 630 ("Dr. Alice Chen", "Stanford University", "Machine Learning"), 631 ("Prof. Bob Thompson", "MIT", "Distributed Systems"), 632 ("Dr. Carol Rodriguez", "UC Berkeley", "Computer Graphics"), 633 ("Prof. David Kim", "CMU", "Cybersecurity"), 634 ("Dr. Eva Martinez", "Google Research", "Machine Learning"), 635 ]; 636 637 let mut author_ids = HashMap::new(); 638 639 for (name, affiliation, primary_field) in authors { 640 let author = json!({ 641 "labels": ["Author"], 642 "properties": { 643 "name": name, 644 "affiliation": affiliation, 645 "h_index": 42, 646 "primary_field": primary_field 647 } 648 }); 649 650 let response = client.post_json("/api/v1/nodes", author).await.expect("Author creation failed"); 651 let body: Value = response.json().await.expect("Response parsing failed"); 652 let author_id = body["node_id"].as_u64().expect("author_id should be present"); 653 author_ids.insert(name, author_id); 654 655 // Connect author to research field 656 let field_id = field_ids[primary_field]; 657 let researches = json!({ 658 "start_node": author_id, 659 "end_node": field_id, 660 "rel_type": "RESEARCHES", 661 "properties": { 662 "years_active": 10 663 } 664 }); 665 666 let response = client.post_json("/api/v1/relationships", researches).await.expect("Research relationship failed"); 667 assert_eq!(response.status(), 200); 668 } 669 670 // Step 4: Create research papers 671 let papers = vec![ 672 ("Attention Is All You Need", 2017, "Dr. Alice Chen", "Machine Learning"), 673 ("MapReduce: Simplified Data Processing", 2004, "Prof. Bob Thompson", "Distributed Systems"), 674 ("Real-Time Ray Tracing", 2020, "Dr. Carol Rodriguez", "Computer Graphics"), 675 ("Zero Trust Security Model", 2019, "Prof. David Kim", "Cybersecurity"), 676 ("Neural Networks for NLP", 2021, "Dr. Eva Martinez", "Machine Learning"), 677 ("Distributed Consensus Algorithms", 2018, "Prof. Bob Thompson", "Distributed Systems"), 678 ]; 679 680 let mut paper_ids = HashMap::new(); 681 682 for (title, year, author, field) in papers { 683 let paper = json!({ 684 "labels": ["Paper"], 685 "properties": { 686 "title": title, 687 "year": year, 688 "abstract": format!("Research paper on {}", title), 689 "citation_count": 150, 690 "venue": "Top Conference" 691 } 692 }); 693 694 let response = client.post_json("/api/v1/nodes", paper).await.expect("Paper creation failed"); 695 let body: Value = response.json().await.expect("Response parsing failed"); 696 let paper_id = body["node_id"].as_u64().expect("paper_id should be present"); 697 paper_ids.insert(title, paper_id); 698 699 // Connect paper to author 700 let author_id = author_ids[author]; 701 let authored = json!({ 702 "start_node": author_id, 703 "end_node": paper_id, 704 "rel_type": "AUTHORED", 705 "properties": { 706 "role": "Lead Author" 707 } 708 }); 709 710 let response = client.post_json("/api/v1/relationships", authored).await.expect("Authored relationship failed"); 711 assert_eq!(response.status(), 200); 712 713 // Connect paper to research field 714 let field_id = field_ids[field]; 715 let belongs_to_field = json!({ 716 "start_node": paper_id, 717 "end_node": field_id, 718 "rel_type": "BELONGS_TO_FIELD", 719 "properties": {} 720 }); 721 722 let response = client.post_json("/api/v1/relationships", belongs_to_field).await.expect("Field relationship failed"); 723 assert_eq!(response.status(), 200); 724 } 725 726 // Step 5: Create citations between papers 727 let citations = vec![ 728 ("Neural Networks for NLP", "Attention Is All You Need"), 729 ("Distributed Consensus Algorithms", "MapReduce: Simplified Data Processing"), 730 ("Real-Time Ray Tracing", "Attention Is All You Need"), // Cross-field citation 731 ]; 732 733 for (citing_paper, cited_paper) in citations { 734 let citing_id = paper_ids[citing_paper]; 735 let cited_id = paper_ids[cited_paper]; 736 737 let cites = json!({ 738 "start_node": citing_id, 739 "end_node": cited_id, 740 "rel_type": "CITES", 741 "properties": { 742 "relevance": "High", 743 "section": "Related Work" 744 } 745 }); 746 747 let response = client.post_json("/api/v1/relationships", cites).await.expect("Citation relationship failed"); 748 assert_eq!(response.status(), 200); 749 } 750 751 // Step 6: Create collaborations between authors 752 let collaborations = vec![ 753 ("Dr. Alice Chen", "Dr. Eva Martinez"), 754 ("Prof. Bob Thompson", "Prof. David Kim"), 755 ]; 756 757 for (author1, author2) in collaborations { 758 let author1_id = author_ids[author1]; 759 let author2_id = author_ids[author2]; 760 761 let collaborates = json!({ 762 "start_node": author1_id, 763 "end_node": author2_id, 764 "rel_type": "COLLABORATES_WITH", 765 "properties": { 766 "projects": 3, 767 "since": 2020 768 } 769 }); 770 771 let response = client.post_json("/api/v1/relationships", collaborates).await.expect("Collaboration relationship failed"); 772 assert_eq!(response.status(), 200); 773 } 774 775 // Step 7: Query the knowledge graph 776 // Verify all entities exist 777 for (paper_title, &paper_id) in &paper_ids { 778 let response = client.get(&format!("/api/v1/nodes/{}", paper_id)).await.expect("Paper retrieval failed"); 779 assert_eq!(response.status(), 200); 780 781 let body: Value = response.json().await.expect("Response parsing failed"); 782 assert_eq!(body["properties"]["title"], json!(paper_title)); 783 assert!(body["properties"]["year"].is_number()); 784 } 785 786 // Step 8: Simulate paper update (new citation count) 787 let attention_paper_id = paper_ids["Attention Is All You Need"]; 788 let paper_update = json!({ 789 "labels": ["Paper", "HighlyInfluential"], 790 "properties": { 791 "title": "Attention Is All You Need", 792 "year": 2017, 793 "abstract": "Groundbreaking paper introducing the Transformer architecture", 794 "citation_count": 25000, // Massive increase 795 "venue": "NIPS 2017", 796 "impact_factor": "Revolutionary" 797 } 798 }); 799 800 let response = client.put_json(&format!("/api/v1/nodes/{}", attention_paper_id), paper_update).await.expect("Paper update failed"); 801 assert_eq!(response.status(), 200); 802 803 // Verify the update 804 let response = client.get(&format!("/api/v1/nodes/{}", attention_paper_id)).await.expect("Paper retrieval failed"); 805 let body: Value = response.json().await.expect("Response parsing failed"); 806 assert_eq!(body["properties"]["citation_count"], 25000); 807 assert!(body["labels"].as_array().unwrap().contains(&json!("HighlyInfluential"))); 808 809 // Step 9: Get knowledge graph statistics 810 let response = client.get("/api/v1/stats").await.expect("Stats failed"); 811 let stats: Value = response.json().await.expect("Stats parsing failed"); 812 let total_nodes = stats["node_count"].as_u64().unwrap(); 813 814 // Authors + Papers + Fields = comprehensive knowledge graph 815 assert!(total_nodes >= 15, "Should have comprehensive knowledge graph"); 816 817 let labels = stats["labels"].as_array().unwrap(); 818 assert!(labels.contains(&json!("Author")), "Should have Author label"); 819 assert!(labels.contains(&json!("Paper")), "Should have Paper label"); 820 assert!(labels.contains(&json!("ResearchField")), "Should have ResearchField label"); 821 822 println!("✅ Research knowledge graph scenario executed successfully!"); 823 println!(" - Created {} research fields", field_ids.len()); 824 println!(" - Created {} authors with affiliations", author_ids.len()); 825 println!(" - Created {} papers with citations", paper_ids.len()); 826 println!(" - Established citations and collaboration networks"); 827 println!(" - Updated high-impact paper metadata"); 828 } 829 830 #[tokio::test] 831 async fn test_data_migration_and_cleanup_scenario() { 832 let server = setup().await; 833 let client = server.client(); 834 835 // Scenario: Test data migration, cleanup, and maintenance operations 836 837 // Step 1: Create initial data set (legacy format) 838 let legacy_nodes = vec![ 839 json!({ 840 "labels": ["LegacyUser"], 841 "properties": { 842 "username": "user001", 843 "email": "user001@old-system.com", 844 "legacy_id": "LEGACY_001", 845 "created_date": "2020-01-01" 846 } 847 }), 848 json!({ 849 "labels": ["LegacyProduct"], 850 "properties": { 851 "product_code": "PROD_001", 852 "name": "Old Product", 853 "legacy_price": "19.99", // String instead of number 854 "status": "deprecated" 855 } 856 }), 857 ]; 858 859 let mut legacy_ids = Vec::new(); 860 861 for node in legacy_nodes { 862 let response = client.post_json("/api/v1/nodes", node).await.expect("Legacy node creation failed"); 863 assert_eq!(response.status(), 200); 864 865 let body: Value = response.json().await.expect("Response parsing failed"); 866 let node_id = body["node_id"].as_u64().expect("node_id should be present"); 867 legacy_ids.push(node_id); 868 } 869 870 // Step 2: Create new schema constraints for migrated data 871 let new_constraints = vec![ 872 json!({ 873 "constraint_type": "required", 874 "label": "User", 875 "property": "email" 876 }), 877 json!({ 878 "constraint_type": "type", 879 "label": "Product", 880 "property": "price", 881 "type_value": "float" 882 }), 883 ]; 884 885 for constraint in new_constraints { 886 let response = client.post_json("/api/v1/constraints", constraint).await.expect("Constraint creation failed"); 887 assert_eq!(response.status(), 200); 888 } 889 890 // Step 3: Migrate legacy user to new format 891 let legacy_user_id = legacy_ids[0]; 892 let migrated_user = json!({ 893 "labels": ["User", "Migrated"], 894 "properties": { 895 "email": "user001@new-system.com", 896 "username": "modernuser001", 897 "full_name": "John Doe", 898 "legacy_id": "LEGACY_001", 899 "migration_date": "2024-01-15", 900 "status": "active" 901 } 902 }); 903 904 let response = client.put_json(&format!("/api/v1/nodes/{}", legacy_user_id), migrated_user).await.expect("User migration failed"); 905 assert_eq!(response.status(), 200); 906 907 // Verify migration 908 let response = client.get(&format!("/api/v1/nodes/{}", legacy_user_id)).await.expect("Migrated user retrieval failed"); 909 let body: Value = response.json().await.expect("Response parsing failed"); 910 assert!(body["labels"].as_array().unwrap().contains(&json!("Migrated"))); 911 assert_eq!(body["properties"]["email"], "user001@new-system.com"); 912 913 // Step 4: Migrate legacy product to new format 914 let legacy_product_id = legacy_ids[1]; 915 let migrated_product = json!({ 916 "labels": ["Product", "Migrated"], 917 "properties": { 918 "name": "Modernized Product", 919 "price": 19.99, // Now a proper number 920 "product_code": "PROD_001_NEW", 921 "legacy_code": "PROD_001", 922 "migration_date": "2024-01-15", 923 "status": "active", 924 "category": "Electronics" 925 } 926 }); 927 928 let response = client.put_json(&format!("/api/v1/nodes/{}", legacy_product_id), migrated_product).await.expect("Product migration failed"); 929 assert_eq!(response.status(), 200); 930 931 // Step 5: Create audit log entries 932 let audit_entries = vec![ 933 json!({ 934 "labels": ["AuditLog"], 935 "properties": { 936 "action": "MIGRATION", 937 "entity_type": "User", 938 "entity_id": legacy_user_id, 939 "timestamp": "2024-01-15T10:00:00Z", 940 "performed_by": "migration_script", 941 "details": "Migrated from LegacyUser to User format" 942 } 943 }), 944 json!({ 945 "labels": ["AuditLog"], 946 "properties": { 947 "action": "MIGRATION", 948 "entity_type": "Product", 949 "entity_id": legacy_product_id, 950 "timestamp": "2024-01-15T10:01:00Z", 951 "performed_by": "migration_script", 952 "details": "Migrated from LegacyProduct to Product format" 953 } 954 }), 955 ]; 956 957 for audit_entry in audit_entries { 958 let response = client.post_json("/api/v1/nodes", audit_entry).await.expect("Audit log creation failed"); 959 assert_eq!(response.status(), 200); 960 } 961 962 // Step 6: Create some test nodes that will be cleaned up 963 let test_nodes = vec![ 964 json!({ 965 "labels": ["TestData"], 966 "properties": { 967 "test_id": "TEST_001", 968 "purpose": "temporary", 969 "created": "2024-01-15" 970 } 971 }), 972 json!({ 973 "labels": ["TempData"], 974 "properties": { 975 "temp_id": "TEMP_001", 976 "expires": "2024-01-16" 977 } 978 }), 979 ]; 980 981 let mut temp_node_ids = Vec::new(); 982 983 for test_node in test_nodes { 984 let response = client.post_json("/api/v1/nodes", test_node).await.expect("Test node creation failed"); 985 let body: Value = response.json().await.expect("Response parsing failed"); 986 let node_id = body["node_id"].as_u64().expect("node_id should be present"); 987 temp_node_ids.push(node_id); 988 } 989 990 // Step 7: Verify all data exists before cleanup 991 let response = client.get("/api/v1/stats").await.expect("Stats failed"); 992 let stats: Value = response.json().await.expect("Stats parsing failed"); 993 let initial_count = stats["node_count"].as_u64().unwrap(); 994 995 assert!(initial_count >= 6, "Should have created sufficient test data"); 996 997 // Step 8: Perform cleanup operations 998 // Delete temporary test data 999 for &temp_id in &temp_node_ids { 1000 let response = client.delete(&format!("/api/v1/nodes/{}", temp_id)).await.expect("Cleanup deletion failed"); 1001 assert_eq!(response.status(), 204); 1002 1003 // Verify deletion 1004 let response = client.get(&format!("/api/v1/nodes/{}", temp_id)).await.expect("Verification get failed"); 1005 assert_eq!(response.status(), 404, "Temp node should be deleted"); 1006 } 1007 1008 // Step 9: Verify cleanup was successful 1009 let response = client.get("/api/v1/stats").await.expect("Final stats failed"); 1010 let final_stats: Value = response.json().await.expect("Stats parsing failed"); 1011 let final_count = final_stats["node_count"].as_u64().unwrap(); 1012 1013 // Should have fewer nodes after cleanup 1014 assert!(final_count < initial_count, "Node count should decrease after cleanup"); 1015 1016 // Step 10: Verify migrated data integrity 1017 // Check that migrated nodes still exist and have correct structure 1018 let response = client.get(&format!("/api/v1/nodes/{}", legacy_user_id)).await.expect("Migrated user check failed"); 1019 assert_eq!(response.status(), 200); 1020 let user_body: Value = response.json().await.expect("Response parsing failed"); 1021 assert!(user_body["labels"].as_array().unwrap().contains(&json!("User"))); 1022 assert!(user_body["labels"].as_array().unwrap().contains(&json!("Migrated"))); 1023 1024 let response = client.get(&format!("/api/v1/nodes/{}", legacy_product_id)).await.expect("Migrated product check failed"); 1025 assert_eq!(response.status(), 200); 1026 let product_body: Value = response.json().await.expect("Response parsing failed"); 1027 assert!(product_body["labels"].as_array().unwrap().contains(&json!("Product"))); 1028 assert_eq!(product_body["properties"]["price"], 19.99); 1029 1030 // Final verification - check that we have the right labels in our system 1031 let labels = final_stats["labels"].as_array().unwrap(); 1032 assert!(labels.contains(&json!("User")), "Should have User label"); 1033 assert!(labels.contains(&json!("Product")), "Should have Product label"); 1034 assert!(labels.contains(&json!("AuditLog")), "Should have AuditLog label"); 1035 assert!(labels.contains(&json!("Migrated")), "Should have Migrated label"); 1036 1037 println!("✅ Data migration and cleanup scenario executed successfully!"); 1038 println!(" - Migrated {} legacy entities to new format", legacy_ids.len()); 1039 println!(" - Created audit trail for all migrations"); 1040 println!(" - Cleaned up {} temporary test nodes", temp_node_ids.len()); 1041 println!(" - Verified data integrity post-migration"); 1042 println!(" - Final node count: {}", final_count); 1043 } 1044}