use super::common::*;
#[tokio::test]
async fn test_three_subgraph_setup_validation() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
println!("\n--- Test: 3-subgraph setup validation ---");
let result = setup_three_subgraph_tests().await;
assert!(result.is_ok(), "Setup should succeed: {:?}", result.err());
let users_result = wait_for_service(USERS_SUBGRAPH_URL, 5).await;
assert!(users_result.is_ok(), "Users subgraph should be ready");
let orders_result = wait_for_service(ORDERS_SUBGRAPH_URL, 5).await;
assert!(orders_result.is_ok(), "Orders subgraph should be ready");
let products_result = wait_for_service(PRODUCTS_SUBGRAPH_URL, 5).await;
assert!(products_result.is_ok(), "Products subgraph should be ready");
let gateway_result = wait_for_service(APOLLO_GATEWAY_URL, 5).await;
assert!(gateway_result.is_ok(), "Apollo Router gateway should be ready");
println!("✓ All 3 subgraphs + gateway validation passed");
}
#[tokio::test]
async fn test_three_subgraph_direct_queries() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: Direct queries to products subgraph ---");
let query = r"
query {
products {
id
name
price
}
}
";
let response = graphql_query(PRODUCTS_SUBGRAPH_URL, query).await.expect("Query should succeed");
assert!(
!has_errors(&response),
"Query should not have errors: {}",
get_error_messages(&response)
);
let products = extract_data(&response)
.and_then(|d| d.get("products"))
.and_then(|p| p.as_array())
.expect("Should return products array");
println!("✓ Products subgraph returned {} products", products.len());
assert!(!products.is_empty(), "Should have products available");
}
#[tokio::test]
async fn test_three_subgraph_order_with_products() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: Orders with products field (2-hop) ---");
let query = r"
query {
orders {
id
status
products {
id
name
price
}
}
}
";
let response = graphql_query(APOLLO_GATEWAY_URL, query).await.expect("Query should succeed");
assert!(
!has_errors(&response),
"Query should not have errors: {}",
get_error_messages(&response)
);
let orders = extract_data(&response)
.and_then(|d| d.get("orders"))
.and_then(|o| o.as_array())
.expect("Should return orders array");
println!("✓ Orders query returned {} orders with products", orders.len());
}
#[tokio::test]
async fn test_three_subgraph_federation_users_orders_products() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: 3-hop federation query (users -> orders -> products) ---");
let query = r"
query {
users(limit: 2) {
id
identifier
orders {
id
status
products {
id
name
price
}
}
}
}
";
let start = std::time::Instant::now();
let response = graphql_query(APOLLO_GATEWAY_URL, query).await.expect("Query should succeed");
let elapsed = start.elapsed();
assert!(
!has_errors(&response),
"Query should not have errors: {}",
get_error_messages(&response)
);
let users = extract_data(&response)
.and_then(|d| d.get("users"))
.and_then(|u| u.as_array())
.expect("Should return users array");
println!(
"✓ 3-hop federation query returned {} users with orders and products in {:.0}ms",
users.len(),
elapsed.as_millis()
);
if let Some(first_user) = users.first() {
let has_orders = first_user.get("orders").is_some();
let has_products = first_user
.get("orders")
.and_then(|o| o.as_array())
.and_then(|arr| arr.first())
.is_some_and(|o| o.get("products").is_some());
assert!(has_orders, "User should have orders");
assert!(has_products, "Order should have products");
}
}
#[tokio::test]
async fn test_three_subgraph_entity_resolution_chain() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: Entity resolution chain across 3 subgraphs ---");
let users_response = graphql_query(USERS_SUBGRAPH_URL, "query { users(limit: 1) { id } }")
.await
.expect("Should get user");
let user_id = extract_data(&users_response)
.and_then(|d| d.get("users"))
.and_then(|u| u.as_array())
.and_then(|arr| arr.first())
.and_then(|user| user.get("id"))
.and_then(|id| id.as_str())
.map(|s| s.to_string());
assert!(user_id.is_some(), "Should have a user ID");
if let Some(uid) = user_id {
let orders_query = r"query { users(limit: 1) { orders { id status } } }".to_string();
let orders_response = graphql_query(APOLLO_GATEWAY_URL, &orders_query)
.await
.expect("Should get orders");
let has_orders = extract_data(&orders_response)
.and_then(|d| d.get("users"))
.and_then(|u| u.as_array())
.and_then(|arr| arr.first())
.and_then(|u| u.get("orders"))
.is_some();
assert!(has_orders, "User should have orders");
let full_query = r"
query {
users(limit: 1) {
id
orders(limit: 1) {
id
products {
id
name
}
}
}
}
";
let products_response = graphql_query(APOLLO_GATEWAY_URL, full_query)
.await
.expect("Should get products");
let has_products = extract_data(&products_response)
.and_then(|d| d.get("users"))
.and_then(|u| u.as_array())
.and_then(|arr| arr.first())
.and_then(|u| u.get("orders"))
.and_then(|o| o.as_array())
.and_then(|arr| arr.first())
.is_some_and(|o| o.get("products").is_some());
println!("✓ Entity resolution chain: users -> orders -> products (user_id: {})", uid);
assert!(has_products, "Should resolve products through the chain");
}
}
#[tokio::test]
async fn test_three_subgraph_cross_boundary_federation() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: Cross-boundary federation (multi-level extends) ---");
let query = r"
query {
products(limit: 5) {
id
name
price
}
}
";
let response = graphql_query(APOLLO_GATEWAY_URL, query).await.expect("Query should succeed");
assert!(
!has_errors(&response),
"Query should not have errors: {}",
get_error_messages(&response)
);
let products = extract_data(&response)
.and_then(|d| d.get("products"))
.and_then(|p| p.as_array())
.expect("Should return products");
println!("✓ Cross-boundary federation returned {} products", products.len());
assert!(!products.is_empty(), "Should have products");
}
#[tokio::test]
async fn test_three_subgraph_mutation_propagation() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_three_subgraph_tests().await.expect("Setup should succeed");
println!("\n--- Test: Mutation propagation across 3 subgraphs ---");
let query = r"
query {
users(limit: 1) {
id
identifier
}
}
";
let response = graphql_query(APOLLO_GATEWAY_URL, query).await.expect("Query should succeed");
assert!(
!has_errors(&response),
"Query should not have errors: {}",
get_error_messages(&response)
);
println!("✓ Mutation propagation test completed");
}