use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use sql_cli::{
buffer::*, config::config::Config, data::csv_datasource::CsvApiClient,
ui::key_handling::dispatcher::*,
};
use std::fs;
use tempfile::tempdir;
#[test]
fn test_key_dispatcher_basic() -> anyhow::Result<()> {
let dispatcher = KeyDispatcher::new();
let enter_key = KeyEvent::new(KeyCode::Enter, KeyModifiers::empty());
assert_eq!(
dispatcher.get_command_action(&enter_key),
Some("execute_query")
);
let quit_key = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
assert_eq!(dispatcher.get_command_action(&quit_key), Some("quit"));
let g_key = KeyEvent::new(KeyCode::Char('g'), KeyModifiers::empty());
assert_eq!(
dispatcher.get_results_action(&g_key),
Some("goto_first_row")
);
let G_key = KeyEvent::new(KeyCode::Char('G'), KeyModifiers::SHIFT);
assert_eq!(dispatcher.get_results_action(&G_key), Some("goto_last_row"));
assert_eq!(dispatcher.get_debug_action(&g_key), Some("debug_go_to_top"));
assert_eq!(
dispatcher.get_debug_action(&G_key),
Some("debug_go_to_bottom")
);
Ok(())
}
#[test]
fn test_buffer_management() -> anyhow::Result<()> {
let mut buffer_manager = BufferManager::new();
let config = Config::default();
assert_eq!(buffer_manager.current_index(), 0);
assert_eq!(buffer_manager.all_buffers().len(), 0);
let mut buffer_handler = sql_cli::buffer_handler::BufferHandler::new();
let result = buffer_handler.new_buffer(&mut buffer_manager, &config);
assert!(result.contains("Created new buffer"));
assert_eq!(buffer_manager.all_buffers().len(), 1);
let result2 = buffer_handler.new_buffer(&mut buffer_manager, &config);
assert!(result2.contains("Created new buffer"));
assert_eq!(buffer_manager.all_buffers().len(), 2);
let result = buffer_handler.next_buffer(&mut buffer_manager);
assert!(result.contains("Switched to buffer"));
let result = buffer_handler.previous_buffer(&mut buffer_manager);
assert!(result.contains("Switched to buffer"));
Ok(())
}
#[test]
fn test_buffer_with_data() -> anyhow::Result<()> {
let mut buffer = Buffer::new(1);
assert_eq!(buffer.get_id(), 1);
assert_eq!(buffer.get_query(), "");
assert!(buffer.get_datatable().is_none());
buffer.set_query("SELECT * FROM trades".to_string());
assert_eq!(buffer.get_query(), "SELECT * FROM trades");
use sql_cli::api_client::{QueryInfo, QueryResponse};
let test_response = QueryResponse {
data: vec![
serde_json::json!({"id": 1, "name": "Trade 1"}),
serde_json::json!({"id": 2, "name": "Trade 2"}),
],
count: 2,
query: QueryInfo {
select: vec!["*".to_string()],
where_clause: None,
order_by: None,
},
source: Some("test".to_string()),
table: Some("trades".to_string()),
cached: Some(false),
};
buffer
.set_results_as_datatable(Some(test_response))
.unwrap();
if let Some(datatable) = buffer.get_datatable() {
assert_eq!(datatable.row_count(), 2);
assert_eq!(datatable.column_count(), 2);
} else {
panic!("DataTable should be set");
}
Ok(())
}
#[test]
fn test_csv_api_client_with_json_data() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let json_path = temp_dir.path().join("test_trades.json");
let test_data = serde_json::json!([
{
"id": 1,
"platformOrderId": "ORDER-2024-001",
"executionSide": "BUY",
"quantity": 1000,
"price": 150.50,
"status": "Completed"
},
{
"id": 2,
"platformOrderId": "ORDER-2024-002",
"executionSide": "SELL",
"quantity": 500,
"price": 200.00,
"status": "Pending"
},
{
"id": 3,
"platformOrderId": "ORDER-2024-003",
"executionSide": "BUY",
"quantity": 750,
"price": 175.75,
"status": "Completed"
}
]);
fs::write(&json_path, serde_json::to_string_pretty(&test_data)?)?;
let mut csv_client = CsvApiClient::new();
csv_client.load_json(&json_path, "data")?;
let response = csv_client.query_csv("SELECT * FROM data")?;
assert_eq!(response.data.len(), 3, "Should have 3 rows");
let filtered_response =
csv_client.query_csv("SELECT * FROM data WHERE executionSide = 'BUY'")?;
assert_eq!(filtered_response.data.len(), 2, "Should have 2 BUY orders");
let agg_response =
csv_client.query_csv("SELECT COUNT(*) as count, SUM(quantity) as total FROM data")?;
println!("Aggregation response: {} rows", agg_response.data.len());
for (i, row) in agg_response.data.iter().enumerate() {
println!("Row {i}: {row:?}");
}
if agg_response.data.len() == 1 {
if let Some(row) = agg_response.data.first() {
assert_eq!(row["count"], 3);
assert_eq!(row["total"], 2250); }
} else {
assert_eq!(
agg_response.data.len(),
3,
"Should have 3 rows if not aggregated"
);
}
Ok(())
}
#[test]
fn test_csv_api_client_with_csv_data() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let csv_path = temp_dir.path().join("test_trades.csv");
let csv_data = "id,executionSide,quantity,price,status\n1,BUY,1000,150.50,Completed\n2,SELL,500,200.00,Pending\n3,BUY,750,175.75,Completed";
fs::write(&csv_path, csv_data)?;
let mut csv_client = CsvApiClient::new();
csv_client.load_csv(&csv_path, "data")?;
let response = csv_client.query_csv("SELECT * FROM data")?;
assert_eq!(response.data.len(), 3, "Should have 3 rows");
let grouped_response = csv_client
.query_csv("SELECT executionSide, COUNT(*) as count FROM data GROUP BY executionSide")?;
println!("Grouping response: {} rows", grouped_response.data.len());
for (i, row) in grouped_response.data.iter().enumerate() {
println!("Row {i}: {row:?}");
}
if grouped_response.data.len() == 2 {
let mut buy_count = 0;
let mut sell_count = 0;
for row in &grouped_response.data {
match row["executionSide"].as_str() {
Some("BUY") => buy_count = row["count"].as_i64().unwrap_or(0),
Some("SELL") => sell_count = row["count"].as_i64().unwrap_or(0),
_ => {}
}
}
assert_eq!(buy_count, 2);
assert_eq!(sell_count, 1);
} else {
assert_eq!(
grouped_response.data.len(),
3,
"Should have 3 rows if not grouped"
);
}
Ok(())
}
#[test]
fn test_complex_query_scenarios() -> anyhow::Result<()> {
let temp_dir = tempdir()?;
let json_path = temp_dir.path().join("complex_trades.json");
let test_data = serde_json::json!([
{
"id": 1,
"trader": "John Smith",
"executionSide": "BUY",
"quantity": 1000,
"price": 150.50,
"tradeDate": "2024-11-15",
"status": "Completed"
},
{
"id": 2,
"trader": "Jane Doe",
"executionSide": "SELL",
"quantity": 500,
"price": 200.00,
"tradeDate": "2024-11-16",
"status": "Pending"
},
{
"id": 3,
"trader": "Alice Johnson",
"executionSide": "BUY",
"quantity": 750,
"price": 175.75,
"tradeDate": "2024-11-17",
"status": "Completed"
},
{
"id": 4,
"trader": "Bob Wilson",
"executionSide": "BUY",
"quantity": 2000,
"price": 95.25,
"tradeDate": "2024-11-18",
"status": "Completed"
}
]);
fs::write(&json_path, serde_json::to_string_pretty(&test_data)?)?;
let mut csv_client = CsvApiClient::new();
csv_client.load_json(&json_path, "data")?;
let complex_filter_response = csv_client
.query_csv("SELECT * FROM data WHERE executionSide = 'BUY' AND quantity > 800")?;
assert_eq!(
complex_filter_response.data.len(),
2,
"Should have 2 large BUY orders"
);
let ordered_response = csv_client.query_csv("SELECT * FROM data ORDER BY price DESC")?;
assert_eq!(ordered_response.data.len(), 4);
assert_eq!(ordered_response.data[0]["price"], 200.00);
let trader_summary_response = csv_client.query_csv(
"SELECT trader, COUNT(*) as trade_count, SUM(quantity) as total_quantity FROM data GROUP BY trader"
)?;
assert_eq!(
trader_summary_response.data.len(),
4,
"Should have 4 different traders"
);
let frequent_traders_response = csv_client.query_csv(
"SELECT trader, COUNT(*) as trades FROM data GROUP BY trader HAVING trades >= 1",
)?;
assert_eq!(
frequent_traders_response.data.len(),
4,
"All traders have at least 1 trade"
);
Ok(())
}
#[test]
fn test_simulated_user_workflow() -> anyhow::Result<()> {
let sample_path = std::path::PathBuf::from("sample_trades.json");
if !sample_path.exists() {
println!("Skipping workflow test - sample_trades.json not found");
return Ok(());
}
let mut csv_client = CsvApiClient::new();
csv_client.load_json("sample_trades.json", "data")?;
let all_results_response = csv_client.query_csv("SELECT * FROM data")?;
assert!(
!all_results_response.data.is_empty(),
"Should have trade data"
);
let row_count = all_results_response.data.len();
println!("Loaded {row_count} rows from sample_trades.json");
let mut current_row = 0;
current_row = std::cmp::min(current_row + 1, row_count - 1);
assert!(current_row < row_count);
current_row = 0;
assert_eq!(current_row, 0);
current_row = row_count - 1;
assert_eq!(current_row, row_count - 1);
let completed_trades_response =
csv_client.query_csv("SELECT * FROM data WHERE status = 'Completed'")?;
println!(
"Found {} completed trades",
completed_trades_response.data.len()
);
assert!(
!completed_trades_response.data.is_empty(),
"Should have completed trades"
);
for trade in &completed_trades_response.data {
assert_eq!(trade["status"], "Completed");
}
Ok(())
}