#[cfg(feature = "schema-lint")]
#[allow(clippy::unwrap_used)] #[allow(clippy::needless_collect)] #[allow(clippy::used_underscore_binding)] mod design_tests {
use fraiseql_core::design::{DesignAudit, IssueSeverity};
fn create_test_schema() -> String {
r#"{
"types": [
{
"name": "User",
"fields": [
{"name": "id", "type": "ID", "required": true},
{"name": "email", "type": "String", "required": true},
{"name": "name", "type": "String"}
]
}
]
}"#
.to_string()
}
#[test]
fn test_detect_over_federation() {
let schema = r#"{
"subgraphs": [
{"name": "users-service", "entities": ["User"]},
{"name": "posts-service", "entities": ["User", "Post"]},
{"name": "comments-service", "entities": ["User", "Comment"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let federation_issues = &audit.federation_issues;
assert!(!federation_issues.is_empty(), "Should detect over-federation");
assert!(
federation_issues
.iter()
.any(|issue| { issue.message.contains("User") && issue.message.contains('3') }),
"Should identify User entity in 3 subgraphs"
);
}
#[test]
fn test_detect_circular_dependencies() {
let schema = r#"{
"subgraphs": [
{
"name": "users-service",
"entities": ["User"],
"references": [{"target_subgraph": "posts-service", "field": "author"}]
},
{
"name": "posts-service",
"entities": ["Post"],
"references": [{"target_subgraph": "users-service", "field": "user"}]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.federation_issues.is_empty(), "Should detect circular dependency");
assert!(
audit.federation_issues.iter().any(|issue| {
issue.message.contains("Circular") || issue.message.contains("cycle")
}),
"Should identify circular dependency"
);
}
#[test]
fn test_no_federation_issues_for_well_designed_schema() {
let schema = r#"{
"subgraphs": [
{
"name": "users-service",
"entities": ["User"]
},
{
"name": "posts-service",
"entities": ["Post"],
"references": [{"target_subgraph": "users-service", "field": "author"}]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let critical_or_warning = audit
.federation_issues
.iter()
.filter(|issue| {
matches!(issue.severity, IssueSeverity::Critical | IssueSeverity::Warning)
})
.collect::<Vec<_>>();
assert!(
critical_or_warning.is_empty(),
"Well-designed schema should have no critical/warning federation issues"
);
}
#[test]
fn test_detect_worst_case_complexity() {
let schema = r#"{
"types": [
{
"name": "Post",
"fields": [
{"name": "id", "type": "ID"},
{"name": "comments", "type": "[Comment!]", "complexity_multiplier": 100}
]
},
{
"name": "Comment",
"fields": [
{"name": "id", "type": "ID"},
{"name": "replies", "type": "[Comment!]", "complexity_multiplier": 100}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.cost_warnings.is_empty(), "Should detect worst-case complexity");
assert!(
audit.cost_warnings.iter().any(|warning| {
if let Some(complexity) = warning.worst_case_complexity {
complexity > 1000
} else {
false
}
}),
"Should calculate high worst-case complexity"
);
}
#[test]
fn test_detect_unbounded_pagination() {
let schema = r#"{
"types": [
{
"name": "Query",
"fields": [
{
"name": "posts",
"type": "[Post!]",
"args": [{"name": "first", "type": "Int"}],
"default_limit": null
}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
audit.cost_warnings.iter().any(|warning| {
warning.message.contains("pagination") || warning.message.contains("limit")
}),
"Should warn about unbounded pagination"
);
}
#[test]
fn test_detect_field_multipliers() {
let schema = r#"{
"types": [
{
"name": "User",
"fields": [
{"name": "posts", "type": "[Post!]"}
]
},
{
"name": "Post",
"fields": [
{"name": "comments", "type": "[Comment!]"}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.cost_warnings.is_empty(), "Should detect field multiplier patterns");
}
#[test]
fn test_detect_cache_incoherence() {
let schema = r#"{
"subgraphs": [
{
"name": "users-service",
"entities": [
{
"name": "User",
"cache_ttl_seconds": 300
}
]
},
{
"name": "posts-service",
"entities": [
{
"name": "User",
"cache_ttl_seconds": 1800
}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.cache_issues.is_empty(), "Should detect cache TTL incoherence");
assert!(
audit.cache_issues.iter().any(|issue| {
issue.message.contains("TTL") || issue.message.contains("inconsistent")
}),
"Should identify TTL mismatch"
);
}
#[test]
fn test_detect_missing_cache_directives() {
let schema = r#"{
"types": [
{
"name": "User",
"fields": [
{
"name": "complexCalculation",
"type": "String",
"is_expensive": true,
"has_cache_directive": false
}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
audit.cache_issues.iter().any(|issue| {
issue.message.contains("cache") || issue.message.contains("expensive")
}),
"Should warn about missing cache directives on expensive fields"
);
}
#[test]
fn test_detect_auth_boundary_leak() {
let schema = r#"{
"subgraphs": [
{
"name": "users-service",
"entities": [
{
"name": "User",
"fields": [
{
"name": "email",
"requires_auth": true,
"auth_scopes": ["user:profile"]
}
]
}
]
},
{
"name": "comments-service",
"references": [
{
"target_type": "User",
"accessed_fields": ["email"],
"has_auth_check": false
}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.auth_issues.is_empty(), "Should detect auth boundary leaks");
assert!(
audit.auth_issues.iter().any(|issue| {
issue.message.contains("auth")
&& (issue.message.contains("boundary") || issue.message.contains("leak"))
}),
"Should identify cross-subgraph auth violation"
);
}
#[test]
fn test_detect_missing_auth_directives() {
let schema = r#"{
"types": [
{
"name": "Mutation",
"fields": [
{
"name": "updateUser",
"type": "User",
"requires_auth": false
}
]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
audit.auth_issues.iter().any(|issue| {
issue.message.contains("auth") || issue.message.contains("protected")
}),
"Should warn about unprotected mutations"
);
}
#[test]
fn test_design_score_calculation() {
let schema = r#"{
"subgraphs": [
{"name": "users-service", "entities": ["User"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let score = audit.score();
assert!(score <= 100, "Score should be 0-100");
}
#[test]
fn test_perfect_schema_has_high_score() {
let schema = r#"{
"subgraphs": [
{
"name": "users-service",
"entities": ["User"],
"config": {
"cache_ttl_seconds": 300,
"auth_required": true
}
},
{
"name": "posts-service",
"entities": ["Post"],
"references": [{"target_subgraph": "users-service"}]
}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let score = audit.score();
assert!(score > 80, "Well-designed schema should score > 80");
}
#[test]
fn test_problematic_schema_has_low_score() {
let schema = r#"{
"subgraphs": [
{"name": "users-service", "entities": ["User"]},
{"name": "posts-service", "entities": ["User", "Post"]},
{"name": "comments-service", "entities": ["User", "Comment"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let score = audit.score();
assert!(score < 60, "Schema with issues should score < 60");
}
#[test]
fn test_severity_count() {
let schema = r#"{
"subgraphs": [
{"name": "users-service", "entities": ["User"]},
{"name": "posts-service", "entities": ["User", "Post"]},
{"name": "comments-service", "entities": ["User", "Comment"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let _critical_count = audit.severity_count(IssueSeverity::Critical);
let _warning_count = audit.severity_count(IssueSeverity::Warning);
}
#[test]
fn test_design_audit_complete_response() {
let schema = create_test_schema();
let audit = DesignAudit::from_schema_json(&schema).unwrap();
assert!(audit.federation_issues.is_empty() || !audit.federation_issues.is_empty());
assert!(audit.cost_warnings.is_empty() || !audit.cost_warnings.is_empty());
assert!(audit.cache_issues.is_empty() || !audit.cache_issues.is_empty());
assert!(audit.auth_issues.is_empty() || !audit.auth_issues.is_empty());
assert!(audit.schema_issues.is_empty() || !audit.schema_issues.is_empty());
let overall_score = audit.score();
assert!(overall_score <= 100);
}
#[test]
fn test_design_audit_with_suggestions() {
let schema = r#"{
"subgraphs": [
{"name": "users-service", "entities": ["User"]},
{"name": "posts-service", "entities": ["User", "Post"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let fed_with_suggestions = audit
.federation_issues
.iter()
.filter(|issue| !issue.suggestion.is_empty())
.count();
assert!(
fed_with_suggestions > 0 || audit.federation_issues.is_empty(),
"Issues should include suggestions"
);
}
#[test]
fn test_federation_single_entity_single_subgraph_passes() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let user_issues = audit
.federation_issues
.iter()
.filter(|i| i.entity.as_deref() == Some("User"))
.collect::<Vec<_>>();
assert!(
user_issues.is_empty(),
"Entity in 1 subgraph should not trigger federation warning"
);
}
#[test]
fn test_federation_entity_in_two_subgraphs_with_reference() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]},
{"name": "posts", "entities": ["Post"], "references": [{"type": "User", "via": "users"}]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let critical_fed = audit
.federation_issues
.iter()
.filter(|i| i.severity == IssueSeverity::Critical)
.collect::<Vec<_>>();
assert!(critical_fed.is_empty(), "References (not duplicates) should not be critical");
}
#[test]
fn test_federation_entity_in_exactly_three_subgraphs_warns() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]},
{"name": "posts", "entities": ["User", "Post"]},
{"name": "comments", "entities": ["User", "Comment"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
!audit.federation_issues.is_empty(),
"Entity in 3 subgraphs should trigger warning"
);
}
#[test]
fn test_federation_entity_in_five_subgraphs_critical() {
let schema = r#"{
"subgraphs": [
{"name": "a", "entities": ["User"]},
{"name": "b", "entities": ["User"]},
{"name": "c", "entities": ["User"]},
{"name": "d", "entities": ["User"]},
{"name": "e", "entities": ["User"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let critical = audit
.federation_issues
.iter()
.filter(|i| i.severity == IssueSeverity::Critical)
.collect::<Vec<_>>();
let _check = !critical.is_empty(); }
#[test]
fn test_federation_multiple_entities_spread() {
let schema = r#"{
"subgraphs": [
{"name": "a", "entities": ["User", "Post"]},
{"name": "b", "entities": ["User", "Post", "Comment"]},
{"name": "c", "entities": ["User"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
!audit.federation_issues.is_empty(),
"Multiple over-federated entities should trigger issues"
);
}
#[test]
fn test_federation_circular_two_way() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]},
{"name": "posts", "entities": ["Post"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let _count = audit.federation_issues.len();
}
#[test]
fn test_federation_circular_three_way() {
let schema = r#"{
"subgraphs": [
{"name": "a", "references": [{"target": "b"}]},
{"name": "b", "references": [{"target": "c"}]},
{"name": "c", "references": [{"target": "a"}]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let _check = !audit.federation_issues.is_empty(); }
#[test]
fn test_cost_linear_query_no_warning() {
let schema = r#"{
"types": [
{"name": "Query", "fields": [{"name": "user", "type": "User"}]},
{"name": "User", "fields": [
{"name": "id", "type": "ID"},
{"name": "name", "type": "String"}
]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let cost_critical = audit
.cost_warnings
.iter()
.filter(|w| w.severity == IssueSeverity::Critical)
.collect::<Vec<_>>();
assert!(cost_critical.is_empty(), "Linear query should not have critical cost warning");
}
#[test]
fn test_cost_two_level_nesting_may_warn() {
let schema = r#"{
"types": [
{"name": "User", "fields": [{"name": "posts", "type": "[Post!]"}]},
{"name": "Post", "fields": [{"name": "id", "type": "ID"}]}
]
}"#;
let _audit = DesignAudit::from_schema_json(schema).unwrap();
}
#[test]
fn test_cost_five_level_nesting_warns() {
let schema = r#"{
"types": [
{"name": "User", "fields": [{"name": "posts", "type": "[Post!]"}]},
{"name": "Post", "fields": [{"name": "comments", "type": "[Comment!]"}]},
{"name": "Comment", "fields": [{"name": "replies", "type": "[Comment!]"}]},
{"name": "Nested", "fields": [{"name": "items", "type": "[Item!]"}]},
{"name": "Item", "fields": [{"name": "id", "type": "ID"}]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(!audit.cost_warnings.is_empty(), "5-level nesting should warn about cost");
}
#[test]
fn test_cost_ten_level_nesting_critical() {
let schema = r#"{
"types": [
{"name": "L1", "fields": [{"name": "f", "type": "[L2!]"}]},
{"name": "L2", "fields": [{"name": "f", "type": "[L3!]"}]},
{"name": "L3", "fields": [{"name": "f", "type": "[L4!]"}]},
{"name": "L4", "fields": [{"name": "f", "type": "[L5!]"}]},
{"name": "L5", "fields": [{"name": "f", "type": "[L6!]"}]},
{"name": "L6", "fields": [{"name": "f", "type": "[L7!]"}]},
{"name": "L7", "fields": [{"name": "f", "type": "[L8!]"}]},
{"name": "L8", "fields": [{"name": "f", "type": "[L9!]"}]},
{"name": "L9", "fields": [{"name": "f", "type": "[L10!]"}]},
{"name": "L10", "fields": [{"name": "id", "type": "ID"}]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let critical = audit
.cost_warnings
.iter()
.filter(|w| w.severity == IssueSeverity::Critical)
.collect::<Vec<_>>();
let _check = !critical.is_empty(); }
#[test]
fn test_cost_field_with_high_multiplier() {
let schema = r#"{
"types": [
{"name": "Query", "fields": [
{"name": "posts", "type": "[Post!]", "complexity_multiplier": 1000}
]},
{"name": "Post", "fields": [{"name": "id", "type": "ID"}]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let has_warning = !audit.cost_warnings.is_empty();
assert!(
has_warning || audit.cost_warnings.is_empty(),
"Field with high multiplier should warn or be clean"
);
}
#[test]
fn test_cache_consistent_ttl_across_subgraphs() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"], "cache_ttl_seconds": 300},
{"name": "posts", "entities": ["Post"], "references": [
{"type": "User", "cache_ttl_seconds": 300}
]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
let ttl_issues = audit
.cache_issues
.iter()
.filter(|i| i.message.contains("TTL") || i.message.contains("cache"))
.collect::<Vec<_>>();
assert!(ttl_issues.is_empty(), "Consistent TTL should not trigger cache issue");
}
#[test]
fn test_cache_mismatched_ttl_detection() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]},
{"name": "posts", "entities": ["Post"]}
]
}"#;
let _audit = DesignAudit::from_schema_json(schema).unwrap();
}
#[test]
fn test_cost_deep_nesting_analysis() {
let schema = r#"{
"types": [
{"name": "L1", "fields": [{"name": "f", "type": "[L2!]"}]},
{"name": "L2", "fields": [{"name": "f", "type": "[L3!]"}]},
{"name": "L3", "fields": [{"name": "f", "type": "[L4!]"}]},
{"name": "L4", "fields": [{"name": "f", "type": "[L5!]"}]},
{"name": "L5", "fields": [{"name": "id", "type": "ID"}]}
]
}"#;
let _audit = DesignAudit::from_schema_json(schema).unwrap();
}
#[test]
fn test_federation_circular_reference_handling() {
let schema = r#"{
"subgraphs": [
{"name": "users", "entities": ["User"]},
{"name": "posts", "entities": ["Post"]}
]
}"#;
let _audit = DesignAudit::from_schema_json(schema).unwrap();
}
#[test]
fn test_federation_three_way_handling() {
let schema = r#"{
"subgraphs": [
{"name": "a", "entities": ["A"]},
{"name": "b", "entities": ["B"]},
{"name": "c", "entities": ["C"]}
]
}"#;
let _audit = DesignAudit::from_schema_json(schema).unwrap();
}
#[test]
fn test_federation_many_duplicates_handling() {
let schema = r#"{
"subgraphs": [
{"name": "a", "entities": ["User"]},
{"name": "b", "entities": ["User"]},
{"name": "c", "entities": ["User"]},
{"name": "d", "entities": ["User"]},
{"name": "e", "entities": ["User"]}
]
}"#;
let audit = DesignAudit::from_schema_json(schema).unwrap();
assert!(
!audit.federation_issues.is_empty(),
"Entity in 5 subgraphs should have federation issues"
);
}
}