use super::super::*;
#[test]
fn test_boolean_override_integer_spread() {
let values: Vec<String> = vec!["0", "1", "2", "3", "0", "1", "4", "0", "5", "8"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec![
"representation.logical.boolean",
"representation.numeric.integer_number",
];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_some());
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.numeric.integer_number");
assert_eq!(rule, "boolean_override_integer_spread");
}
#[test]
fn test_boolean_override_preserves_real_boolean() {
let values: Vec<String> = vec!["0", "1", "0", "1", "1", "0", "0", "1", "0", "1"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.logical.boolean"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_none());
}
#[test]
fn test_boolean_override_single_char_categorical() {
let values: Vec<String> = vec!["S", "C", "Q", "S", "S", "C", "Q", "S", "S", "C"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.logical.boolean", "representation.text.word"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_some());
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.text.word");
assert_eq!(rule, "boolean_override_single_char_categorical");
}
#[test]
fn test_boolean_override_preserves_true_false_chars() {
let values: Vec<String> = vec!["T", "F", "T", "F", "T", "F", "T", "F"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.logical.boolean"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_none());
}
#[test]
fn binary_vocab_veto_is_default_on() {
assert!(!rhh::is_disabled("binary_vocab_veto"));
}
#[test]
fn test_boolean_override_with_current_model_label() {
let values: Vec<String> = vec!["0", "1", "2", "3", "0", "1", "4", "0", "5", "8"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec![
"technology.development.boolean",
"representation.numeric.integer_number",
];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(
result.is_some(),
"Boolean override must trigger for technology.development.boolean"
);
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.numeric.integer_number");
assert_eq!(rule, "boolean_override_integer_spread");
}
#[test]
fn test_boolean_override_preserves_real_boolean_current_label() {
let values: Vec<String> = vec!["0", "1", "0", "1", "1", "0", "0", "1", "0", "1"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["technology.development.boolean"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(
result.is_none(),
"Real boolean {{0,1}} must not be overridden"
);
}
#[test]
fn test_boolean_subtype_terms() {
let values: Vec<String> = vec![
"True", "False", "True", "True", "False", "True", "False", "False", "True", "False",
]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.terms"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_some());
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.boolean.terms");
assert_eq!(rule, "boolean_subtype_terms");
}
#[test]
fn test_boolean_subtype_binary() {
let values: Vec<String> = vec!["0", "1", "0", "1", "1", "0", "0", "1", "0", "1"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_some());
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.boolean.binary");
assert_eq!(rule, "boolean_subtype_binary");
}
#[test]
fn test_boolean_subtype_initials() {
let values: Vec<String> = vec!["T", "F", "T", "F", "T", "T", "F", "F", "T", "F"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.initials"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_some());
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.boolean.initials");
assert_eq!(rule, "boolean_subtype_initials");
}
#[test]
fn test_boolean_subtype_yes_no() {
let values: Vec<String> = vec!["yes", "no", "yes", "yes", "no", "no", "yes", "no"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.terms"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_some());
let (label, _) = result.unwrap();
assert_eq!(label, "representation.boolean.terms");
}
#[test]
fn test_boolean_subtype_override_categorical() {
let values: Vec<String> = vec![
"True", "False", "True", "True", "False", "True", "False", "False",
]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.discrete.categorical"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(
result.is_some(),
"Should override categorical for True/False column"
);
let (label, _) = result.unwrap();
assert_eq!(label, "representation.boolean.terms");
}
#[test]
fn test_boolean_subtype_not_triggered_for_mixed() {
let values: Vec<String> = vec!["yes", "no", "maybe", "yes", "unknown", "no"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.text.word"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_none());
}
#[test]
fn test_boolean_subtype_too_few_values() {
let values: Vec<String> = vec!["True", "False"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.terms"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_none());
}
#[test]
fn test_boolean_subtype_skewed_integers_not_binary() {
let values: Vec<String> = vec![
"0", "1", "0", "1", "0", "0", "1", "0", "0", "1", "2", "0", "3", "0", "1", "0", "0", "1",
"4", "0",
]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec![
"representation.numeric.integer_number",
"representation.boolean.binary",
];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(
result.is_none(),
"Skewed integer column (SibSp-like) should not be classified as boolean"
);
}
#[test]
fn test_boolean_subtype_pure_binary_still_works() {
let values: Vec<String> = vec!["0", "1", "1", "0", "0", "1", "0", "1", "1", "0"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(result.is_some());
let (label, _) = result.unwrap();
assert_eq!(label, "representation.boolean.binary");
}
#[test]
fn test_boolean_override_all_zeros() {
let values: Vec<String> = vec!["0", "0", "0", "0", "0", "0", "0", "0"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_some(), "All-zero column should be overridden");
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.numeric.integer_number");
assert_eq!(rule, "boolean_override_single_value");
}
#[test]
fn test_boolean_override_all_ones() {
let values: Vec<String> = vec!["1", "1", "1", "1", "1"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_some(), "All-ones column should be overridden");
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.numeric.integer_number");
assert_eq!(rule, "boolean_override_single_value");
}
#[test]
fn test_boolean_override_skewed_95pct() {
let mut values: Vec<String> = vec!["0"; 19].into_iter().map(String::from).collect();
values.push("1".to_string());
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(
result.is_none(),
"95% skew should still allow binary (boundary)"
);
}
#[test]
fn test_boolean_override_skewed_above_95pct() {
let mut values: Vec<String> = vec!["0"; 48].into_iter().map(String::from).collect();
for _ in 0..2 {
values.push("1".to_string());
}
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(
result.is_some(),
"96% skew should override binary to integer"
);
let (label, rule) = result.unwrap();
assert_eq!(label, "representation.numeric.integer_number");
assert_eq!(rule, "boolean_override_skewed");
}
#[test]
fn test_boolean_override_balanced_binary_preserved() {
let values: Vec<String> = vec!["0", "1", "0", "1", "0", "1", "0", "1"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_override(&values, &top_labels);
assert!(result.is_none(), "Balanced binary should not be overridden");
}
#[test]
fn test_boolean_subtype_all_zeros_rejected() {
let values: Vec<String> = vec!["0", "0", "0", "0", "0"]
.into_iter()
.map(String::from)
.collect();
let top_labels = vec!["representation.boolean.binary"];
let result = disambiguate_boolean_subtype(&values, &top_labels);
assert!(
result.is_none(),
"All-zero through subtype path should return None"
);
}
#[test]
fn binary_vocab_veto_full_column_catches_rare_count() {
let cc =
ColumnClassifier::with_defaults(Box::new(crate::inference::MockClassifier::new("unknown")));
let mut counts: Vec<String> = vec!["0".into(); 60];
counts.push("1".into());
counts.push("30".into());
let r = cc
.compose_from_sense("Comments", &counts, "representation.boolean.binary", 0.5)
.unwrap();
assert_eq!(r.label, "representation.numeric.integer_number");
let bins: Vec<String> = vec!["0".into(), "1".into(), "0".into(), "1".into(), "1".into()];
let r2 = cc
.compose_from_sense("flag", &bins, "representation.boolean.binary", 0.5)
.unwrap();
assert_eq!(r2.label, "representation.boolean.binary");
}