use std::sync::Arc;
use alopex_dataframe::ops::JoinType;
use alopex_dataframe::{DataFrame, DataFrameError, Series};
use arrow::array::{Array, ArrayRef, Int64Array, StringArray};
fn s_i64(name: &str, values: Vec<i64>) -> Series {
let array: ArrayRef = Arc::new(Int64Array::from(values));
Series::from_arrow(name, vec![array]).unwrap()
}
fn s_str(name: &str, values: Vec<&str>) -> Series {
let array: ArrayRef = Arc::new(StringArray::from(values));
Series::from_arrow(name, vec![array]).unwrap()
}
fn left_df() -> DataFrame {
DataFrame::new(vec![
s_i64("id", vec![1, 2, 3]),
s_i64("value", vec![10, 20, 30]),
])
.unwrap()
}
fn right_df() -> DataFrame {
DataFrame::new(vec![
s_i64("id", vec![2, 3, 4]),
s_i64("value", vec![200, 300, 400]),
])
.unwrap()
}
#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn join_inner_suffixes_right_columns() {
let left = left_df();
let right = right_df();
let out = left
.join(&right, vec!["id".to_string()], JoinType::Inner)
.unwrap();
let schema = out.schema();
let names: Vec<String> = schema
.fields()
.iter()
.map(|f| f.name().to_string())
.collect();
assert_eq!(
names,
vec![
"id".to_string(),
"value".to_string(),
"value_right".to_string()
]
);
assert_eq!(out.height(), 2);
}
#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn join_left_keeps_unmatched_rows() {
let left = left_df();
let right = right_df();
let out = left
.join(&right, vec!["id".to_string()], JoinType::Left)
.unwrap();
assert_eq!(out.height(), 3);
let right_vals = out.column("value_right").unwrap().to_arrow();
let array = right_vals[0].as_any().downcast_ref::<Int64Array>().unwrap();
assert!(array.is_null(0));
}
#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn join_semi_and_anti_return_left_columns_only() {
let left = left_df();
let right = right_df();
let semi = left
.join(&right, vec!["id".to_string()], JoinType::Semi)
.unwrap();
let anti = left
.join(&right, vec!["id".to_string()], JoinType::Anti)
.unwrap();
let semi_schema = semi.schema();
let semi_names: Vec<String> = semi_schema
.fields()
.iter()
.map(|f| f.name().to_string())
.collect();
let anti_schema = anti.schema();
let anti_names: Vec<String> = anti_schema
.fields()
.iter()
.map(|f| f.name().to_string())
.collect();
assert_eq!(semi_names, vec!["id".to_string(), "value".to_string()]);
assert_eq!(anti_names, vec!["id".to_string(), "value".to_string()]);
assert_eq!(semi.height(), 2);
assert_eq!(anti.height(), 1);
}
#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn join_left_right_keys_include_right_key_column() {
let left = DataFrame::new(vec![
s_i64("id", vec![1, 2, 3]),
s_i64("value", vec![10, 20, 30]),
])
.unwrap();
let right = DataFrame::new(vec![
s_i64("rid", vec![2, 3, 4]),
s_i64("value", vec![200, 300, 400]),
])
.unwrap();
let out = left
.join(
&right,
(vec!["id".to_string()], vec!["rid".to_string()]),
JoinType::Inner,
)
.unwrap();
let schema = out.schema();
let names: Vec<String> = schema
.fields()
.iter()
.map(|f| f.name().to_string())
.collect();
assert_eq!(
names,
vec![
"id".to_string(),
"value".to_string(),
"rid".to_string(),
"value_right".to_string()
]
);
}
#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn join_rejects_mismatched_key_types() {
let left = DataFrame::new(vec![s_i64("id", vec![1, 2])]).unwrap();
let right = DataFrame::new(vec![s_str("id", vec!["1", "2"])]).unwrap();
let err = left
.join(&right, vec!["id".to_string()], JoinType::Inner)
.unwrap_err();
assert!(matches!(err, DataFrameError::TypeMismatch { .. }));
}