use super::*;
#[test]
fn gts_type_path_valid_cases() {
let valid = vec![
("gts.cf.core.rg.type.v1~", "gts.cf.core.rg.type.v1~"),
("GTS.CF.CORE.RG.TYPE.V1~", "gts.cf.core.rg.type.v1~"),
(" GTS.CF.CORE.RG.TYPE.V1~ ", "gts.cf.core.rg.type.v1~"),
(
"gts.cf.core.rg.type.v1~x.test.unit.root.v1~",
"gts.cf.core.rg.type.v1~x.test.unit.root.v1~",
),
("gts.cf.core.rg.type.v2~", "gts.cf.core.rg.type.v2~"),
("gts.cf.core.rg.my_type.v1~", "gts.cf.core.rg.my_type.v1~"),
];
for (input, expected) in valid {
let path = GtsTypePath::new(input);
assert!(path.is_ok(), "should be valid: {input}");
assert_eq!(path.unwrap().as_str(), expected, "for input: {input}");
}
}
#[test]
fn gts_type_path_invalid_cases() {
let cases = vec![
("", "must not be empty"),
("invalid.path~", "Invalid GTS type path format"),
("gts.cf.core.rg.type.v1", "Invalid GTS type path format"),
("gts.cf.core.rg.type.v1~~", "Invalid GTS type path format"),
(
"gts.cf.core.rg.type.v1~hello-world~",
"Invalid GTS type path format",
),
("gts.~", "Invalid GTS type path format"),
(" ", "must not be empty"),
];
for (input, expected_msg) in cases {
let result = GtsTypePath::new(input);
assert!(result.is_err(), "should be invalid: '{input}'");
let err = result.unwrap_err();
assert!(
err.contains(expected_msg),
"for input '{input}': expected '{expected_msg}' in error, got: {err}"
);
}
}
#[test]
#[allow(unknown_lints, de0901_gts_string_pattern)]
fn gts_type_path_max_length_boundary() {
let name = "a".repeat(1005);
let path_1024 = format!("gts.cf.core.rg.{name}.v1~");
assert_eq!(path_1024.len(), 1024);
assert!(
GtsTypePath::new(&path_1024).is_ok(),
"exactly 1024 chars should be valid: {:?}",
GtsTypePath::new(&path_1024)
);
let name_long = "a".repeat(1006);
let path_1025 = format!("gts.cf.core.rg.{name_long}.v1~");
assert_eq!(path_1025.len(), 1025);
let result = GtsTypePath::new(&path_1025);
assert!(result.is_err(), "1025 chars should exceed max length");
assert!(result.unwrap_err().contains("exceeds maximum length"));
}
#[test]
fn gts_type_path_serde_round_trip() {
let original = GtsTypePath::new("gts.cf.core.rg.type.v1~").unwrap();
let json = serde_json::to_string(&original).unwrap();
let deserialized: GtsTypePath = serde_json::from_str(&json).unwrap();
assert_eq!(original, deserialized);
}
#[test]
fn gts_type_path_serde_invalid_rejects() {
let result = serde_json::from_str::<GtsTypePath>("\"invalid\"");
assert!(result.is_err(), "invalid path should fail deserialization");
}
#[test]
fn gts_type_path_display_and_into_string() {
let path = GtsTypePath::new("gts.cf.core.rg.type.v1~").unwrap();
let display = path.to_string();
let into_string: String = path.into();
assert_eq!(display, into_string);
}
#[test]
fn resource_group_type_camel_case_keys() {
let rgt = ResourceGroupType {
code: "gts.cf.core.rg.type.v1~".to_owned(),
can_be_root: true,
allowed_parent_types: vec!["gts.parent~".to_owned()],
allowed_membership_types: vec!["gts.member~".to_owned()],
metadata_schema: None,
};
let json = serde_json::to_value(&rgt).unwrap();
assert!(
json.get("canBeRoot").is_some(),
"expected camelCase 'canBeRoot'"
);
assert!(
json.get("allowedParentTypes").is_some(),
"expected camelCase 'allowedParentTypes'"
);
assert!(
json.get("allowedMembershipTypes").is_some(),
"expected camelCase 'allowedMembershipTypes'"
);
assert!(
json.get("metadataSchema").is_none(),
"metadataSchema should be absent when None"
);
}
#[test]
fn resource_group_type_field_renamed() {
let group = ResourceGroup {
id: Uuid::nil(),
code: "gts.cf.core.rg.type.v1~".to_owned(),
name: "Test".to_owned(),
hierarchy: GroupHierarchy {
parent_id: None,
tenant_id: Uuid::nil(),
},
metadata: Some(serde_json::json!({"key": "val"})),
};
let json = serde_json::to_value(&group).unwrap();
assert!(
json.get("type").is_some(),
"expected 'type' key on the wire"
);
assert!(
json.get("code").is_none(),
"'code' (Rust field name) must not leak to JSON -- serde renames it to 'type'"
);
}
#[test]
fn resource_group_metadata_absent_when_none() {
let group = ResourceGroup {
id: Uuid::nil(),
code: "gts.cf.core.rg.type.v1~".to_owned(),
name: "Test".to_owned(),
hierarchy: GroupHierarchy {
parent_id: None,
tenant_id: Uuid::nil(),
},
metadata: None,
};
let json = serde_json::to_value(&group).unwrap();
assert!(
json.get("metadata").is_none(),
"metadata should be absent when None, got: {json}"
);
}
#[test]
fn gts_type_path_trims_and_lowercases() {
let input = " GTS.CF.CORE.RG.TYPE.V1~ ";
let path = GtsTypePath::new(input);
assert!(
path.is_ok(),
"GtsTypePath::new should accept trimmed/lowered input"
);
assert_eq!(path.unwrap().as_str(), "gts.cf.core.rg.type.v1~");
}