use tsafe_cli::op_mapping::op_field_label_to_key;
#[test]
fn spaces_become_underscores_and_label_is_uppercased() {
assert_eq!(op_field_label_to_key("My Secret"), "MY_SECRET");
}
#[test]
fn hyphens_become_underscores_and_label_is_uppercased() {
assert_eq!(op_field_label_to_key("db-password"), "DB_PASSWORD");
}
#[test]
fn already_screaming_snake_is_unchanged() {
assert_eq!(op_field_label_to_key("API_KEY"), "API_KEY");
}
#[test]
fn mixed_spaces_and_hyphens_both_become_underscores() {
assert_eq!(op_field_label_to_key("my-API key"), "MY_API_KEY");
}
#[test]
fn no_item_name_prefix_is_added() {
assert_eq!(op_field_label_to_key("password"), "PASSWORD");
assert_eq!(op_field_label_to_key("username"), "USERNAME");
}
fn candidates_collide(labels: &[&str]) -> Option<Vec<String>> {
use std::collections::HashMap;
let candidates: Vec<String> = labels.iter().map(|l| op_field_label_to_key(l)).collect();
let mut seen: HashMap<&str, usize> = HashMap::new();
for key in &candidates {
*seen.entry(key.as_str()).or_insert(0) += 1;
}
let mut colliding: Vec<String> = seen
.iter()
.filter(|(_, &count)| count > 1)
.map(|(key, _)| key.to_string())
.collect();
if colliding.is_empty() {
None
} else {
colliding.sort_unstable();
Some(colliding)
}
}
#[test]
fn two_labels_with_same_normalised_form_are_detected_as_collision() {
let result = candidates_collide(&["API Key", "api_key"]);
assert!(
result.is_some(),
"expected collision detection for 'API Key' and 'api_key'"
);
assert_eq!(result.unwrap(), vec!["API_KEY"]);
}
#[test]
fn distinct_normalised_forms_produce_no_collision() {
let result = candidates_collide(&["username", "password", "API Key"]);
assert!(
result.is_none(),
"expected no collision for distinct labels, got {result:?}"
);
}
#[test]
fn three_fields_with_same_normalised_form_are_detected() {
let result = candidates_collide(&["My Secret", "my-secret", "MY_SECRET"]);
assert!(
result.is_some(),
"expected collision detection for three equivalent labels"
);
assert_eq!(result.unwrap(), vec!["MY_SECRET"]);
}