use sql_cli::data::data_view::DataView;
use sql_cli::datatable::DataValue;
use sql_cli::datatable_loaders::load_json_to_datatable;
use std::path::PathBuf;
use std::sync::Arc;
fn get_test_data_path(filename: &str) -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("data");
path.push(filename);
path
}
fn load_trades_datatable() -> sql_cli::datatable::DataTable {
let trades_path = get_test_data_path("trades.json");
load_json_to_datatable(trades_path, "trades").expect("Failed to load trades.json")
}
#[test]
fn test_trades_data_loaded_correctly() {
let table = load_trades_datatable();
let view = DataView::new(Arc::new(table));
assert_eq!(view.row_count(), 100, "Should have 100 trades");
assert_eq!(view.column_count(), 15, "Should have 15 columns");
let columns = view.column_names();
assert!(columns.contains(&"id".to_string()));
assert!(columns.contains(&"instrumentId".to_string()));
assert!(columns.contains(&"quantity".to_string()));
assert!(columns.contains(&"price".to_string()));
assert!(columns.contains(&"counterparty".to_string()));
assert!(columns.contains(&"trader".to_string()));
assert!(columns.contains(&"book".to_string()));
}
#[test]
fn test_hide_columns_on_trades() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
assert_eq!(view.column_count(), 15);
view.hide_column_by_name("commission");
assert_eq!(view.column_count(), 14);
view.hide_column_by_name("confirmationStatus");
assert_eq!(view.column_count(), 13);
let columns = view.column_names();
assert!(!columns.contains(&"commission".to_string()));
assert!(!columns.contains(&"confirmationStatus".to_string()));
let hidden = view.get_hidden_column_names();
assert_eq!(hidden.len(), 2);
assert!(hidden.contains(&"commission".to_string()));
assert!(hidden.contains(&"confirmationStatus".to_string()));
view.unhide_all_columns();
assert_eq!(view.column_count(), 15);
}
#[test]
fn test_pin_columns_on_trades() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.pin_column_by_name("instrumentId").unwrap();
view.pin_column_by_name("quantity").unwrap();
view.pin_column_by_name("price").unwrap();
assert_eq!(view.get_pinned_columns().len(), 3);
let pinned_names = view.get_pinned_column_names();
assert_eq!(pinned_names, vec!["instrumentId", "quantity", "price"]);
let columns = view.column_names();
assert_eq!(columns[0], "instrumentId");
assert_eq!(columns[1], "quantity");
assert_eq!(columns[2], "price");
let hidden = view.hide_column_by_name("price");
assert!(!hidden);
assert_eq!(view.column_count(), 15); }
#[test]
fn test_filter_trades_by_counterparty() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
assert_eq!(view.row_count(), 100);
view.apply_text_filter("MORGAN", false);
let filtered_count = view.row_count();
assert!(filtered_count > 0, "Should find some MORGAN trades");
assert!(filtered_count < 100, "Should filter out non-MORGAN trades");
for i in 0..filtered_count.min(5) {
if let Some(row) = view.get_row(i) {
let row_text = row
.values
.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join(" ");
assert!(row_text.contains("MORGAN"));
}
}
view.clear_filter();
assert_eq!(view.row_count(), 100);
}
#[test]
fn test_sort_trades_by_quantity() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
let columns = view.column_names();
let quantity_idx = columns.iter().position(|c| c == "quantity").unwrap();
view.apply_sort(quantity_idx, false).unwrap();
let mut last_quantity = f64::MAX;
for i in 0..5 {
if let Some(row) = view.get_row(i) {
if let DataValue::Float(qty) = &row.values[quantity_idx] {
assert!(*qty <= last_quantity, "Quantities should be descending");
last_quantity = *qty;
} else if let DataValue::Integer(qty) = &row.values[quantity_idx] {
let qty_float = *qty as f64;
assert!(
qty_float <= last_quantity,
"Quantities should be descending"
);
last_quantity = qty_float;
}
}
}
}
#[test]
fn test_fuzzy_filter_on_counterparty() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.apply_fuzzy_filter("MRGN", true);
let matches = view.row_count();
assert!(
matches > 0,
"Should find some MORGAN trades with fuzzy match"
);
assert!(matches < 100, "Should filter out non-MORGAN trades");
view.clear_filter();
view.apply_fuzzy_filter("'MORGAN", false);
let exact_matches = view.row_count();
assert!(
exact_matches > 0,
"Should find MORGAN trades with exact match"
);
}
#[test]
fn test_combined_operations_on_trades() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.pin_column_by_name("instrumentId").unwrap();
view.pin_column_by_name("counterparty").unwrap();
view.hide_column_by_name("commission");
view.hide_column_by_name("confirmationStatus");
view.apply_text_filter("Jane", false);
let columns = view.column_names();
let price_idx = columns.iter().position(|c| c == "price").unwrap();
view.apply_sort(price_idx, false).unwrap();
assert_eq!(view.get_pinned_columns().len(), 2);
assert_eq!(view.column_count(), 13); let filtered = view.row_count();
assert!(filtered > 0, "Should find Jane's trades");
assert!(filtered < 100, "Should be filtered");
if let Some(row) = view.get_row(0) {
let columns = view.column_names();
assert_eq!(columns[0], "instrumentId");
assert_eq!(columns[1], "counterparty");
}
}
#[test]
fn test_export_filtered_trades() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.pin_column_by_name("instrumentId").unwrap();
view.hide_column_by_name("commission");
view.apply_text_filter("USD", false);
let csv = view.to_csv().unwrap();
let lines: Vec<&str> = csv.lines().collect();
assert!(lines[0].starts_with("instrumentId,"));
assert!(!lines[0].contains("commission"));
let json = view.to_json();
if let Some(array) = json.as_array() {
assert!(!array.is_empty(), "Should have some USD trades");
assert!(array.len() < 100, "Should be filtered");
if let Some(first) = array.first() {
assert!(first.get("instrumentId").is_some());
assert!(first.get("commission").is_none()); }
}
}
#[test]
fn test_column_search_on_trades() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.search_columns("counter");
let matches = view.get_matching_columns();
assert!(!matches.is_empty(), "Should find counter columns");
let match_names: Vec<String> = matches.iter().map(|(_, name)| name.clone()).collect();
assert!(match_names.contains(&"counterparty".to_string()));
assert!(match_names.contains(&"counterpartyCountry".to_string()));
assert!(match_names.contains(&"counterpartyType".to_string()));
let first_match = view.get_current_column_match();
assert!(first_match.is_some());
if matches.len() > 1 {
let next_match = view.next_column_match();
assert!(next_match.is_some());
}
view.clear_column_search();
assert!(!view.has_column_search());
}
#[test]
fn test_trades_data_integrity() {
let table = load_trades_datatable();
let view = DataView::new(Arc::new(table));
if let Some(first_trade) = view.get_row(0) {
assert_eq!(first_trade.values.len(), 15);
let columns = view.column_names();
let id_idx = columns.iter().position(|c| c == "id").unwrap();
let inst_idx = columns.iter().position(|c| c == "instrumentId").unwrap();
assert!(!matches!(first_trade.values[id_idx], DataValue::Null));
assert!(!matches!(first_trade.values[inst_idx], DataValue::Null));
}
if let Some(last_trade) = view.get_row(99) {
assert_eq!(last_trade.values.len(), 15);
}
}
#[test]
fn test_move_columns_with_pinned() {
let table = load_trades_datatable();
let mut view = DataView::new(Arc::new(table));
view.pin_column_by_name("id").unwrap();
view.pin_column_by_name("instrumentId").unwrap();
let columns = view.column_names();
assert_eq!(columns[0], "id");
assert_eq!(columns[1], "instrumentId");
let book_idx = columns.iter().position(|c| c == "book").unwrap();
view.move_column_left(book_idx);
let columns = view.column_names();
assert_eq!(columns[0], "id");
assert_eq!(columns[1], "instrumentId");
}