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<()> {
23 if self.servers.is_empty() {
24 return Err(anyhow::anyhow!(
25 "Configuration must have at least one server"
26 ));
27 }
28
29 for server in &self.servers {
30 validate_server(server)?;
31 }
32
33 Ok(())
34 }
35}
36
37fn validate_server(server: &Server) -> Result<()> {
39 if let Some(keepalive) = server.connection_keepalive {
46 if keepalive < MIN_RECOMMENDED_KEEPALIVE {
47 tracing::warn!(
48 "Server '{}' has connection_keepalive set to {:?} (< {:?}). \
49 This may cause excessive health check traffic and connection churn. \
50 Consider using at least {:?} or None to disable.",
51 server.name.as_str(),
52 keepalive,
53 MIN_RECOMMENDED_KEEPALIVE,
54 MIN_RECOMMENDED_KEEPALIVE
55 );
56 } else if keepalive > MAX_RECOMMENDED_KEEPALIVE {
57 tracing::warn!(
58 "Server '{}' has connection_keepalive set to {:?} (> {:?} / 5 minutes). \
59 This may not detect stale connections quickly enough. Consider a lower value.",
60 server.name.as_str(),
61 keepalive,
62 MAX_RECOMMENDED_KEEPALIVE
63 );
64 }
65 }
66
67 Ok(())
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 fn create_test_server(name: &str, keepalive: Option<Duration>) -> Server {
75 let mut builder = Server::builder("localhost", 119).name(name);
76
77 if let Some(ka) = keepalive {
78 builder = builder.connection_keepalive(ka);
79 }
80
81 builder.build().unwrap()
82 }
83
84 #[test]
85 fn test_validate_empty_config_fails() {
86 let config = Config {
87 servers: vec![],
88 ..Default::default()
89 };
90 assert!(config.validate().is_err());
91 }
92
93 #[test]
94 fn test_validate_single_server_succeeds() {
95 let config = Config {
96 servers: vec![create_test_server("test", None)],
97 ..Default::default()
98 };
99 assert!(config.validate().is_ok());
100 }
101
102 #[test]
103 fn test_validate_multiple_servers_succeeds() {
104 let config = Config {
105 servers: vec![
106 create_test_server("server1", None),
107 create_test_server("server2", None),
108 ],
109 ..Default::default()
110 };
111 assert!(config.validate().is_ok());
112 }
113
114 #[test]
115 fn test_validate_server_with_recommended_keepalive() {
116 let server = create_test_server("test", Some(Duration::from_secs(60)));
117 assert!(validate_server(&server).is_ok());
118 }
119
120 #[test]
121 fn test_validate_server_with_low_keepalive_warns() {
122 let server = create_test_server("test", Some(Duration::from_secs(5)));
124 assert!(validate_server(&server).is_ok());
125 }
126
127 #[test]
128 fn test_validate_server_with_high_keepalive_warns() {
129 let server = create_test_server("test", Some(Duration::from_secs(600)));
131 assert!(validate_server(&server).is_ok());
132 }
133
134 #[test]
135 fn test_validate_server_with_no_keepalive() {
136 let server = create_test_server("test", None);
137 assert!(validate_server(&server).is_ok());
138 }
139
140 #[test]
141 fn test_validate_server_at_min_boundary() {
142 let server = create_test_server("test", Some(MIN_RECOMMENDED_KEEPALIVE));
143 assert!(validate_server(&server).is_ok());
144 }
145
146 #[test]
147 fn test_validate_server_at_max_boundary() {
148 let server = create_test_server("test", Some(MAX_RECOMMENDED_KEEPALIVE));
149 assert!(validate_server(&server).is_ok());
150 }
151}