nntp_proxy/config/
validation.rs1use anyhow::Result;
7use std::time::Duration;
8
9use super::types::{Config, Server};
10use crate::constants::pool::{MAX_RECOMMENDED_KEEPALIVE_SECS, MIN_RECOMMENDED_KEEPALIVE_SECS};
11
12const MIN_RECOMMENDED_KEEPALIVE: Duration = Duration::from_secs(MIN_RECOMMENDED_KEEPALIVE_SECS);
13const MAX_RECOMMENDED_KEEPALIVE: Duration = Duration::from_secs(MAX_RECOMMENDED_KEEPALIVE_SECS);
14
15impl Config {
16 pub fn validate(&self) -> Result<()> {
24 if self.servers.is_empty() {
25 return Err(anyhow::anyhow!(
26 "Configuration must have at least one server"
27 ));
28 }
29
30 if self.servers.len() > 8 {
31 return Err(anyhow::anyhow!(
32 "Configuration cannot have more than 8 servers (current limitation: u8 bitset for article availability tracking). \
33 Found {} servers. Consider running multiple proxy instances or file an issue if you need more backends.",
34 self.servers.len()
35 ));
36 }
37
38 for server in &self.servers {
39 validate_server(server)?;
40 }
41
42 Ok(())
43 }
44}
45
46fn validate_server(server: &Server) -> Result<()> {
48 if let Some(keepalive) = server.connection_keepalive {
55 if keepalive < MIN_RECOMMENDED_KEEPALIVE {
56 tracing::warn!(
57 "Server '{}' has connection_keepalive set to {:?} (< {:?}). \
58 This may cause excessive health check traffic and connection churn. \
59 Consider using at least {:?} or None to disable.",
60 server.name.as_str(),
61 keepalive,
62 MIN_RECOMMENDED_KEEPALIVE,
63 MIN_RECOMMENDED_KEEPALIVE
64 );
65 } else if keepalive > MAX_RECOMMENDED_KEEPALIVE {
66 tracing::warn!(
67 "Server '{}' has connection_keepalive set to {:?} (> {:?} / 5 minutes). \
68 This may not detect stale connections quickly enough. Consider a lower value.",
69 server.name.as_str(),
70 keepalive,
71 MAX_RECOMMENDED_KEEPALIVE
72 );
73 }
74 }
75
76 Ok(())
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::types::Port;
83
84 fn create_test_server(name: &str, keepalive: Option<Duration>) -> Server {
85 let mut builder = Server::builder("localhost", Port::try_new(119).unwrap()).name(name);
86
87 if let Some(ka) = keepalive {
88 builder = builder.connection_keepalive(ka);
89 }
90
91 builder.build().unwrap()
92 }
93
94 #[test]
95 fn test_validate_empty_config_fails() {
96 let config = Config {
97 servers: vec![],
98 ..Default::default()
99 };
100 assert!(config.validate().is_err());
101 }
102
103 #[test]
104 fn test_validate_single_server_succeeds() {
105 let config = Config {
106 servers: vec![create_test_server("test", None)],
107 ..Default::default()
108 };
109 assert!(config.validate().is_ok());
110 }
111
112 #[test]
113 fn test_validate_eight_servers_succeeds() {
114 let config = Config {
115 servers: (0..8)
116 .map(|i| create_test_server(&format!("server{}", i), None))
117 .collect(),
118 ..Default::default()
119 };
120 assert!(config.validate().is_ok());
121 }
122
123 #[test]
124 fn test_validate_nine_servers_fails() {
125 let config = Config {
126 servers: (0..9)
127 .map(|i| create_test_server(&format!("server{}", i), None))
128 .collect(),
129 ..Default::default()
130 };
131 let result = config.validate();
132 assert!(result.is_err());
133 assert!(
134 result
135 .unwrap_err()
136 .to_string()
137 .contains("cannot have more than 8 servers")
138 );
139 }
140
141 #[test]
142 fn test_validate_multiple_servers_succeeds() {
143 let config = Config {
144 servers: vec![
145 create_test_server("server1", None),
146 create_test_server("server2", None),
147 ],
148 ..Default::default()
149 };
150 assert!(config.validate().is_ok());
151 }
152
153 #[test]
154 fn test_validate_server_with_recommended_keepalive() {
155 let server = create_test_server("test", Some(Duration::from_secs(60)));
156 assert!(validate_server(&server).is_ok());
157 }
158
159 #[test]
160 fn test_validate_server_with_low_keepalive_warns() {
161 let server = create_test_server("test", Some(Duration::from_secs(5)));
163 assert!(validate_server(&server).is_ok());
164 }
165
166 #[test]
167 fn test_validate_server_with_high_keepalive_warns() {
168 let server = create_test_server("test", Some(Duration::from_secs(600)));
170 assert!(validate_server(&server).is_ok());
171 }
172
173 #[test]
174 fn test_validate_server_with_no_keepalive() {
175 let server = create_test_server("test", None);
176 assert!(validate_server(&server).is_ok());
177 }
178
179 #[test]
180 fn test_validate_server_at_min_boundary() {
181 let server = create_test_server("test", Some(MIN_RECOMMENDED_KEEPALIVE));
182 assert!(validate_server(&server).is_ok());
183 }
184
185 #[test]
186 fn test_validate_server_at_max_boundary() {
187 let server = create_test_server("test", Some(MAX_RECOMMENDED_KEEPALIVE));
188 assert!(validate_server(&server).is_ok());
189 }
190}