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 config = SignerPoolConfig {
80 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
81 signers: vec![SignerConfig {
82 name: "test_signer".to_string(),
83 weight: Some(10), config: SignerTypeConfig::Memory {
85 config: MemorySignerConfig { private_key_env: "TEST_KEY".to_string() },
86 },
87 }],
88 };
89
90 let (warnings, errors) = SignerValidator::validate_with_result(&config);
91 assert!(errors.is_empty());
92 assert!(!warnings.is_empty());
93 assert!(warnings[0].contains("weight will be ignored"));
94 }
95
96 #[test]
97 fn test_validate_duplicate_names() {
98 let config = SignerPoolConfig {
99 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
100 signers: vec![
101 SignerConfig {
102 name: "duplicate".to_string(),
103 weight: None,
104 config: SignerTypeConfig::Memory {
105 config: MemorySignerConfig { private_key_env: "TEST_KEY_1".to_string() },
106 },
107 },
108 SignerConfig {
109 name: "duplicate".to_string(),
110 weight: None,
111 config: SignerTypeConfig::Memory {
112 config: MemorySignerConfig { private_key_env: "TEST_KEY_2".to_string() },
113 },
114 },
115 ],
116 };
117
118 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
119 assert!(!errors.is_empty());
120 assert!(errors.iter().any(|e| e.contains("Duplicate signer name")));
121 }
122
123 #[test]
124 fn test_validate_with_result_zero_weight() {
125 let config = SignerPoolConfig {
126 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::Weighted },
127 signers: vec![SignerConfig {
128 name: "test_signer".to_string(),
129 weight: Some(0),
130 config: SignerTypeConfig::Memory {
131 config: MemorySignerConfig { private_key_env: "TEST_KEY".to_string() },
132 },
133 }],
134 };
135
136 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
137 assert!(!errors.is_empty());
138 assert!(errors.iter().any(|e| e.contains("weight of 0 in weighted strategy")));
139 }
140
141 #[test]
142 fn test_validate_with_result_empty_signers() {
143 let config = SignerPoolConfig {
144 signer_pool: SignerPoolSettings { strategy: SelectionStrategy::RoundRobin },
145 signers: vec![],
146 };
147
148 let (_warnings, errors) = SignerValidator::validate_with_result(&config);
149 assert!(!errors.is_empty());
150 assert!(errors.iter().any(|e| e.contains("At least one signer must be configured")));
151 }
152}