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");
 | |
| }
 |