// 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::KompAC::table_script::PostTableScriptRequest; 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<(&str, &str)>, ) -> i64 { let column_definitions: Vec = columns .iter() .map(|(name, type_def)| format!("\"{}\" {}", name, type_def)) .collect(); let columns_json = json!(column_definitions); let indexes_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![("result", "NUMERIC(30, 15)")]; 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![ ("test_value", column_type), ("result", "NUMERIC(30, 15)"), ]; 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![ ("principal", "NUMERIC(16, 2)"), // Principal amount ("annual_rate", "NUMERIC(6, 5)"), // Interest rate ("years", "INTEGER"), // Time period ("compounding_periods", "INTEGER"), // Compounding frequency ("compound_interest", "NUMERIC(20, 8)"), // 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![ ("measurement_a", "NUMERIC(25, 15)"), ("measurement_b", "NUMERIC(25, 15)"), ("coefficient", "NUMERIC(10, 8)"), ("scientific_result", "NUMERIC(30, 18)"), ]; 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![ ("boundary_value", numeric_type), ("result", "NUMERIC(30, 15)"), ]; 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![ ("integer_quantity", "INTEGER"), ("numeric_price", "NUMERIC(10, 4)"), ("numeric_tax_rate", "NUMERIC(5, 4)"), ("total_with_tax", "NUMERIC(15, 4)"), ]; 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![ ("test_value", "NUMERIC(15, 6)"), ("result", "NUMERIC(20, 8)"), ]; 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![ ("value_a", "NUMERIC(10, 2)"), ("value_b", "INTEGER"), ("comparison_result", "BOOLEAN"), ]; 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![ ("x", "NUMERIC(15, 8)"), ("y", "NUMERIC(15, 8)"), ("z", "INTEGER"), ("nested_result", "NUMERIC(25, 12)"), ]; 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"); }