mockforge_analytics/
config.rs1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AnalyticsConfig {
9 pub enabled: bool,
11
12 pub database_path: PathBuf,
14
15 #[serde(default = "default_aggregation_interval")]
17 pub aggregation_interval_seconds: u64,
18
19 #[serde(default = "default_rollup_interval")]
21 pub rollup_interval_hours: u64,
22
23 #[serde(default)]
25 pub retention: RetentionConfig,
26
27 #[serde(default = "default_batch_size")]
29 pub batch_size: usize,
30
31 #[serde(default = "default_max_query_results")]
33 pub max_query_results: usize,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct RetentionConfig {
39 #[serde(default = "default_minute_retention")]
41 pub minute_aggregates_days: u32,
42
43 #[serde(default = "default_hour_retention")]
45 pub hour_aggregates_days: u32,
46
47 #[serde(default = "default_day_retention")]
49 pub day_aggregates_days: u32,
50
51 #[serde(default = "default_error_retention")]
53 pub error_events_days: u32,
54
55 #[serde(default = "default_client_retention")]
57 pub client_analytics_days: u32,
58
59 #[serde(default = "default_traffic_retention")]
61 pub traffic_patterns_days: u32,
62
63 #[serde(default = "default_snapshot_retention")]
65 pub snapshots_days: u32,
66
67 #[serde(default = "default_cleanup_interval")]
69 pub cleanup_interval_hours: u32,
70}
71
72impl Default for AnalyticsConfig {
73 fn default() -> Self {
74 Self {
75 enabled: true,
76 database_path: PathBuf::from("mockforge-analytics.db"),
77 aggregation_interval_seconds: default_aggregation_interval(),
78 rollup_interval_hours: default_rollup_interval(),
79 retention: RetentionConfig::default(),
80 batch_size: default_batch_size(),
81 max_query_results: default_max_query_results(),
82 }
83 }
84}
85
86impl Default for RetentionConfig {
87 fn default() -> Self {
88 Self {
89 minute_aggregates_days: default_minute_retention(),
90 hour_aggregates_days: default_hour_retention(),
91 day_aggregates_days: default_day_retention(),
92 error_events_days: default_error_retention(),
93 client_analytics_days: default_client_retention(),
94 traffic_patterns_days: default_traffic_retention(),
95 snapshots_days: default_snapshot_retention(),
96 cleanup_interval_hours: default_cleanup_interval(),
97 }
98 }
99}
100
101const fn default_aggregation_interval() -> u64 {
104 60 }
106
107const fn default_rollup_interval() -> u64 {
108 1 }
110
111const fn default_batch_size() -> usize {
112 1000
113}
114
115const fn default_max_query_results() -> usize {
116 10000
117}
118
119const fn default_minute_retention() -> u32 {
120 7 }
122
123const fn default_hour_retention() -> u32 {
124 30 }
126
127const fn default_day_retention() -> u32 {
128 365 }
130
131const fn default_error_retention() -> u32 {
132 7 }
134
135const fn default_client_retention() -> u32 {
136 30 }
138
139const fn default_traffic_retention() -> u32 {
140 90 }
142
143const fn default_snapshot_retention() -> u32 {
144 90 }
146
147const fn default_cleanup_interval() -> u32 {
148 24 }
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_analytics_config_default() {
157 let config = AnalyticsConfig::default();
158 assert!(config.enabled);
159 assert_eq!(config.database_path, PathBuf::from("mockforge-analytics.db"));
160 assert_eq!(config.aggregation_interval_seconds, 60);
161 assert_eq!(config.rollup_interval_hours, 1);
162 assert_eq!(config.batch_size, 1000);
163 assert_eq!(config.max_query_results, 10000);
164 }
165
166 #[test]
167 fn test_retention_config_default() {
168 let config = RetentionConfig::default();
169 assert_eq!(config.minute_aggregates_days, 7);
170 assert_eq!(config.hour_aggregates_days, 30);
171 assert_eq!(config.day_aggregates_days, 365);
172 assert_eq!(config.error_events_days, 7);
173 assert_eq!(config.client_analytics_days, 30);
174 assert_eq!(config.traffic_patterns_days, 90);
175 assert_eq!(config.snapshots_days, 90);
176 assert_eq!(config.cleanup_interval_hours, 24);
177 }
178
179 #[test]
180 fn test_analytics_config_serialize() {
181 let config = AnalyticsConfig::default();
182 let json = serde_json::to_string(&config).unwrap();
183 assert!(json.contains("\"enabled\":true"));
184 assert!(json.contains("\"aggregation_interval_seconds\":60"));
185 }
186
187 #[test]
188 fn test_analytics_config_deserialize() {
189 let json = r#"{
190 "enabled": false,
191 "database_path": "/tmp/test.db",
192 "aggregation_interval_seconds": 120,
193 "rollup_interval_hours": 2,
194 "batch_size": 500,
195 "max_query_results": 5000
196 }"#;
197 let config: AnalyticsConfig = serde_json::from_str(json).unwrap();
198 assert!(!config.enabled);
199 assert_eq!(config.database_path, PathBuf::from("/tmp/test.db"));
200 assert_eq!(config.aggregation_interval_seconds, 120);
201 assert_eq!(config.rollup_interval_hours, 2);
202 assert_eq!(config.batch_size, 500);
203 assert_eq!(config.max_query_results, 5000);
204 }
205
206 #[test]
207 fn test_retention_config_serialize() {
208 let config = RetentionConfig::default();
209 let json = serde_json::to_string(&config).unwrap();
210 assert!(json.contains("\"minute_aggregates_days\":7"));
211 assert!(json.contains("\"hour_aggregates_days\":30"));
212 }
213
214 #[test]
215 fn test_retention_config_deserialize() {
216 let json = r#"{
217 "minute_aggregates_days": 14,
218 "hour_aggregates_days": 60,
219 "day_aggregates_days": 180,
220 "error_events_days": 30,
221 "client_analytics_days": 60,
222 "traffic_patterns_days": 45,
223 "snapshots_days": 120,
224 "cleanup_interval_hours": 12
225 }"#;
226 let config: RetentionConfig = serde_json::from_str(json).unwrap();
227 assert_eq!(config.minute_aggregates_days, 14);
228 assert_eq!(config.hour_aggregates_days, 60);
229 assert_eq!(config.day_aggregates_days, 180);
230 assert_eq!(config.error_events_days, 30);
231 assert_eq!(config.client_analytics_days, 60);
232 assert_eq!(config.traffic_patterns_days, 45);
233 assert_eq!(config.snapshots_days, 120);
234 assert_eq!(config.cleanup_interval_hours, 12);
235 }
236
237 #[test]
238 fn test_analytics_config_clone() {
239 let config = AnalyticsConfig::default();
240 let cloned = config.clone();
241 assert_eq!(config.enabled, cloned.enabled);
242 assert_eq!(config.database_path, cloned.database_path);
243 }
244
245 #[test]
246 fn test_retention_config_clone() {
247 let config = RetentionConfig::default();
248 let cloned = config.clone();
249 assert_eq!(config.minute_aggregates_days, cloned.minute_aggregates_days);
250 assert_eq!(config.hour_aggregates_days, cloned.hour_aggregates_days);
251 }
252
253 #[test]
254 fn test_analytics_config_with_defaults_in_partial_json() {
255 let json = r#"{
256 "enabled": true,
257 "database_path": "/data/analytics.db"
258 }"#;
259 let config: AnalyticsConfig = serde_json::from_str(json).unwrap();
260 assert_eq!(config.aggregation_interval_seconds, 60);
262 assert_eq!(config.rollup_interval_hours, 1);
263 assert_eq!(config.batch_size, 1000);
264 }
265
266 #[test]
267 fn test_retention_config_debug() {
268 let config = RetentionConfig::default();
269 let debug = format!("{:?}", config);
270 assert!(debug.contains("RetentionConfig"));
271 assert!(debug.contains("minute_aggregates_days"));
272 }
273
274 #[test]
275 fn test_analytics_config_debug() {
276 let config = AnalyticsConfig::default();
277 let debug = format!("{:?}", config);
278 assert!(debug.contains("AnalyticsConfig"));
279 assert!(debug.contains("enabled"));
280 assert!(debug.contains("database_path"));
281 }
282}