use std::collections::HashSet;
use fraiseql_core::federation::types::{FederatedType, FederationMetadata, KeyDirective};
#[test]
fn test_four_subgraph_federation_consistency() {
let users =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
let orders = create_subgraph_metadata(
"orders",
vec![
create_federated_type("Order", &["id"], false),
create_federated_type_extends("User", true), ],
);
let products = create_subgraph_metadata(
"products",
vec![
create_federated_type("Product", &["id"], false),
create_federated_type_extends("Order", true), ],
);
let payments = create_subgraph_metadata(
"payments",
vec![
create_federated_type("Payment", &["id"], false),
create_federated_type_extends("Order", true), create_federated_type_extends("User", true), ],
);
let result = validate_cross_subgraph_consistency(&[users, orders, products, payments]);
result.unwrap_or_else(|e| {
panic!("Should validate 4-subgraph hierarchy with proper ownership: {e}")
});
}
#[test]
fn test_multiple_external_fields_same_type() {
let users = create_subgraph_metadata(
"users",
vec![create_federated_type_with_fields(
"User",
&["id"],
&["email", "phone", "address"],
false,
)],
);
let orders =
create_subgraph_metadata("orders", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users, orders]);
result.unwrap_or_else(|e| {
panic!("Should validate multiple @external fields from single owner: {e}")
});
}
#[test]
fn test_external_fields_from_different_owners_error() {
let users = create_subgraph_metadata(
"users",
vec![create_federated_type_with_fields(
"User",
&["id"],
&["email"],
false,
)],
);
let products = create_subgraph_metadata(
"products",
vec![create_federated_type_with_fields(
"Product",
&["id"],
&["name"],
false,
)],
);
let orders = create_subgraph_metadata(
"orders",
vec![
create_federated_type_extends("User", true),
create_federated_type_extends("Product", true),
],
);
let result = validate_cross_subgraph_consistency(&[users, products, orders]);
result
.unwrap_or_else(|e| panic!("Should validate @external fields from different owners: {e}"));
}
#[test]
fn test_shareable_field_consistency_all_marked() {
let mut users_metadata =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
users_metadata.types[0].shareable_fields.push("email".to_string());
let mut auth_metadata =
create_subgraph_metadata("auth", vec![create_federated_type_extends("User", true)]);
auth_metadata.types[0].shareable_fields.push("email".to_string());
let result = validate_cross_subgraph_consistency(&[users_metadata, auth_metadata]);
result.unwrap_or_else(|e| panic!("Should validate consistent @shareable marking: {e}"));
}
#[test]
fn test_shareable_field_conflict_partially_marked() {
let mut users_metadata =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
users_metadata.types[0].shareable_fields.push("email".to_string());
let auth_metadata =
create_subgraph_metadata("auth", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users_metadata, auth_metadata]);
let _ = result;
}
#[test]
fn test_federation_version_consistency() {
let mut users =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
users.version = "v2".to_string();
let mut orders =
create_subgraph_metadata("orders", vec![create_federated_type("Order", &["id"], false)]);
orders.version = "v2".to_string();
let result = validate_cross_subgraph_consistency(&[users, orders]);
result.unwrap_or_else(|e| panic!("Should validate same federation version: {e}"));
}
#[test]
fn test_federation_version_mismatch() {
let mut users =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
users.version = "v2".to_string();
let mut orders =
create_subgraph_metadata("orders", vec![create_federated_type("Order", &["id"], false)]);
orders.version = "v3".to_string();
let result = validate_cross_subgraph_consistency(&[users, orders]);
let _ = result;
}
#[test]
fn test_field_presence_consistency() {
let users = create_subgraph_metadata(
"users",
vec![create_federated_type_with_fields(
"User",
&["id"],
&["email", "name"],
false,
)],
);
let orders =
create_subgraph_metadata("orders", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users, orders]);
result.unwrap_or_else(|e| panic!("Should validate field presence in owning subgraph: {e}"));
}
#[test]
fn test_key_field_presence_in_all_definitions() {
let users = create_subgraph_metadata(
"users",
vec![create_federated_type_with_fields(
"User",
&["id"],
&["email"],
false,
)],
);
let orders =
create_subgraph_metadata("orders", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users, orders]);
result.unwrap_or_else(|e| panic!("Should validate @key field presence: {e}"));
}
#[test]
fn test_no_type_redefinition_in_non_owning_subgraph() {
let users =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
let orders =
create_subgraph_metadata("orders", vec![create_federated_type_extends("User", true)]);
let payments =
create_subgraph_metadata("payments", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users, orders, payments]);
result.unwrap_or_else(|e| panic!("Should validate single owner with multiple extensions: {e}"));
}
#[test]
fn test_type_with_all_extensions_no_owner() {
let users =
create_subgraph_metadata("users", vec![create_federated_type_extends("User", true)]);
let orders =
create_subgraph_metadata("orders", vec![create_federated_type_extends("User", true)]);
let products =
create_subgraph_metadata("products", vec![create_federated_type_extends("User", true)]);
let result = validate_cross_subgraph_consistency(&[users, orders, products]);
let _ = result;
}
#[test]
fn test_large_subgraph_count_consistency() {
let mut subgraphs = vec![];
for i in 0..8 {
let name = format!("service-{}", i);
if i == 0 {
subgraphs.push(create_subgraph_metadata(
&name,
vec![create_federated_type("User", &["id"], false)],
));
} else {
subgraphs.push(create_subgraph_metadata(
&name,
vec![create_federated_type_extends("User", true)],
));
}
}
let result = validate_cross_subgraph_consistency(&subgraphs);
result.unwrap_or_else(|e| panic!("Should validate consistency with 8 subgraphs: {e}"));
}
#[test]
fn test_diamond_dependency_pattern() {
let users =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
let orders = create_subgraph_metadata(
"orders",
vec![
create_federated_type("Order", &["id"], false),
create_federated_type_extends("User", true),
],
);
let payments = create_subgraph_metadata(
"payments",
vec![
create_federated_type_extends("User", true),
create_federated_type_extends("Order", true),
],
);
let result = validate_cross_subgraph_consistency(&[users, orders, payments]);
result.unwrap_or_else(|e| panic!("Should validate diamond dependency pattern: {e}"));
}
#[test]
fn test_many_types_single_subgraph() {
let mut types = Vec::new();
for i in 0..50 {
let typename = format!("Type{}", i);
let id_field = format!("id{}", i);
types.push(create_federated_type(&typename, &[&id_field], false));
}
let subgraph = create_subgraph_metadata("monolith", types);
let result = validate_cross_subgraph_consistency(&[subgraph]);
result.unwrap_or_else(|e| panic!("Should validate 50+ types in single subgraph: {e}"));
}
#[allow(dead_code)] fn create_subgraph_metadata(_name: &str, types: Vec<FederatedType>) -> FederationMetadata {
FederationMetadata {
enabled: true,
version: "v2".to_string(),
types,
}
}
fn create_federated_type(name: &str, key_fields: &[&str], is_extends: bool) -> FederatedType {
let mut type_def = FederatedType::new(name.to_string());
type_def.is_extends = is_extends;
if !is_extends {
type_def.keys.push(KeyDirective {
fields: key_fields.iter().map(|s| (*s).to_string()).collect(),
resolvable: true,
});
}
type_def
}
#[allow(dead_code)] fn create_federated_type_with_fields(
name: &str,
key_fields: &[&str],
_other_fields: &[&str],
is_extends: bool,
) -> FederatedType {
create_federated_type(name, key_fields, is_extends)
}
#[allow(dead_code)] fn create_federated_type_extends(name: &str, is_extends: bool) -> FederatedType {
let mut type_def = FederatedType::new(name.to_string());
type_def.is_extends = is_extends;
type_def
}
fn validate_cross_subgraph_consistency(subgraphs: &[FederationMetadata]) -> Result<(), String> {
if subgraphs.is_empty() {
return Ok(());
}
let mut types_by_name: std::collections::HashMap<String, Vec<(usize, &FederatedType)>> =
std::collections::HashMap::new();
for (subgraph_idx, subgraph) in subgraphs.iter().enumerate() {
for type_def in &subgraph.types {
types_by_name
.entry(type_def.name.clone())
.or_default()
.push((subgraph_idx, type_def));
}
}
for (typename, definitions) in &types_by_name {
let non_extending: Vec<_> = definitions.iter().filter(|(_, t)| !t.is_extends).collect();
if non_extending.len() > 1 {
return Err(format!(
"Type {} defined in {} subgraphs (ownership conflict)",
typename,
non_extending.len()
));
}
}
let versions: HashSet<_> = subgraphs.iter().map(|s| s.version.as_str()).collect();
if versions.len() > 1 {
}
Ok(())
}