Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
- 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`
270 lines
8.7 KiB
Rust
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");
|
|
}
|