// tests/table_script/mathematical_operations_tests.rs use crate::common::setup_isolated_db; use server::table_script::handlers::post_table_script::post_table_script; // Fixed import use common::proto::komp_ac::table_script::PostTableScriptRequest; use common::proto::komp_ac::table_definition::ColumnDefinition; use rstest::*; use serde_json::json; use sqlx::PgPool; /// Helper function to create a test table with specified columns async fn create_test_table( pool: &PgPool, schema_id: i64, table_name: &str, columns: Vec, ) -> i64 { let columns_json = serde_json::to_value(columns).unwrap(); let indexes_json = serde_json::json!([]); sqlx::query_scalar!( r#"INSERT INTO table_definitions (schema_id, table_name, columns, indexes) VALUES ($1, $2, $3, $4) RETURNING id"#, schema_id, table_name, columns_json, indexes_json ) .fetch_one(pool) .await .expect("Failed to create test table") } /// Helper function to get default schema ID async fn get_default_schema_id(pool: &PgPool) -> i64 { sqlx::query_scalar!("SELECT id FROM schemas WHERE name = 'default'") .fetch_one(pool) .await .expect("Failed to get default schema ID") } /// Test fixture providing all mathematical operations that should work with steel_decimal #[fixture] #[once] pub fn steel_decimal_operations() -> Vec<(&'static str, &'static str, &'static str)> { vec![ // Basic arithmetic ("+", "addition", "binary"), ("-", "subtraction", "binary"), ("*", "multiplication", "binary"), ("/", "division", "binary"), // Advanced math ("sqrt", "square_root", "unary"), ("abs", "absolute_value", "unary"), ("min", "minimum", "binary"), ("max", "maximum", "binary"), ("pow", "power", "binary"), // Comparison operations (">", "greater_than", "binary"), ("<", "less_than", "binary"), ("=", "equals", "binary"), (">=", "greater_equal", "binary"), ("<=", "less_equal", "binary"), ] } /// Test fixture providing precision test scenarios #[fixture] #[once] pub fn precision_scenarios() -> Vec<(&'static str, &'static str, &'static str)> { vec![ ("NUMERIC(5, 2)", "999.99", "Low precision"), ("NUMERIC(10, 4)", "999999.9999", "Medium precision"), ("NUMERIC(28, 15)", "9999999999999.999999999999999", "High precision"), ("NUMERIC(14, 4)", "9999999999.9999", "Currency precision"), ("NUMERIC(5, 4)", "9.9999", "Percentage precision"), ] } #[rstest] #[case::basic_addition("+", "10", "20")] #[case::decimal_multiplication("*", "100.50", "2.5")] #[case::high_precision_division("/", "123.456789012345", "3")] #[case::scientific_notation("+", "1.5e-10", "2.3e-8")] #[tokio::test] async fn test_steel_decimal_literal_operations( #[case] operation: &str, #[case] value1: &str, #[case] value2: &str, ) { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "result".to_string(), field_type: "NUMERIC(30, 15)".to_string() } ]; let table_id = create_test_table(&pool, schema_id, "literal_test", columns).await; let script = format!(r#"({} "{}" "{}")"#, operation, value1, value2); let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "result".to_string(), script, description: format!("Steel decimal {} with literals", operation), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!( result.is_ok(), "Steel decimal {} with literals should succeed", operation ); } #[rstest] #[case::integer_basic("INTEGER", "+")] #[case::integer_multiplication("INTEGER", "*")] #[case::integer_sqrt("INTEGER", "sqrt")] #[case::numeric_basic("NUMERIC(10, 2)", "+")] #[case::numeric_division("NUMERIC(15, 6)", "/")] #[case::numeric_power("NUMERIC(8, 4)", "pow")] #[case::high_precision("NUMERIC(28, 15)", "*")] #[tokio::test] async fn test_steel_decimal_column_operations( #[case] column_type: &str, #[case] operation: &str, ) { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "test_value".to_string(), field_type: column_type.to_string() }, ColumnDefinition { name: "result".to_string(), field_type: "NUMERIC(30, 15)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "column_test", columns).await; let script = match operation { "sqrt" | "abs" => { format!( r#"({} (steel_get_column "column_test" "test_value"))"#, operation ) } _ => { format!( r#"({} (steel_get_column "column_test" "test_value") "10")"#, operation ) } }; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "result".to_string(), script, description: format!("Steel decimal {} with {} column", operation, column_type), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!( result.is_ok(), "Steel decimal {} with {} column should succeed", operation, column_type ); } #[rstest] #[tokio::test] async fn test_complex_financial_calculation( precision_scenarios: &Vec<(&'static str, &'static str, &'static str)>, ) { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; // Create a realistic financial calculation table let columns: Vec = vec![ ColumnDefinition { name: "principal".to_string(), field_type: "NUMERIC(16, 2)".to_string() }, // Principal amount ColumnDefinition { name: "annual_rate".to_string(), field_type: "NUMERIC(6, 5)".to_string() }, // Interest rate ColumnDefinition { name: "years".to_string(), field_type: "INTEGER".to_string() }, // Time period ColumnDefinition { name: "compounding_periods".to_string(), field_type: "INTEGER".to_string() }, // Compounding frequency ColumnDefinition { name: "compound_interest".to_string(), field_type: "NUMERIC(20, 8)".to_string() }, // Result ]; let table_id = create_test_table(&pool, schema_id, "financial_calc", columns).await; // Complex compound interest formula: P * (1 + r/n)^(n*t) let compound_script = r#" (* (steel_get_column "financial_calc" "principal") (pow (+ "1" (/ (steel_get_column "financial_calc" "annual_rate") (steel_get_column "financial_calc" "compounding_periods"))) (* (steel_get_column "financial_calc" "years") (steel_get_column "financial_calc" "compounding_periods")))) "#; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "compound_interest".to_string(), script: compound_script.to_string(), description: "Complex compound interest calculation".to_string(), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!(result.is_ok(), "Complex financial calculation should succeed"); } #[tokio::test] async fn test_scientific_precision_calculations() { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "measurement_a".to_string(), field_type: "NUMERIC(25, 15)".to_string() }, ColumnDefinition { name: "measurement_b".to_string(), field_type: "NUMERIC(25, 15)".to_string() }, ColumnDefinition { name: "coefficient".to_string(), field_type: "NUMERIC(10, 8)".to_string() }, ColumnDefinition { name: "scientific_result".to_string(), field_type: "NUMERIC(30, 18)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "scientific_data", columns).await; // Complex scientific calculation with high precision let scientific_script = r#" (+ (sqrt (pow (steel_get_column "scientific_data" "measurement_a") "2")) (* (steel_get_column "scientific_data" "coefficient") (abs (- (steel_get_column "scientific_data" "measurement_b") (steel_get_column "scientific_data" "measurement_a"))))) "#; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "scientific_result".to_string(), script: scientific_script.to_string(), description: "High precision scientific calculation".to_string(), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!(result.is_ok(), "High precision scientific calculation should succeed"); } #[rstest] #[case::min_precision("NUMERIC(1, 0)", "Minimum precision")] #[case::max_scale("NUMERIC(10, 10)", "Maximum relative scale")] #[case::currency_standard("NUMERIC(14, 4)", "Standard currency precision")] #[case::percentage_precision("NUMERIC(5, 4)", "Percentage precision")] #[tokio::test] async fn test_precision_boundary_conditions( #[case] numeric_type: &str, #[case] description: &str, ) { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "boundary_value".to_string(), field_type: numeric_type.to_string() }, ColumnDefinition { name: "result".to_string(), field_type: "NUMERIC(30, 15)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "boundary_test", columns).await; let script = r#"(+ (steel_get_column "boundary_test" "boundary_value") "0.00001")"#; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "result".to_string(), script: script.to_string(), description: description.to_string(), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!(result.is_ok(), "{} should work with steel_decimal", description); } #[tokio::test] async fn test_mixed_integer_and_numeric_operations() { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "integer_quantity".to_string(), field_type: "INTEGER".to_string() }, ColumnDefinition { name: "numeric_price".to_string(), field_type: "NUMERIC(10, 4)".to_string() }, ColumnDefinition { name: "numeric_tax_rate".to_string(), field_type: "NUMERIC(5, 4)".to_string() }, ColumnDefinition { name: "total_with_tax".to_string(), field_type: "NUMERIC(15, 4)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "mixed_types_calc", columns).await; // Calculate total with tax: (quantity * price) * (1 + tax_rate) let mixed_script = r#" (* (* (steel_get_column "mixed_types_calc" "integer_quantity") (steel_get_column "mixed_types_calc" "numeric_price")) (+ "1" (steel_get_column "mixed_types_calc" "numeric_tax_rate"))) "#; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "total_with_tax".to_string(), script: mixed_script.to_string(), description: "Mixed INTEGER and NUMERIC calculation".to_string(), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!(result.is_ok(), "Mixed INTEGER and NUMERIC operations should succeed"); } #[rstest] #[case::zero_division("/", "0", "Division by zero should be handled")] #[case::negative_sqrt("sqrt", "-1", "Square root of negative should be handled")] #[case::large_power("pow", "999999999", "Very large power should be handled")] #[tokio::test] async fn test_mathematical_edge_cases( #[case] operation: &str, #[case] problematic_value: &str, #[case] description: &str, ) { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "test_value".to_string(), field_type: "NUMERIC(15, 6)".to_string() }, ColumnDefinition { name: "result".to_string(), field_type: "NUMERIC(20, 8)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "edge_case_test", columns).await; let script = match operation { "sqrt" => { format!(r#"(sqrt "{}")"#, problematic_value) } "/" => { format!(r#"(/ "10" "{}")"#, problematic_value) } "pow" => { format!(r#"(pow "2" "{}")"#, problematic_value) } _ => { format!(r#"({} "10" "{}")"#, operation, problematic_value) } }; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "result".to_string(), script, description: description.to_string(), // Fixed: removed Some() }; // Note: These operations should either succeed (if steel_decimal handles them gracefully) // or fail with appropriate error messages (if they're genuinely problematic) let result = post_table_script(&pool, request).await; // For now, we just ensure the validation doesn't crash // The specific behavior (success vs failure) depends on steel_decimal implementation match result { Ok(_) => { // Steel decimal handled the edge case gracefully } Err(error) => { // Should fail with meaningful error message, not a crash let error_msg = error.to_string(); assert!( !error_msg.contains("panic") && !error_msg.contains("internal error"), "Should fail gracefully, not crash: {}", error_msg ); } } } #[tokio::test] async fn test_comparison_operations_with_valid_types() { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "value_a".to_string(), field_type: "NUMERIC(10, 2)".to_string() }, ColumnDefinition { name: "value_b".to_string(), field_type: "INTEGER".to_string() }, ColumnDefinition { name: "comparison_result".to_string(), field_type: "BOOLEAN".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "comparison_test", columns).await; let comparison_operations = vec![">", "<", "=", ">=", "<="]; for operation in comparison_operations { let script = format!( r#"({} (steel_get_column "comparison_test" "value_a") (steel_get_column "comparison_test" "value_b"))"#, operation ); let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "comparison_result".to_string(), script, description: format!("Comparison operation: {}", operation), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!( result.is_ok(), "Comparison operation {} should succeed with allowed types", operation ); } } #[tokio::test] async fn test_nested_mathematical_expressions() { let pool = setup_isolated_db().await; let schema_id = get_default_schema_id(&pool).await; let columns: Vec = vec![ ColumnDefinition { name: "x".to_string(), field_type: "NUMERIC(15, 8)".to_string() }, ColumnDefinition { name: "y".to_string(), field_type: "NUMERIC(15, 8)".to_string() }, ColumnDefinition { name: "z".to_string(), field_type: "INTEGER".to_string() }, ColumnDefinition { name: "nested_result".to_string(), field_type: "NUMERIC(25, 12)".to_string() }, ]; let table_id = create_test_table(&pool, schema_id, "nested_calc", columns).await; // Deeply nested expression: sqrt((x^2 + y^2) / z) + abs(x - y) let nested_script = r#" (+ (sqrt (/ (+ (pow (steel_get_column "nested_calc" "x") "2") (pow (steel_get_column "nested_calc" "y") "2")) (steel_get_column "nested_calc" "z"))) (abs (- (steel_get_column "nested_calc" "x") (steel_get_column "nested_calc" "y")))) "#; let request = PostTableScriptRequest { table_definition_id: table_id, target_column: "nested_result".to_string(), script: nested_script.to_string(), description: "Deeply nested mathematical expression".to_string(), // Fixed: removed Some() }; let result = post_table_script(&pool, request).await; assert!(result.is_ok(), "Deeply nested mathematical expressions should succeed"); }