this repo has no description
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}