sal/rhai/tests/core_tests.rs
Mahmoud-Emad 8012a66250
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
feat: Add Rhai scripting support
- Add new `sal-rhai` crate for Rhai scripting integration
- Integrate Rhai with existing SAL modules
- Improve error handling for Rhai scripts and SAL functions
- Add comprehensive unit and integration tests for `sal-rhai`
2025-06-23 16:23:51 +03:00

270 lines
8.7 KiB
Rust

//! Tests for sal-rhai core module functionality
//!
//! These tests verify the core Rhai integration functions work correctly.
use rhai::Engine;
use sal_rhai::{error::ToRhaiError, register};
use std::fs;
use tempfile::TempDir;
/// Test the ToRhaiError trait implementation
#[test]
fn test_to_rhai_error_trait() {
// Test with a standard Result<T, E> where E implements std::error::Error
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "File not found");
let result: Result<String, std::io::Error> = Err(io_error);
let rhai_result = result.to_rhai_error();
assert!(rhai_result.is_err(), "Should convert to Rhai error");
let error = rhai_result.unwrap_err();
let error_str = error.to_string();
assert!(
error_str.contains("File not found"),
"Error message should be preserved: {}",
error_str
);
}
/// Test the ToRhaiError trait with successful result
#[test]
fn test_to_rhai_error_success() {
let result: Result<String, std::io::Error> = Ok("success".to_string());
let rhai_result = result.to_rhai_error();
assert!(rhai_result.is_ok(), "Should preserve successful result");
assert_eq!(rhai_result.unwrap(), "success", "Value should be preserved");
}
/// Test core module registration
#[test]
fn test_core_module_registration() {
let mut engine = Engine::new();
// Register only the core module
let result = sal_rhai::core::register_core_module(&mut engine);
assert!(
result.is_ok(),
"Core module registration should succeed: {:?}",
result
);
// Verify exec function is registered
let script = r#"exec("42")"#;
let result = engine.eval::<i64>(script);
assert!(
result.is_ok(),
"Exec function should be available: {:?}",
result
);
assert_eq!(
result.unwrap(),
42,
"Exec should return the evaluated result"
);
}
/// Test exec function with direct code execution
#[test]
fn test_exec_direct_code() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Test simple arithmetic
let result = engine.eval::<i64>(r#"exec("10 + 20")"#);
assert!(result.is_ok(), "Direct code execution failed: {:?}", result);
assert_eq!(result.unwrap(), 30, "Should return 30");
// Test string operations
let result = engine.eval::<String>(r#"exec(`"Hello" + " " + "World"`)"#);
assert!(result.is_ok(), "String operation failed: {:?}", result);
assert_eq!(result.unwrap(), "Hello World", "Should concatenate strings");
// Test variable assignment and usage
let result = engine.eval::<i64>(r#"exec("let x = 5; let y = 10; x * y")"#);
assert!(result.is_ok(), "Variable operations failed: {:?}", result);
assert_eq!(result.unwrap(), 50, "Should return 5 * 10 = 50");
}
/// Test exec function with file execution
#[test]
fn test_exec_file_execution() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let script_file = temp_dir.path().join("test_exec.rhai");
// Create a test script file
let script_content = r#"
let numbers = [1, 2, 3, 4, 5];
let sum = 0;
for num in numbers {
sum += num;
}
sum
"#;
fs::write(&script_file, script_content).expect("Failed to write script file");
// Execute the script file
let exec_script = format!(r#"exec("{}")"#, script_file.display());
let result = engine.eval::<i64>(&exec_script);
assert!(result.is_ok(), "File execution failed: {:?}", result);
assert_eq!(result.unwrap(), 15, "Should return sum of 1+2+3+4+5 = 15");
}
/// Test exec function with non-existent file
#[test]
fn test_exec_nonexistent_file() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Try to execute a non-existent file
let result = engine.eval::<i64>(r#"exec(`nonexistent_file_xyz123.rhai`)"#);
assert!(result.is_err(), "Should fail for non-existent file");
let error = result.unwrap_err();
let error_str = error.to_string();
assert!(
error_str.contains("No files found")
|| error_str.contains("File not found")
|| error_str.contains("File system error")
|| error_str.contains("Variable not found"),
"Error should indicate file not found: {}",
error_str
);
}
/// Test exec function with malformed Rhai code
#[test]
fn test_exec_malformed_code() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Test with syntax error
let result = engine.eval::<i64>(r#"exec("let x = ; // malformed")"#);
assert!(result.is_err(), "Should fail for malformed code");
// Test with undefined variable
let result = engine.eval::<i64>(r#"exec("undefined_variable")"#);
assert!(result.is_err(), "Should fail for undefined variable");
}
/// Test exec function with complex nested operations
#[test]
fn test_exec_complex_operations() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
let complex_script = r#"
exec(`
fn factorial(n) {
if n <= 1 {
1
} else {
n * factorial(n - 1)
}
}
factorial(5)
`)
"#;
let result = engine.eval::<i64>(complex_script);
assert!(result.is_ok(), "Complex operation failed: {:?}", result);
assert_eq!(result.unwrap(), 120, "Should return 5! = 120");
}
/// Test exec function with SAL functions
#[test]
fn test_exec_with_sal_functions() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Test using SAL functions within exec
let script = r#"exec(`exist("Cargo.toml")`)"#;
let result = engine.eval::<bool>(script);
assert!(result.is_ok(), "SAL function in exec failed: {:?}", result);
assert!(result.unwrap(), "Cargo.toml should exist");
}
/// Test exec function return types
#[test]
fn test_exec_return_types() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Test boolean return
let result = engine.eval::<bool>(r#"exec("true")"#);
assert!(
result.is_ok() && result.unwrap(),
"Should return boolean true"
);
// Test string return
let result = engine.eval::<String>(r#"exec(`"test string"`)"#);
assert!(result.is_ok(), "String return failed: {:?}", result);
assert_eq!(
result.unwrap(),
"test string",
"Should return correct string"
);
// Test array return
let result = engine.eval::<rhai::Array>(r#"exec("[1, 2, 3]")"#);
assert!(result.is_ok(), "Array return failed: {:?}", result);
let array = result.unwrap();
assert_eq!(array.len(), 3, "Array should have 3 elements");
// Test unit return (no return value)
let result = engine.eval::<()>(r#"exec("let x = 42;")"#);
assert!(result.is_ok(), "Unit return failed: {:?}", result);
}
/// Test error propagation in exec function
#[test]
fn test_exec_error_propagation() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
// Test that errors from executed code are properly propagated
let result = engine.eval::<i64>(r#"exec("1 / 0")"#);
assert!(result.is_err(), "Division by zero should cause error");
// Test that runtime errors are caught
let result = engine.eval::<i64>(r#"exec("throw 'Custom error'")"#);
assert!(result.is_err(), "Thrown errors should be caught");
}
/// Test exec function with file containing SAL operations
#[test]
fn test_exec_file_with_sal_operations() {
let mut engine = Engine::new();
register(&mut engine).expect("Failed to register SAL modules");
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let script_file = temp_dir.path().join("sal_operations.rhai");
// Create a script that uses SAL functions
let script_content = r#"
// Test text processing
let text = " indented text ";
let processed = dedent(text);
let prefixed = prefix(processed, ">> ");
// Return length of processed text
prefixed.len()
"#;
fs::write(&script_file, script_content).expect("Failed to write script file");
// Execute the script file
let exec_script = format!(r#"exec("{}")"#, script_file.display());
let result = engine.eval::<i64>(&exec_script);
assert!(
result.is_ok(),
"SAL operations in file failed: {:?}",
result
);
assert!(result.unwrap() > 0, "Should return positive length");
}