use super::common::*;
#[tokio::test]
async fn test_extended_mutation_user_from_authoritative_subgraph() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Direct user mutation in authoritative subgraph ---");
let mutation = r#"
mutation {
createUser(
identifier: "test_user_direct@example.com"
name: "Test User Direct"
email: "test_user_direct@example.com"
) {
id
identifier
name
email
}
}
"#;
let response = graphql_query(USERS_SUBGRAPH_URL, mutation)
.await
.expect("Create user mutation should succeed");
if has_errors(&response) {
eprintln!("! User creation has errors: {}", get_error_messages(&response));
} else {
let user = extract_data(&response)
.and_then(|d| d.get("createUser"))
.expect("Should return created user");
let user_id = user.get("id").and_then(|id| id.as_str()).expect("Should have user ID");
println!("✓ Created user directly in authoritative subgraph: {}", user_id);
}
}
#[tokio::test]
async fn test_extended_mutation_update_user_from_extended_subgraph() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Update user mutation from extended subgraph (HTTP propagation) ---");
let users_response = graphql_query(USERS_SUBGRAPH_URL, r"query { users(limit: 1) { id } }")
.await
.expect("Get users query should succeed");
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())
.expect("Should have user ID");
println!("Using user ID for mutation: {}", user_id);
let mutation = format!(
r#"
mutation {{
updateUser(
id: "{}"
name: "Updated from Orders"
) {{
id
name
email
}}
}}
"#,
user_id
);
let response = graphql_query(ORDERS_SUBGRAPH_URL, &mutation)
.await
.expect("Update user mutation should complete");
if has_errors(&response) {
eprintln!("! Update user mutation has errors: {}", get_error_messages(&response));
eprintln!("! This may indicate extended mutations are not configured");
} else {
let updated_user = extract_data(&response)
.and_then(|d| d.get("updateUser"))
.expect("Should return updated user");
let updated_name = updated_user
.get("name")
.and_then(|n| n.as_str())
.expect("Should have updated name");
println!("✓ Successfully updated user from extended subgraph: {}", updated_name);
}
}
#[tokio::test]
async fn test_extended_mutation_create_order_with_user_reference() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Create order with user reference (entity linking) ---");
let users_response = graphql_query(USERS_SUBGRAPH_URL, r"query { users(limit: 1) { id } }")
.await
.expect("Get users query should succeed");
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())
.expect("Should have user ID");
println!("Creating order for user: {}", user_id);
let mutation = format!(
r#"
mutation {{
createOrder(
userId: "{}"
status: "pending"
total: 99.99
) {{
id
status
total
user {{
id
identifier
}}
}}
}}
"#,
user_id
);
let response = graphql_query(ORDERS_SUBGRAPH_URL, &mutation)
.await
.expect("Create order mutation should succeed");
if has_errors(&response) {
eprintln!("! Create order mutation has errors: {}", get_error_messages(&response));
} else {
let order = extract_data(&response)
.and_then(|d| d.get("createOrder"))
.expect("Should return created order");
let order_id = order.get("id").and_then(|id| id.as_str()).expect("Should have order ID");
println!("✓ Successfully created order with user reference: {}", order_id);
if let Some(user) = order.get("user") {
if let Some(resolved_id) = user.get("id").and_then(|id| id.as_str()) {
println!("✓ User reference resolved in order: {}", resolved_id);
}
}
}
}
#[tokio::test]
async fn test_extended_mutation_error_handling() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Error handling in extended mutations ---");
let mutation = r#"
mutation {
updateUser(
id: "00000000-0000-0000-0000-000000000000"
name: "Nonexistent"
) {
id
name
}
}
"#;
let response = graphql_query(USERS_SUBGRAPH_URL, mutation)
.await
.expect("Query should complete");
if has_errors(&response) {
let errors = get_error_messages(&response);
println!("✓ Expected error for non-existent user: {}", errors);
} else {
let result = extract_data(&response)
.and_then(|d| d.get("updateUser"))
.expect("Should return result");
if result.is_null() {
println!("✓ Non-existent user returned null (expected behavior)");
} else {
println!("⚠ Unexpected result for non-existent user: {:?}", result);
}
}
}
#[tokio::test]
async fn test_extended_mutation_data_consistency_after_mutation() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Data consistency after extended mutations ---");
let users_response =
graphql_query(USERS_SUBGRAPH_URL, r"query { users(limit: 1) { id name } }")
.await
.expect("Get users query should succeed");
let original_user = extract_data(&users_response)
.and_then(|d| d.get("users"))
.and_then(|u| u.as_array())
.and_then(|arr| arr.first())
.expect("Should have at least one user")
.clone();
let user_id = original_user.get("id").and_then(|id| id.as_str()).expect("Should have ID");
let original_name = original_user.get("name").and_then(|n| n.as_str()).unwrap_or("unknown");
println!("Original user: id={}, name={}", user_id, original_name);
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs());
let new_name = format!("Updated_{}", timestamp);
let mutation = format!(
r#"
mutation {{
updateUser(
id: "{}"
name: "{}"
) {{
id
name
}}
}}
"#,
user_id, new_name
);
let update_response = graphql_query(USERS_SUBGRAPH_URL, &mutation)
.await
.expect("Update mutation should succeed");
if !has_errors(&update_response) {
let query = format!(r#"query {{ user(id: "{}") {{ id name }} }}"#, user_id);
let verify_response = graphql_query(USERS_SUBGRAPH_URL, &query)
.await
.expect("Verification query should succeed");
let verified_user = extract_data(&verify_response)
.and_then(|d| d.get("user"))
.expect("Should return user");
let verified_name =
verified_user.get("name").and_then(|n| n.as_str()).expect("Should have name");
assert_eq!(verified_name, &new_name, "Updated name should persist after mutation");
println!("✓ Data consistency verified: mutation persisted correctly");
} else {
eprintln!("! Mutation failed: {}", get_error_messages(&update_response));
}
}
#[tokio::test]
async fn test_extended_mutation_through_gateway() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Mutation through gateway (federated mutation) ---");
let users_response = graphql_query(APOLLO_GATEWAY_URL, r"query { users(limit: 1) { id } }")
.await
.expect("Get users query should succeed");
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())
.expect("Should have user ID");
let mutation = format!(
r#"
mutation {{
updateUser(
id: "{}"
name: "Updated via Gateway"
) {{
id
name
}}
}}
"#,
user_id
);
let response = graphql_query(APOLLO_GATEWAY_URL, &mutation)
.await
.expect("Gateway mutation should complete");
if has_errors(&response) {
eprintln!("! Gateway mutation has errors: {}", get_error_messages(&response));
eprintln!("! This may be expected if mutations are not yet routed through gateway");
} else {
let result = extract_data(&response)
.and_then(|d| d.get("updateUser"))
.expect("Should return result");
println!("✓ Gateway mutation executed successfully: {:?}", result.get("name"));
}
}
#[tokio::test]
async fn test_composite_key_mutation_with_isolation() {
if std::env::var("FEDERATION_TESTS").is_err() {
eprintln!("Skipping: FEDERATION_TESTS not set");
return;
}
setup_federation_tests().await.expect("Setup should succeed");
println!("\n--- Test: Mutation with composite key tenant isolation ---");
let mutation = r#"
mutation {
createUser(
identifier: "composite_key_test@example.com"
name: "Composite Key Test"
email: "composite_key_test@example.com"
) {
id
identifier
}
}
"#;
let response = graphql_query(USERS_SUBGRAPH_URL, mutation)
.await
.expect("Mutation should succeed");
if !has_errors(&response) {
let user = extract_data(&response)
.and_then(|d| d.get("createUser"))
.expect("Should return created user");
let user_id = user.get("id").and_then(|id| id.as_str()).expect("Should have ID");
println!("✓ Created user with composite key structure: {}", user_id);
let query = format!(r#"query {{ user(id: "{}") {{ id identifier }} }}"#, user_id);
let verify_response =
graphql_query(USERS_SUBGRAPH_URL, &query).await.expect("Query should succeed");
if !has_errors(&verify_response) {
let verified = extract_data(&verify_response)
.and_then(|d| d.get("user"))
.expect("Should return user");
let verified_id =
verified.get("id").and_then(|id| id.as_str()).expect("Should have ID");
assert_eq!(verified_id, user_id, "IDs should match");
println!("✓ Mutation with composite key isolation validated");
}
} else {
eprintln!("! User creation failed: {}", get_error_messages(&response));
}
}