openzeppelin_monitor/services/trigger/script/
validation.rs

1//! Trigger script validation implementation.
2//!
3//! This module provides functionality to validate script configuration parameters.
4
5use crate::models::{ConfigError, ScriptLanguage};
6use std::path::Path;
7
8/// Validates script configuration parameters
9///
10/// # Arguments
11/// * `script_path` - Path to the script file
12/// * `language` - The supported script language
13/// * `timeout_ms` - Timeout in milliseconds
14///
15/// # Returns
16/// * `Ok(())` if validation passes
17/// * `Err(ConfigError)` if any validation fails
18#[allow(clippy::result_large_err)]
19pub fn validate_script_config(
20	script_path: &str,
21	language: &ScriptLanguage,
22	timeout_ms: &u32,
23) -> Result<(), ConfigError> {
24	// Validate script path exists
25	if !Path::new(script_path).exists() {
26		return Err(ConfigError::validation_error(
27			format!("Script path does not exist: {}", script_path),
28			None,
29			None,
30		));
31	}
32
33	let script_path_instance = Path::new(script_path);
34	// Validate file extension matches language
35	let extension = script_path_instance
36		.extension()
37		.and_then(|ext| ext.to_str())
38		.unwrap_or("");
39
40	let valid_extension = match language {
41		ScriptLanguage::Python => extension == "py",
42		ScriptLanguage::JavaScript => extension == "js",
43		ScriptLanguage::Bash => extension == "sh",
44	};
45
46	if !valid_extension {
47		return Err(ConfigError::validation_error(
48			format!(
49				"Script file extension does not match specified language {:?}: {}",
50				language, script_path
51			),
52			None,
53			None,
54		));
55	}
56
57	// Validate timeout
58	if *timeout_ms == 0 {
59		return Err(ConfigError::validation_error(
60			"Timeout must be greater than 0".to_string(),
61			None,
62			None,
63		));
64	}
65
66	Ok(())
67}
68
69#[cfg(test)]
70mod tests {
71	use super::*;
72	use std::fs;
73	use tempfile::NamedTempFile;
74
75	#[test]
76	fn test_validate_script_config_valid_python() {
77		let temp_file = NamedTempFile::new().unwrap();
78		let path = temp_file.path().to_str().unwrap().to_string();
79		let python_path = path + ".py";
80		fs::rename(temp_file.path(), &python_path).unwrap();
81
82		let result = validate_script_config(&python_path, &ScriptLanguage::Python, &1000);
83
84		assert!(result.is_ok());
85		fs::remove_file(python_path).unwrap();
86	}
87
88	#[test]
89	fn test_validate_script_config_invalid_path() {
90		let result =
91			validate_script_config("nonexistent_script.py", &ScriptLanguage::Python, &1000);
92
93		assert!(result.is_err());
94		if let Err(e) = result {
95			assert!(e.to_string().contains("Script path does not exist"));
96		}
97	}
98
99	#[test]
100	fn test_validate_script_config_wrong_extension() {
101		let temp_file = NamedTempFile::new().unwrap();
102		let path = temp_file.path().to_str().unwrap().to_string();
103		let wrong_path = path + ".py";
104		fs::rename(temp_file.path(), &wrong_path).unwrap();
105
106		let result = validate_script_config(&wrong_path, &ScriptLanguage::JavaScript, &1000);
107
108		assert!(result.is_err());
109		if let Err(e) = result {
110			assert!(e.to_string().contains("does not match specified language"));
111		}
112		fs::remove_file(wrong_path).unwrap();
113	}
114
115	#[test]
116	fn test_validate_script_config_zero_timeout() {
117		let temp_file = NamedTempFile::new().unwrap();
118		let path = temp_file.path().to_str().unwrap().to_string();
119		let python_path = path + ".py";
120		fs::rename(temp_file.path(), &python_path).unwrap();
121
122		let result = validate_script_config(&python_path, &ScriptLanguage::Python, &0);
123
124		assert!(result.is_err());
125		if let Err(e) = result {
126			assert!(e.to_string().contains("Timeout must be greater than 0"));
127		}
128		fs::remove_file(python_path).unwrap();
129	}
130}