kora_lib/validator/
signer_validator.rs1use crate::{
2 error::KoraError,
3 signer::{SelectionStrategy, SignerPoolConfig},
4};
5
6pub struct SignerValidator {}
7
8impl SignerValidator {
9 pub fn validate_with_result(config: &SignerPoolConfig) -> (Vec<String>, Vec<String>) {
11 let mut errors = Vec::new();
12 let mut warnings = Vec::new();
13
14 Self::try_result(config.validate_signer_not_empty(), &mut errors);
16
17 for (index, signer) in config.signers.iter().enumerate() {
19 Self::try_result(signer.validate_individual_signer_config(index), &mut errors);
20 }
21
22 Self::try_result(config.validate_signer_names(), &mut errors);
24
25 Self::try_result(config.validate_strategy_weights(), &mut errors);
27
28 Self::validate_strategy_warnings(config, &mut warnings);
30
31 (warnings, errors)
32 }
33
34 fn try_result(result: Result<(), KoraError>, errors: &mut Vec<String>) {
36 if let Err(KoraError::ValidationError(msg)) = result {
37 errors.push(msg);
38 }
39 }
40
41 fn validate_strategy_warnings(config: &SignerPoolConfig, warnings: &mut Vec<String>) {
43 match config.signer_pool.strategy {
44 SelectionStrategy::Weighted => {
45 for signer in &config.signers {
46 if signer.weight.is_none() {
47 warnings.push(format!(
48 "Signer '{}' has no weight specified for weighted strategy",
49 signer.name
50 ));
51 }
52 }
53 }
54 _ => {
55 for signer in &config.signers {
57 if signer.weight.is_some() {
58 warnings.push(format!(
59 "Signer '{}' has weight specified but using {} strategy - weight will be ignored",
60 signer.name,
61 config.signer_pool.strategy
62 ));
63 }
64 }
65 }
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::signer::config::{
74 MemorySignerConfig, SignerConfig, SignerPoolSettings, SignerTypeConfig,
75 };
76
77 #[test]
78 fn test_validate_with_result_warnings() {
79 let _m = crate::tests::config_mock::ConfigMockBuilder::new().build_and_setup();
80 let config = SignerPoolConfig {
81 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
82 signers: vec![SignerConfig {
83 name: "test_signer".to_string(),
84 weight: Some(10), config: SignerTypeConfig::Memory {
86 config: MemorySignerConfig { private_key_env: "TEST_KEY".to_string() },
87 },
88 }],
89 };
90
91 std::env::set_var("TEST_KEY", "dummy");
92 let (warnings, errors) = SignerValidator::validate_with_result(&config);
93
94 assert!(errors.is_empty());
95 assert!(!warnings.is_empty());
96 assert!(warnings[0].contains("weight will be ignored"));
97 std::env::remove_var("TEST_KEY");
98 }
99
100 #[test]
101 fn test_validate_duplicate_names() {
102 let config = SignerPoolConfig {
103 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
104 signers: vec![
105 SignerConfig {
106 name: "duplicate".to_string(),
107 weight: None,
108 config: SignerTypeConfig::Memory {
109 config: MemorySignerConfig { private_key_env: "TEST_KEY_1".to_string() },
110 },
111 },
112 SignerConfig {
113 name: "duplicate".to_string(),
114 weight: None,
115 config: SignerTypeConfig::Memory {
116 config: MemorySignerConfig { private_key_env: "TEST_KEY_2".to_string() },
117 },
118 },
119 ],
120 };
121
122 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
123 assert!(!errors.is_empty());
124 assert!(errors.iter().any(|e| e.contains("Duplicate signer name")));
125 }
126
127 #[test]
128 fn test_validate_with_result_zero_weight() {
129 let config = SignerPoolConfig {
130 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::Weighted },
131 signers: vec![SignerConfig {
132 name: "test_signer".to_string(),
133 weight: Some(0),
134 config: SignerTypeConfig::Memory {
135 config: MemorySignerConfig { private_key_env: "TEST_KEY".to_string() },
136 },
137 }],
138 };
139
140 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
141 assert!(!errors.is_empty());
142 assert!(errors.iter().any(|e| e.contains("weight of 0 in weighted strategy")));
143 }
144
145 #[test]
146 fn test_validate_with_result_empty_signers() {
147 let config = SignerPoolConfig {
148 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
149 signers: vec![],
150 };
151
152 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
153 assert!(!errors.is_empty());
154 assert!(errors.iter().any(|e| e.contains("At least one signer must be configured")));
155 }
156}