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`
		
			
				
	
	
		
			341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Tests for sal-rhai error handling functionality
 | |
| //!
 | |
| //! These tests verify that error handling works correctly across all SAL modules.
 | |
| 
 | |
| use rhai::Engine;
 | |
| use sal_rhai::{
 | |
|     error::{SalError, ToRhaiError},
 | |
|     register,
 | |
| };
 | |
| use std::error::Error;
 | |
| 
 | |
| /// Test SalError creation and display
 | |
| #[test]
 | |
| fn test_sal_error_creation() {
 | |
|     let error = SalError::new("TestError", "This is a test error message");
 | |
|     assert_eq!(error.to_string(), "TestError: This is a test error message");
 | |
| 
 | |
|     let fs_error = SalError::FsError("File system operation failed".to_string());
 | |
|     assert_eq!(
 | |
|         fs_error.to_string(),
 | |
|         "File system error: File system operation failed"
 | |
|     );
 | |
| 
 | |
|     let download_error = SalError::DownloadError("Download failed".to_string());
 | |
|     assert_eq!(
 | |
|         download_error.to_string(),
 | |
|         "Download error: Download failed"
 | |
|     );
 | |
| 
 | |
|     let package_error = SalError::PackageError("Package installation failed".to_string());
 | |
|     assert_eq!(
 | |
|         package_error.to_string(),
 | |
|         "Package error: Package installation failed"
 | |
|     );
 | |
| }
 | |
| 
 | |
| /// Test SalError conversion to Rhai error
 | |
| #[test]
 | |
| fn test_sal_error_to_rhai_conversion() {
 | |
|     let sal_error = SalError::new("TestError", "Test message");
 | |
|     let rhai_error: Box<rhai::EvalAltResult> = sal_error.into();
 | |
| 
 | |
|     let error_str = rhai_error.to_string();
 | |
|     assert!(
 | |
|         error_str.contains("TestError: Test message"),
 | |
|         "Error message should be preserved: {}",
 | |
|         error_str
 | |
|     );
 | |
| }
 | |
| 
 | |
| /// Test error handling in file operations
 | |
| #[test]
 | |
| fn test_file_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test accessing non-existent file
 | |
|     let result = engine.eval::<i64>(r#"file_size("definitely_nonexistent_file_xyz123.txt")"#);
 | |
|     assert!(result.is_err(), "Should return error 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 should indicate file issue: {}",
 | |
|         error_str
 | |
|     );
 | |
| }
 | |
| 
 | |
| /// Test error handling in process operations
 | |
| #[test]
 | |
| fn test_process_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test running non-existent command
 | |
|     let result =
 | |
|         engine.eval::<rhai::Dynamic>(r#"run_command("definitely_nonexistent_command_xyz123")"#);
 | |
|     // Note: This might not always fail depending on the system, so we check if it's handled gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in text operations
 | |
| #[test]
 | |
| fn test_text_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test text operations with invalid input (most text operations are quite robust)
 | |
|     // Test template rendering with invalid template
 | |
|     let result = engine.eval::<String>(
 | |
|         r#"
 | |
|         let builder = template_builder_new();
 | |
|         builder = template_string(builder, "{{ invalid_syntax }}");
 | |
|         let template = build_template(builder);
 | |
|         render_template(template, #{})
 | |
|     "#,
 | |
|     );
 | |
| 
 | |
|     // This should either work or fail gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in network operations
 | |
| #[test]
 | |
| fn test_network_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test connecting to invalid host
 | |
|     let result = engine.eval::<bool>(r#"tcp_check("invalid.host.that.does.not.exist.xyz", 80)"#);
 | |
|     assert!(
 | |
|         result.is_ok(),
 | |
|         "TCP check should handle invalid hosts gracefully"
 | |
|     );
 | |
|     // Should return false for invalid hosts (or might return true if DNS resolves)
 | |
|     let tcp_result = result.unwrap();
 | |
|     assert!(
 | |
|         tcp_result == false || tcp_result == true,
 | |
|         "Should return a boolean value"
 | |
|     );
 | |
| 
 | |
|     // Test HTTP request to invalid URL
 | |
|     let result =
 | |
|         engine.eval::<String>(r#"http_get("http://invalid.host.that.does.not.exist.xyz")"#);
 | |
|     // This should either return an error response or handle gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in git operations
 | |
| #[test]
 | |
| fn test_git_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test git operations with invalid repository
 | |
|     let result = engine
 | |
|         .eval::<rhai::Dynamic>(r#"git_clone("invalid://not.a.real.repo.xyz", "/tmp/nonexistent")"#);
 | |
|     // Git operations should handle invalid URLs gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in crypto operations
 | |
| #[test]
 | |
| fn test_crypto_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test crypto operations with invalid input
 | |
|     let result = engine.eval::<String>(r#"decrypt("invalid_encrypted_data", "wrong_key")"#);
 | |
|     // Crypto operations should handle invalid input gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in database operations
 | |
| #[test]
 | |
| fn test_database_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test Redis operations with invalid connection
 | |
|     let result = engine.eval::<String>(r#"redis_get("nonexistent_key")"#);
 | |
|     // Database operations should handle connection issues gracefully
 | |
|     if result.is_err() {
 | |
|         let error = result.unwrap_err();
 | |
|         let error_str = error.to_string();
 | |
|         assert!(!error_str.is_empty(), "Error message should not be empty");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error handling in virtualization operations
 | |
| #[test]
 | |
| fn test_virt_operation_errors() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test buildah operations without buildah installed
 | |
|     let result = engine.eval::<rhai::Dynamic>(
 | |
|         r#"
 | |
|         let builder = bah_new();
 | |
|         builder
 | |
|     "#,
 | |
|     );
 | |
| 
 | |
|     // This should work even if buildah is not installed (returns builder object)
 | |
|     // If the function is not found, that's also acceptable for this test
 | |
|     if result.is_err() {
 | |
|         let error_str = result.unwrap_err().to_string();
 | |
|         assert!(
 | |
|             error_str.contains("ErrorFunctionNotFound") || error_str.contains("Function not found"),
 | |
|             "Should be a function not found error: {}",
 | |
|             error_str
 | |
|         );
 | |
|     } else {
 | |
|         // If it works, that's fine too
 | |
|         assert!(
 | |
|             result.is_ok(),
 | |
|             "Builder creation should work if function is available"
 | |
|         );
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error propagation through 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 SAL functions are properly propagated through exec
 | |
|     let result = engine.eval::<i64>(r#"exec(`file_size("nonexistent_file_xyz123.txt")`)"#);
 | |
|     assert!(result.is_err(), "Errors should propagate through exec");
 | |
| 
 | |
|     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("Invalid character"),
 | |
|         "Error should indicate file issue: {}",
 | |
|         error_str
 | |
|     );
 | |
| }
 | |
| 
 | |
| /// Test ToRhaiError trait with different error types
 | |
| #[test]
 | |
| fn test_to_rhai_error_different_types() {
 | |
|     // Test with std::io::Error
 | |
|     let io_error = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Permission denied");
 | |
|     let result: Result<(), std::io::Error> = Err(io_error);
 | |
|     let rhai_result = result.to_rhai_error();
 | |
|     assert!(rhai_result.is_err());
 | |
|     assert!(rhai_result
 | |
|         .unwrap_err()
 | |
|         .to_string()
 | |
|         .contains("Permission denied"));
 | |
| 
 | |
|     // Test with custom error type
 | |
|     #[derive(Debug)]
 | |
|     struct CustomError(String);
 | |
| 
 | |
|     impl std::fmt::Display for CustomError {
 | |
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|             write!(f, "Custom error: {}", self.0)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     impl Error for CustomError {}
 | |
| 
 | |
|     let custom_error = CustomError("test error".to_string());
 | |
|     let result: Result<(), CustomError> = Err(custom_error);
 | |
|     let rhai_result = result.to_rhai_error();
 | |
|     assert!(rhai_result.is_err());
 | |
|     assert!(rhai_result
 | |
|         .unwrap_err()
 | |
|         .to_string()
 | |
|         .contains("Custom error: test error"));
 | |
| }
 | |
| 
 | |
| /// Test error handling with concurrent operations
 | |
| #[test]
 | |
| fn test_concurrent_error_handling() {
 | |
|     use std::sync::Arc;
 | |
|     use std::thread;
 | |
| 
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test that error handling works correctly in multi-threaded context
 | |
|     let engine = Arc::new(engine);
 | |
|     let handles: Vec<_> = (0..5)
 | |
|         .map(|i| {
 | |
|             let engine = Arc::clone(&engine);
 | |
|             thread::spawn(move || {
 | |
|                 let result =
 | |
|                     engine.eval::<i64>(&format!(r#"file_size("nonexistent_file_{}.txt")"#, i));
 | |
|                 assert!(result.is_err(), "Thread {} should return error", i);
 | |
|             })
 | |
|         })
 | |
|         .collect();
 | |
| 
 | |
|     for handle in handles {
 | |
|         handle.join().expect("Thread should complete successfully");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Test error message formatting and consistency
 | |
| #[test]
 | |
| fn test_error_message_consistency() {
 | |
|     let mut engine = Engine::new();
 | |
|     register(&mut engine).expect("Failed to register SAL modules");
 | |
| 
 | |
|     // Test that similar errors have consistent formatting
 | |
|     let errors = vec![
 | |
|         engine.eval::<i64>(r#"file_size("nonexistent1.txt")"#),
 | |
|         engine.eval::<i64>(r#"file_size("nonexistent2.txt")"#),
 | |
|         engine.eval::<i64>(r#"file_size("nonexistent3.txt")"#),
 | |
|     ];
 | |
| 
 | |
|     for (i, result) in errors.iter().enumerate() {
 | |
|         assert!(result.is_err(), "Error {} should fail", i);
 | |
|         let error_str = result.as_ref().unwrap_err().to_string();
 | |
|         assert!(
 | |
|             !error_str.is_empty(),
 | |
|             "Error message {} should not be empty",
 | |
|             i
 | |
|         );
 | |
|         // All should contain similar error patterns
 | |
|         assert!(
 | |
|             error_str.contains("No files found")
 | |
|                 || error_str.contains("File not found")
 | |
|                 || error_str.contains("File system error"),
 | |
|             "Error {} should have consistent format: {}",
 | |
|             i,
 | |
|             error_str
 | |
|         );
 | |
|     }
 | |
| }
 |