use hedl_core::Value;
use hedl_csv::{from_csv_with_config, FromCsvConfig};
#[test]
fn test_infer_all_integers() {
let csv_data = r"id,age,count
1,30,100
2,25,200
3,35,150
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["age", "count"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[0], Value::Int(1)); assert_eq!(list.rows[0].fields[1], Value::Int(30)); assert_eq!(list.rows[0].fields[2], Value::Int(100));
assert_eq!(list.rows[1].fields[0], Value::Int(2));
assert_eq!(list.rows[1].fields[1], Value::Int(25));
assert_eq!(list.rows[1].fields[2], Value::Int(200));
}
#[test]
fn test_infer_all_floats() {
let csv_data = r"id,price,score
1,99.99,87.5
2,149.50,92.3
3,75.25,88.1
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Product", &["price", "score"], config).unwrap();
let list = doc.get("products").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[0], Value::Int(1)); assert_eq!(list.rows[0].fields[1], Value::Float(99.99)); assert_eq!(list.rows[0].fields[2], Value::Float(87.5)); }
#[test]
fn test_infer_all_booleans() {
let csv_data = r"id,active,verified
1,true,false
2,false,true
3,true,true
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "User", &["active", "verified"], config).unwrap();
let list = doc.get("users").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[0], Value::Int(1)); assert_eq!(list.rows[0].fields[1], Value::Bool(true)); assert_eq!(list.rows[0].fields[2], Value::Bool(false));
assert_eq!(list.rows[1].fields[0], Value::Int(2));
assert_eq!(list.rows[1].fields[1], Value::Bool(false));
assert_eq!(list.rows[1].fields[2], Value::Bool(true));
}
#[test]
fn test_infer_all_strings() {
let csv_data = r"id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com
3,Charlie,charlie@example.com
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "User", &["name", "email"], config).unwrap();
let list = doc.get("users").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[0], Value::Int(1)); assert_eq!(
list.rows[0].fields[1],
Value::String("Alice".to_string().into())
);
assert_eq!(
list.rows[0].fields[2],
Value::String("alice@example.com".to_string().into())
);
}
#[test]
fn test_infer_mixed_types() {
let csv_data = r"id,name,age,score,active
1,Alice,30,95.5,true
2,Bob,25,87.3,false
3,Charlie,35,92.1,true
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(
csv_data,
"Person",
&["name", "age", "score", "active"],
config,
)
.unwrap();
let list = doc.get("persons").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[0], Value::Int(1)); assert_eq!(
list.rows[0].fields[1],
Value::String("Alice".to_string().into())
); assert_eq!(list.rows[0].fields[2], Value::Int(30)); assert_eq!(list.rows[0].fields[3], Value::Float(95.5)); assert_eq!(list.rows[0].fields[4], Value::Bool(true)); }
#[test]
fn test_inference_with_nulls() {
let csv_data = r"id,optional_age,optional_name
1,30,Alice
2,,Bob
3,25,
4,,
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(
csv_data,
"Record",
&["optional_age", "optional_name"],
config,
)
.unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 4);
assert_eq!(list.rows[0].fields[1], Value::Int(30));
assert_eq!(
list.rows[0].fields[2],
Value::String("Alice".to_string().into())
);
assert_eq!(list.rows[1].fields[1], Value::Null);
assert_eq!(
list.rows[1].fields[2],
Value::String("Bob".to_string().into())
);
assert_eq!(list.rows[2].fields[1], Value::Int(25));
assert_eq!(list.rows[2].fields[2], Value::Null);
assert_eq!(list.rows[3].fields[1], Value::Null);
assert_eq!(list.rows[3].fields[2], Value::Null);
}
#[test]
fn test_inference_all_nulls_column() {
let csv_data = r"id,always_null,value
1,,100
2,,200
3,,300
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["always_null", "value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 3);
assert_eq!(list.rows[0].fields[1], Value::Null);
assert_eq!(list.rows[1].fields[1], Value::Null);
assert_eq!(list.rows[2].fields[1], Value::Null);
assert_eq!(list.rows[0].fields[2], Value::Int(100));
}
#[test]
fn test_inference_integers_vs_floats() {
let csv_data = r"id,value
1,10
2,20.5
3,30
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Float(10.0));
assert_eq!(list.rows[1].fields[1], Value::Float(20.5));
assert_eq!(list.rows[2].fields[1], Value::Float(30.0));
}
#[test]
fn test_inference_string_with_number_looking_values() {
let csv_data = r"id,code
1,123
2,ABC
3,456
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Item", &["code"], config).unwrap();
let list = doc.get("items").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Int(123)); assert_eq!(
list.rows[1].fields[1],
Value::String("ABC".to_string().into())
);
assert_eq!(list.rows[2].fields[1], Value::Int(456)); }
#[test]
fn test_inference_bool_with_mixed_values() {
let csv_data = r"id,flag
1,true
2,maybe
3,false
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["flag"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Bool(true));
assert_eq!(
list.rows[1].fields[1],
Value::String("maybe".to_string().into())
);
assert_eq!(list.rows[2].fields[1], Value::Bool(false));
}
#[test]
fn test_inference_small_sample_size() {
let csv_data = r"id,value
1,10
2,20
3,30
4,not_a_number
5,50
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 3,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Int(10));
assert_eq!(list.rows[1].fields[1], Value::Int(20));
assert_eq!(list.rows[2].fields[1], Value::Int(30));
assert_eq!(
list.rows[3].fields[1],
Value::String("not_a_number".to_string().into())
);
assert_eq!(list.rows[4].fields[1], Value::Int(50));
}
#[test]
fn test_inference_large_sample_size() {
let csv_data = r"id,value
1,10
2,20
3,30
4,not_a_number
5,50
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Int(10));
assert_eq!(list.rows[1].fields[1], Value::Int(20));
assert_eq!(list.rows[2].fields[1], Value::Int(30));
assert_eq!(
list.rows[3].fields[1],
Value::String("not_a_number".to_string().into())
);
assert_eq!(list.rows[4].fields[1], Value::Int(50));
}
#[test]
fn test_schema_inference_vs_no_inference() {
let csv_data = r"id,age,name
1,30,Alice
2,25,Bob
";
let config_no_infer = FromCsvConfig {
infer_schema: false,
..Default::default()
};
let doc_no_infer =
from_csv_with_config(csv_data, "Person", &["age", "name"], config_no_infer).unwrap();
let list_no_infer = doc_no_infer.get("persons").unwrap().as_list().unwrap();
let config_infer = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc_infer =
from_csv_with_config(csv_data, "Person", &["age", "name"], config_infer).unwrap();
let list_infer = doc_infer.get("persons").unwrap().as_list().unwrap();
assert_eq!(list_no_infer.rows.len(), list_infer.rows.len());
assert!(matches!(list_no_infer.rows[0].fields[1], Value::Int(30)));
assert!(matches!(list_infer.rows[0].fields[1], Value::Int(30)));
}
#[test]
fn test_inference_with_scientific_notation() {
let csv_data = r"id,measurement
1,1.5e10
2,2.3e-5
3,1000000
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Data", &["measurement"], config).unwrap();
let list = doc.get("datas").unwrap().as_list().unwrap();
assert!(matches!(list.rows[0].fields[1], Value::Float(_)));
assert!(matches!(list.rows[1].fields[1], Value::Float(_)));
assert_eq!(list.rows[2].fields[1], Value::Float(1000000.0));
}
#[test]
fn test_inference_empty_csv() {
let csv_data = "id,value\n";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 0);
}
#[test]
fn test_inference_single_row() {
let csv_data = r"id,value
1,42
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 1);
assert_eq!(list.rows[0].fields[1], Value::Int(42));
}
#[test]
fn test_inference_with_whitespace() {
let csv_data = r"id,value
1, 42
2, 100
3, 75
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
trim: true, ..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Int(42));
assert_eq!(list.rows[1].fields[1], Value::Int(100));
assert_eq!(list.rows[2].fields[1], Value::Int(75));
}
#[test]
fn test_inference_with_tab_delimiter() {
let csv_data = "id\tage\tactive\n1\t30\ttrue\n2\t25\tfalse\n";
let config = FromCsvConfig {
delimiter: b'\t',
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "User", &["age", "active"], config).unwrap();
let list = doc.get("users").unwrap().as_list().unwrap();
assert_eq!(list.rows.len(), 2);
assert_eq!(list.rows[0].fields[1], Value::Int(30));
assert_eq!(list.rows[0].fields[2], Value::Bool(true));
}
#[test]
fn test_inference_fallback_to_string() {
let csv_data = r"id,value
1,10
2,20
3,30
";
let config = FromCsvConfig {
infer_schema: true,
sample_rows: 100,
..Default::default()
};
let doc = from_csv_with_config(csv_data, "Record", &["value"], config).unwrap();
let list = doc.get("records").unwrap().as_list().unwrap();
assert_eq!(list.rows[0].fields[1], Value::Int(10));
assert_eq!(list.rows[1].fields[1], Value::Int(20));
assert_eq!(list.rows[2].fields[1], Value::Int(30));
}