#![allow(clippy::unwrap_used)] #![allow(clippy::cast_precision_loss)] #![allow(clippy::cast_sign_loss)] #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_possible_wrap)] #![allow(clippy::cast_lossless)] #![allow(clippy::missing_panics_doc)] #![allow(clippy::missing_errors_doc)] #![allow(missing_docs)] #![allow(clippy::items_after_statements)] #![allow(clippy::used_underscore_binding)] #![allow(clippy::needless_pass_by_value)]
use fraiseql_server::routes::api::design::DesignAuditRequest;
use serde_json::json;
#[test]
fn test_design_audit_rejects_extremely_large_schema() {
let mut large_types = vec![];
for i in 0..10000 {
large_types.push(format!(
r#"{{"name": "Type{}", "fields": [{{"name": "id", "type": "ID"}}]}}"#,
i
));
}
let large_schema = format!(r#"{{"types": [{}]}}"#, large_types.join(","));
let parsed: serde_json::Value = serde_json::from_str(&large_schema).unwrap();
let req = DesignAuditRequest { schema: parsed };
let types = req.schema.get("types").unwrap().as_array().unwrap();
assert_eq!(types.len(), 10000, "All 10,000 types should be parsed");
assert_eq!(types[0]["name"], "Type0");
assert_eq!(types[9999]["name"], "Type9999");
}
#[test]
fn test_design_audit_handles_malformed_json() {
let malformed_schema = r#"{"types": [{"name": "User", malformed}]}"#;
let result = serde_json::from_str::<serde_json::Value>(malformed_schema);
assert!(result.is_err(), "Malformed JSON should fail to parse");
}
#[test]
fn test_design_audit_handles_null_schema() {
let req = DesignAuditRequest {
schema: json!(null),
};
assert!(req.schema.is_null(), "Null schema should remain null");
assert!(req.schema.get("types").is_none(), "Null schema has no types");
}
#[test]
fn test_design_audit_handles_recursive_structures() {
let schema = json!({
"types": [
{"name": "User", "fields": [{"ref": "self"}]}
]
});
let req = DesignAuditRequest { schema };
let fields = req.schema["types"][0]["fields"].as_array().unwrap();
assert_eq!(fields.len(), 1);
assert_eq!(fields[0]["ref"], "self", "Self-reference should be preserved");
}
#[test]
fn test_design_audit_handles_deeply_nested_json() {
let mut nested = String::from(r#"{"value""#);
for _ in 0..128 {
nested.push_str(r#": {"value""#);
}
for _ in 0..128 {
nested.push('}');
}
nested.push('}');
let result = serde_json::from_str::<serde_json::Value>(&nested);
assert!(
result.is_err(),
"Deeply nested JSON (128+ levels) should hit serde_json's recursion limit"
);
}
#[test]
fn test_design_audit_rejects_unicode_injection() {
let schema = json!({
"types": [{
"name": "User🔓",
"fields": [{"name": "id\u{0000}", "type": "ID"}]
}]
});
let req = DesignAuditRequest { schema };
let type_name = req.schema["types"][0]["name"].as_str().unwrap();
assert!(type_name.contains('🔓'), "Unicode should be preserved, not stripped");
let field_name = req.schema["types"][0]["fields"][0]["name"].as_str().unwrap();
assert!(field_name.contains('\0'), "Null bytes should be preserved in JSON strings");
}
#[test]
fn test_design_audit_error_messages_dont_leak_paths() {
let schema = json!({"types": []});
let req = DesignAuditRequest { schema };
let json_str = serde_json::to_string(&req.schema).unwrap();
assert!(
!json_str.contains("/home") && !json_str.contains("/etc") && !json_str.contains("C:\\"),
"Serialized schema should not contain filesystem paths"
);
}
#[test]
fn test_design_audit_sanitizes_schema_names() {
let schema = json!({
"types": [{
"name": "../../../etc/passwd",
"fields": []
}]
});
let req = DesignAuditRequest { schema };
let name = req.schema["types"][0]["name"].as_str().unwrap();
assert_eq!(name, "../../../etc/passwd", "Path traversal preserved as inert string data");
}
#[test]
fn test_design_audit_extra_fields_ignored_by_struct() {
let schema = json!({
"types": [],
"extra1": "value",
"extra2": {"nested": "data"},
"extra3": [1, 2, 3]
});
let req = DesignAuditRequest { schema };
assert_eq!(req.schema["types"].as_array().unwrap().len(), 0);
assert_eq!(req.schema["extra1"], "value", "Extra fields preserved in raw JSON");
assert!(req.schema.get("types").is_some());
}
#[test]
fn test_design_audit_auth_fields_survive_roundtrip() {
let schema = json!({
"types": [{
"name": "Admin",
"fields": [
{"name": "secret", "type": "String", "requires_auth": true, "required_role": "admin"}
]
}]
});
let req = DesignAuditRequest { schema };
let field = &req.schema["types"][0]["fields"][0];
assert_eq!(field["requires_auth"], true, "Auth requirement must survive");
assert_eq!(field["required_role"], "admin", "Role requirement must survive");
let serialized = serde_json::to_string(&req.schema).unwrap();
let deserialized: serde_json::Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(
deserialized["types"][0]["fields"][0]["required_role"], "admin",
"Auth metadata must survive serialization roundtrip"
);
}
#[test]
fn test_design_audit_recovers_from_invalid_type_field() {
let schema = json!({
"types": [{
"name": "User",
"fields": [{"name": "id", "type": 123}]
}]
});
let req = DesignAuditRequest { schema };
let type_value = &req.schema["types"][0]["fields"][0]["type"];
assert!(type_value.is_number(), "Invalid type field should be preserved as-is");
assert_eq!(type_value.as_i64().unwrap(), 123);
}
#[test]
fn test_design_audit_request_is_serializable() {
let schema = json!({
"types": [{"name": "User", "fields": []}]
});
let req = DesignAuditRequest { schema };
let json_str = serde_json::to_string(&req.schema).unwrap();
assert!(!json_str.is_empty(), "Serialized schema should not be empty");
assert!(json_str.contains("\"types\""), "Should contain 'types' field");
let reparsed: serde_json::Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(reparsed["types"][0]["name"], "User");
}