flowcode-core 0.4.3-alpha

Core execution engine for FlowCode data scripting language
Documentation
// duckdb sort verb integration tests
#![cfg(feature = "duckdb")]

use std::io::Write;
use tempfile::{Builder, TempPath};

use flowcode_core::duckdb_session::DuckSession;
use flowcode_core::types::TypedValue;
use flowcode_core::ast::ArgValue;
use flowcode_core::duckdb_verbs::map_verb_to_sql;

fn create_test_csv(content: &str) -> (TempPath, String) {
    let mut temp_file = Builder::new()
        .prefix("test_")
        .suffix(".csv")
        .tempfile()
        .unwrap();
    writeln!(temp_file, "{}", content).unwrap();
    let temp_path = temp_file.into_temp_path(); // close handle, keep file
    let path_str = temp_path.to_str().unwrap().to_string();
    (temp_path, path_str)
}

#[test]
fn test_sort_by_single_column_asc() {
    let csv_content = "region,sales\nEast,1200\nWest,800\nNorth,1000";
    let (_guard, file_path) = create_test_csv(csv_content);
    let session = DuckSession::new().unwrap();
    let table_name = "test_sales_sort_asc";

    let load_result = session.load_csv_auto(table_name, &file_path);
    assert!(load_result.is_ok(), "Failed to load CSV: {:?}", load_result.err());

    let args = vec![
        TypedValue::new(
            flowcode_core::types::ValueKind::String,
            ArgValue::String(table_name.to_string())
        ),
        TypedValue::new(
            flowcode_core::types::ValueKind::String,
            ArgValue::String("sales".to_string())
        )
    ];
    
    let result = map_verb_to_sql("sort", &args, &session);
    assert!(result.is_ok());
    let sql_query = result.unwrap();
    assert_eq!(sql_query, format!("SELECT * FROM {} ORDER BY sales ASC", table_name));

    let query_result = session.execute_sql(&sql_query);
    assert!(query_result.is_ok(), "Sort query failed: {:?}", query_result.err());

    if let Ok(TypedValue { value: ArgValue::Table(data), .. }) = query_result {
        assert_eq!(data.len(), 3, "Expected 3 rows from sort");
        assert_eq!(data[0].len(), 2, "Expected 2 columns from sort");
        // Check if sales are in ascending order
        if let ArgValue::Number(n) = &data[0][1].value {
            assert_eq!(*n, 800.0);
        } else {
            panic!("Expected numeric value for sales");
        }
        if let ArgValue::Number(n) = &data[1][1].value {
            assert_eq!(*n, 1000.0);
        } else {
            panic!("Expected numeric value for sales");
        }
        if let ArgValue::Number(n) = &data[2][1].value {
            assert_eq!(*n, 1200.0);
        } else {
            panic!("Expected numeric value for sales");
        }
    } else {
        panic!("Expected table data in sort result");
    }
}

#[test]
fn test_sort_by_single_column_desc() {
    let csv_content = "region,sales\nEast,1200\nWest,800\nNorth,1000";
    let (_guard, file_path) = create_test_csv(csv_content);
    let session = DuckSession::new().unwrap();
    let table_name = "test_sales_sort_desc";

    let load_result = session.load_csv_auto(table_name, &file_path);
    assert!(load_result.is_ok(), "Failed to load CSV: {:?}", load_result.err());

    let args = vec![
        TypedValue::new(
            flowcode_core::types::ValueKind::String,
            ArgValue::String(table_name.to_string())
        ),
        TypedValue::new(
            flowcode_core::types::ValueKind::String,
            ArgValue::String("sales".to_string())
        ),
        TypedValue::new(
            flowcode_core::types::ValueKind::String,
            ArgValue::String("desc".to_string())
        )
    ];
    
    let result = map_verb_to_sql("sort", &args, &session);
    assert!(result.is_ok());
    let sql_query = result.unwrap();
    assert_eq!(sql_query, format!("SELECT * FROM {} ORDER BY sales DESC", table_name));

    let query_result = session.execute_sql(&sql_query);
    assert!(query_result.is_ok(), "Sort query failed: {:?}", query_result.err());

    if let Ok(TypedValue { value: ArgValue::Table(data), .. }) = query_result {
        assert_eq!(data.len(), 3, "Expected 3 rows from sort");
        assert_eq!(data[0].len(), 2, "Expected 2 columns from sort");
        // Check if sales are in descending order
        if let ArgValue::Number(n) = &data[0][1].value {
            assert_eq!(*n, 1200.0);
        } else {
            panic!("Expected numeric value for sales");
        }
        if let ArgValue::Number(n) = &data[1][1].value {
            assert_eq!(*n, 1000.0);
        } else {
            panic!("Expected numeric value for sales");
        }
        if let ArgValue::Number(n) = &data[2][1].value {
            assert_eq!(*n, 800.0);
        } else {
            panic!("Expected numeric value for sales");
        }
    } else {
        panic!("Expected table data in sort result");
    }
}

#[test]
fn test_sort_no_arguments() {
    let session = DuckSession::new().unwrap();
    let args = vec![];
    
    let result = map_verb_to_sql("sort", &args, &session);
    assert!(result.is_err());
    if let Err(e) = result {
        assert!(e.to_string().contains("Invalid argument"));
    }
}