use ferrolearn_core::traits::{Fit, FitTransform, Transform};
use ferrolearn_preprocess::LabelEncoder;
use ndarray::{Array1, array};
fn str_arr(v: &[&str]) -> Array1<String> {
Array1::from_vec(v.iter().map(std::string::ToString::to_string).collect())
}
#[test]
fn divergence_empty_fit_succeeds() {
let enc = LabelEncoder::new();
let empty: Array1<String> = Array1::from_vec(vec![]);
let fitted = enc
.fit(&empty, &())
.expect("sklearn _label.py:84-99: fit([]) succeeds with empty classes_");
assert_eq!(
fitted.n_classes(),
0,
"sklearn empty-fit yields classes_ of length 0"
);
assert!(
fitted.classes().is_empty(),
"sklearn empty-fit yields empty classes_"
);
}
#[test]
fn green_fit_classes_sorted() {
let enc = LabelEncoder::new();
let labels = str_arr(&["cat", "dog", "cat", "bird"]);
let fitted = enc.fit(&labels, &()).unwrap();
assert_eq!(fitted.classes(), &["bird", "cat", "dog"]);
assert_eq!(fitted.n_classes(), 3);
}
#[test]
fn green_transform() {
let enc = LabelEncoder::new();
let labels = str_arr(&["cat", "dog", "cat", "bird"]);
let fitted = enc.fit(&labels, &()).unwrap();
let encoded = fitted.transform(&labels).unwrap();
assert_eq!(encoded.to_vec(), vec![1usize, 2, 1, 0]);
}
#[test]
fn green_inverse_transform_roundtrip() {
let enc = LabelEncoder::new();
let labels = str_arr(&["cat", "dog", "cat", "bird"]);
let fitted = enc.fit(&labels, &()).unwrap();
let codes = array![1usize, 2, 1, 0];
let recovered = fitted.inverse_transform(&codes).unwrap();
assert_eq!(
recovered.to_vec(),
vec![
"cat".to_string(),
"dog".to_string(),
"cat".to_string(),
"bird".to_string()
]
);
}
#[test]
fn green_fit_transform_equals_fit_then_transform() {
let enc = LabelEncoder::new();
let labels = str_arr(&["cat", "dog", "cat", "bird"]);
let via_ft = enc.fit_transform(&labels).unwrap();
assert_eq!(via_ft.to_vec(), vec![1usize, 2, 1, 0]); let fitted = enc.fit(&labels, &()).unwrap();
let via_sep = fitted.transform(&labels).unwrap();
assert_eq!(via_ft, via_sep);
}
#[test]
fn green_empty_transform_returns_empty() {
let enc = LabelEncoder::new();
let fitted = enc.fit(&str_arr(&["a", "b"]), &()).unwrap();
let empty: Array1<String> = Array1::from_vec(vec![]);
let out = fitted
.transform(&empty)
.expect("sklearn _label.py:134-135: empty transform is empty array");
assert!(out.is_empty());
}
#[test]
fn green_empty_inverse_transform_returns_empty() {
let enc = LabelEncoder::new();
let fitted = enc.fit(&str_arr(&["a", "b"]), &()).unwrap();
let empty: Array1<usize> = Array1::from_vec(vec![]);
let out = fitted
.inverse_transform(&empty)
.expect("sklearn _label.py:155-156: empty inverse_transform is empty array");
assert!(out.is_empty());
}
#[test]
fn green_sort_order_mixed_ascii_matches_numpy() {
let enc = LabelEncoder::new();
let labels = str_arr(&["B", "a", "A", "b", "10", "2"]);
let fitted = enc.fit(&labels, &()).unwrap();
assert_eq!(fitted.classes(), &["10", "2", "A", "B", "a", "b"]);
}
#[test]
fn green_unseen_label_rejected() {
let enc = LabelEncoder::new();
let fitted = enc.fit(&str_arr(&["a", "b"]), &()).unwrap();
let unseen = str_arr(&["c"]);
assert!(
fitted.transform(&unseen).is_err(),
"sklearn raises ValueError on previously unseen labels; ferrolearn must also reject"
);
}
#[test]
fn green_empty_fit_then_empty_transform_ok() {
let enc = LabelEncoder::new();
let empty: Array1<String> = Array1::from_vec(vec![]);
let fitted = enc.fit(&empty, &()).expect("fit([]) succeeds");
let out = fitted
.transform(&Array1::from_vec(vec![]))
.expect("sklearn: empty transform after empty fit is Ok(empty)");
assert!(out.is_empty());
}
#[test]
fn green_empty_fit_then_transform_unseen_rejected() {
let enc = LabelEncoder::new();
let empty: Array1<String> = Array1::from_vec(vec![]);
let fitted = enc.fit(&empty, &()).expect("fit([]) succeeds");
assert!(
fitted.transform(&str_arr(&["a"])).is_err(),
"sklearn raises on unseen label after empty fit; ferrolearn must also reject"
);
}
#[test]
fn green_empty_fit_then_empty_inverse_ok() {
let enc = LabelEncoder::new();
let empty: Array1<String> = Array1::from_vec(vec![]);
let fitted = enc.fit(&empty, &()).expect("fit([]) succeeds");
let out = fitted
.inverse_transform(&Array1::from_vec(vec![]))
.expect("sklearn: empty inverse_transform after empty fit is Ok(empty)");
assert!(out.is_empty());
}
#[test]
fn green_empty_fit_then_inverse_oob_rejected() {
let enc = LabelEncoder::new();
let empty: Array1<String> = Array1::from_vec(vec![]);
let fitted = enc.fit(&empty, &()).expect("fit([]) succeeds");
assert!(
fitted.inverse_transform(&array![0usize]).is_err(),
"sklearn raises out-of-range on inverse_transform([0]) after empty fit; ferrolearn must reject"
);
}