alopex-dataframe 0.7.2

Polars-compatible DataFrame API for Alopex DB (v0.1)
Documentation
use std::sync::Arc;

use alopex_dataframe::expr::col;
use alopex_dataframe::io::{read_csv_with_options, CsvReadOptions};
use alopex_dataframe::{DataFrame, DataFrameError, Series};
use arrow::array::{ArrayRef, Float64Array, Int64Array, StringArray};
use arrow::datatypes::{DataType, Field, Schema};
use arrow::record_batch::RecordBatch;

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn column_not_found_is_reported() {
    let a: ArrayRef = Arc::new(Int64Array::from(vec![1_i64]));
    let df = DataFrame::new(vec![Series::from_arrow("a", vec![a]).unwrap()]).unwrap();
    let err = df.column("missing").unwrap_err();
    assert!(matches!(err, DataFrameError::ColumnNotFound { .. }));
    assert!(err.to_string().contains("missing"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn schema_mismatch_duplicate_column_name() {
    let a1: ArrayRef = Arc::new(Int64Array::from(vec![1_i64]));
    let a2: ArrayRef = Arc::new(Int64Array::from(vec![2_i64]));
    let err = DataFrame::new(vec![
        Series::from_arrow("a", vec![a1]).unwrap(),
        Series::from_arrow("a", vec![a2]).unwrap(),
    ])
    .unwrap_err();
    assert!(matches!(err, DataFrameError::SchemaMismatch { .. }));
    assert!(err.to_string().contains("duplicate"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn schema_mismatch_length_mismatch() {
    let a: ArrayRef = Arc::new(Int64Array::from(vec![1_i64, 2]));
    let b: ArrayRef = Arc::new(Int64Array::from(vec![10_i64]));
    let err = DataFrame::new(vec![
        Series::from_arrow("a", vec![a]).unwrap(),
        Series::from_arrow("b", vec![b]).unwrap(),
    ])
    .unwrap_err();
    assert!(matches!(err, DataFrameError::SchemaMismatch { .. }));
    assert!(err.to_string().contains("length"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn from_batches_schema_mismatch() {
    let s1 = Arc::new(Schema::new(vec![Field::new("a", DataType::Int64, true)]));
    let s2 = Arc::new(Schema::new(vec![Field::new("b", DataType::Int64, true)]));
    let b1 = RecordBatch::try_new(
        s1,
        vec![Arc::new(Int64Array::from(vec![1_i64])) as ArrayRef],
    )
    .unwrap();
    let b2 = RecordBatch::try_new(
        s2,
        vec![Arc::new(Int64Array::from(vec![2_i64])) as ArrayRef],
    )
    .unwrap();
    let err = DataFrame::from_batches(vec![b1, b2]).unwrap_err();
    assert!(matches!(err, DataFrameError::SchemaMismatch { .. }));
    let msg = err.to_string();
    assert!(msg.contains("schema mismatch"));
    assert!(msg.contains("batch 0"));
    assert!(msg.contains("batch 1"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn type_mismatch_for_mixed_series_dtypes() {
    let a: ArrayRef = Arc::new(Int64Array::from(vec![1_i64]));
    let b: ArrayRef = Arc::new(Float64Array::from(vec![1.0]));
    let err = Series::from_arrow("x", vec![a, b]).unwrap_err();
    assert!(matches!(err, DataFrameError::TypeMismatch { .. }));
    assert!(err.to_string().contains("expected"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn type_mismatch_for_non_numeric_agg() {
    let g: ArrayRef = Arc::new(StringArray::from(vec!["x", "x"]));
    let s: ArrayRef = Arc::new(StringArray::from(vec!["a", "b"]));
    let df = DataFrame::new(vec![
        Series::from_arrow("g", vec![g]).unwrap(),
        Series::from_arrow("s", vec![s]).unwrap(),
    ])
    .unwrap();

    let err = df
        .lazy()
        .group_by(vec![col("g")])
        .agg(vec![col("s").sum().alias("sum_s")])
        .collect()
        .unwrap_err();

    assert!(matches!(err, DataFrameError::TypeMismatch { .. }));
    let msg = err.to_string();
    assert!(msg.contains("expected"));
    assert!(msg.contains("numeric"));
    assert!(msg.contains("Utf8"));
}

#[cfg_attr(not(feature = "lane_ci"), ignore)]
#[test]
fn configuration_error_for_invalid_csv_delimiter() {
    let dir = tempfile::tempdir().unwrap();
    let path = dir.path().join("data.csv");
    std::fs::write(&path, "a,b\n1,2\n").unwrap();

    let opts = CsvReadOptions::default().with_delimiter(b'\0');
    let err = read_csv_with_options(&path, &opts).unwrap_err();
    assert!(matches!(err, DataFrameError::Configuration { .. }));
    assert!(err.to_string().contains("delimiter"));
}