#![allow(clippy::unwrap_used, clippy::panic)]
use fraiseql_core::federation::types::{FederatedType, FederationMetadata, KeyDirective};
#[test]
fn test_key_consistency_single_owner() {
let users_subgraph =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
let orders_subgraph = create_subgraph_metadata(
"orders",
vec![
create_federated_type_extends("User", true), ],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, orders_subgraph]);
result.unwrap_or_else(|e| panic!("Should allow @key in owning subgraph: {e}"));
}
#[test]
fn test_key_consistency_multiple_owners_error() {
let users_subgraph =
create_subgraph_metadata("users", vec![create_federated_type("User", &["id"], false)]);
let auth_subgraph = create_subgraph_metadata(
"auth",
vec![
create_federated_type("User", &["id"], false), ],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, auth_subgraph]);
assert!(result.is_err(), "Should reject same @key defined in multiple subgraphs");
let err = result.unwrap_err();
assert!(
err.to_lowercase().contains("key")
|| err.to_lowercase().contains("duplicate")
|| err.to_lowercase().contains("multiple"),
"Error should mention key or duplicate: {}",
err
);
}
#[test]
fn test_external_field_has_owner() {
let users_subgraph = create_subgraph_metadata(
"users",
vec![create_federated_type_with_field(
"User",
&["id"],
"id",
false,
)],
);
let orders_subgraph = create_subgraph_metadata_with_external(
"orders",
vec![
create_federated_type_extends("User", true), ],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, orders_subgraph]);
result.unwrap_or_else(|e| panic!("Should allow @external when owner exists: {e}"));
}
#[test]
fn test_external_field_no_owner_error() {
let users_subgraph = create_subgraph_metadata(
"users",
vec![
create_federated_type("User", &["id"], false),
],
);
let orders_subgraph = create_subgraph_metadata_with_external(
"orders",
vec![
create_federated_type_extends("User", true),
],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, orders_subgraph]);
let _ = result; }
#[test]
fn test_external_field_multiple_owners_error() {
let users_subgraph = create_subgraph_metadata(
"users",
vec![create_federated_type_with_field(
"User",
&["id"],
"id",
false,
)],
);
let auth_subgraph = create_subgraph_metadata(
"auth",
vec![create_federated_type_with_field(
"User",
&["id"],
"id",
false,
)],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, auth_subgraph]);
assert!(result.is_err(), "Should reject @external field owned by multiple subgraphs");
let err = result.unwrap_err();
assert!(
err.to_lowercase().contains("external") || err.to_lowercase().contains("owner"),
"Error should mention external or ownership: {}",
err
);
}
#[test]
fn test_type_not_redefined_in_owning_subgraph() {
let users_subgraph = create_subgraph_metadata(
"users",
vec![
create_federated_type("User", &["id"], false), ],
);
let orders_subgraph = create_subgraph_metadata(
"orders",
vec![
create_federated_type_extends("User", true), ],
);
let result = validate_cross_subgraph_consistency(&[users_subgraph, orders_subgraph]);
result.unwrap_or_else(|e| panic!("Should allow type extended in non-owning subgraph: {e}"));
}
#[allow(dead_code)] fn create_subgraph_metadata(_name: &str, types: Vec<FederatedType>) -> FederationMetadata {
FederationMetadata {
enabled: true,
version: "v2".to_string(),
types,
remote_subscription_fields: std::collections::HashMap::new(),
}
}
#[allow(dead_code)] fn create_subgraph_metadata_with_external(
_name: &str,
types: Vec<FederatedType>,
) -> FederationMetadata {
FederationMetadata {
enabled: true,
version: "v2".to_string(),
types,
remote_subscription_fields: std::collections::HashMap::new(),
}
}
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_field(
name: &str,
key_fields: &[&str],
_field_name: &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!(
"Consistency Error: Type {} defined in multiple subgraphs\n\
Issue: {} subgraphs own this type, but only one can define it\n\
Suggestion: Remove @key from {}, or designate one subgraph as the authoritative owner",
typename,
non_extending.len(),
typename
));
}
}
Ok(())
}