mockforge_analytics/
config.rs

1//! Configuration types for the analytics system
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Analytics system configuration
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AnalyticsConfig {
9    /// Whether analytics is enabled
10    pub enabled: bool,
11
12    /// Path to the analytics database file
13    pub database_path: PathBuf,
14
15    /// How often to aggregate metrics from Prometheus (in seconds)
16    #[serde(default = "default_aggregation_interval")]
17    pub aggregation_interval_seconds: u64,
18
19    /// How often to roll up minute data to hour data (in hours)
20    #[serde(default = "default_rollup_interval")]
21    pub rollup_interval_hours: u64,
22
23    /// Data retention policies
24    #[serde(default)]
25    pub retention: RetentionConfig,
26
27    /// Batch size for database operations
28    #[serde(default = "default_batch_size")]
29    pub batch_size: usize,
30
31    /// Maximum number of results to return from queries
32    #[serde(default = "default_max_query_results")]
33    pub max_query_results: usize,
34}
35
36/// Data retention configuration
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct RetentionConfig {
39    /// How long to keep minute-level aggregates (in days)
40    #[serde(default = "default_minute_retention")]
41    pub minute_aggregates_days: u32,
42
43    /// How long to keep hour-level aggregates (in days)
44    #[serde(default = "default_hour_retention")]
45    pub hour_aggregates_days: u32,
46
47    /// How long to keep day-level aggregates (in days)
48    #[serde(default = "default_day_retention")]
49    pub day_aggregates_days: u32,
50
51    /// How long to keep error events (in days)
52    #[serde(default = "default_error_retention")]
53    pub error_events_days: u32,
54
55    /// How long to keep client analytics (in days)
56    #[serde(default = "default_client_retention")]
57    pub client_analytics_days: u32,
58
59    /// How long to keep traffic patterns (in days)
60    #[serde(default = "default_traffic_retention")]
61    pub traffic_patterns_days: u32,
62
63    /// How long to keep analytics snapshots (in days)
64    #[serde(default = "default_snapshot_retention")]
65    pub snapshots_days: u32,
66
67    /// How often to run cleanup (in hours)
68    #[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
101// Default value functions
102
103const fn default_aggregation_interval() -> u64 {
104    60 // 1 minute
105}
106
107const fn default_rollup_interval() -> u64 {
108    1 // 1 hour
109}
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 // 7 days
121}
122
123const fn default_hour_retention() -> u32 {
124    30 // 30 days
125}
126
127const fn default_day_retention() -> u32 {
128    365 // 1 year
129}
130
131const fn default_error_retention() -> u32 {
132    7 // 7 days
133}
134
135const fn default_client_retention() -> u32 {
136    30 // 30 days
137}
138
139const fn default_traffic_retention() -> u32 {
140    90 // 90 days
141}
142
143const fn default_snapshot_retention() -> u32 {
144    90 // 90 days
145}
146
147const fn default_cleanup_interval() -> u32 {
148    24 // Daily
149}
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        // Defaults should be applied for missing fields
261        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}