1#[cfg(test)]
9mod tests {
10 use crate::{Decision, DecisionContext, DecisionLogger};
11 use std::path::PathBuf;
12
13
14
15 #[tokio::test]
18 async fn prop_decision_capture_preserves_all_metadata() {
19 let logger = DecisionLogger::new();
20
21 let context = DecisionContext {
22 project_path: PathBuf::from("/project1"),
23 file_path: PathBuf::from("/project1/src/main.rs"),
24 line_number: 42,
25 agent_type: "code_generator".to_string(),
26 };
27
28 let decision = Decision::new(
29 context.clone(),
30 "code_generation".to_string(),
31 serde_json::json!({"input": "test"}),
32 serde_json::json!({"output": "result"}),
33 );
34
35 let decision_id = decision.id.clone();
36 let decision_type = decision.decision_type.clone();
37
38 let result = logger.log_decision(decision).await;
40 assert!(result.is_ok());
41 assert_eq!(result.unwrap(), decision_id);
42
43 let retrieved = logger.get_decision(&decision_id).await;
45 assert!(retrieved.is_ok());
46
47 let retrieved_decision = retrieved.unwrap();
48
49 assert_eq!(retrieved_decision.id, decision_id);
51 assert_eq!(retrieved_decision.decision_type, decision_type);
52 assert_eq!(retrieved_decision.context.project_path, context.project_path);
53 assert_eq!(retrieved_decision.context.file_path, context.file_path);
54 assert_eq!(retrieved_decision.context.line_number, context.line_number);
55 assert_eq!(retrieved_decision.context.agent_type, context.agent_type);
56 assert!(retrieved_decision.timestamp.timestamp() > 0);
57 }
58
59 #[tokio::test]
62 async fn prop_all_decisions_appear_in_history() {
63 let logger = DecisionLogger::new();
64
65 let mut decision_ids = Vec::new();
66 for i in 0..50 {
67 let context = DecisionContext {
68 project_path: PathBuf::from(format!("/project{}", i % 5)),
69 file_path: PathBuf::from(format!("/project{}/src/file{}.rs", i % 5, i)),
70 line_number: (i * 10) as u32,
71 agent_type: format!("agent_{}", i % 3),
72 };
73
74 let decision = Decision::new(
75 context,
76 format!("type_{}", i % 4),
77 serde_json::json!({"index": i}),
78 serde_json::json!({"result": i * 2}),
79 );
80
81 decision_ids.push(decision.id.clone());
82 let result = logger.log_decision(decision).await;
83 assert!(result.is_ok());
84 }
85
86 let history = logger.get_history().await;
88 assert_eq!(history.len(), decision_ids.len());
89
90 for (i, decision_id) in decision_ids.iter().enumerate() {
91 assert_eq!(history[i].id, *decision_id);
92 }
93 }
94
95 #[tokio::test]
98 async fn prop_decisions_filterable_by_type() {
99 let logger = DecisionLogger::new();
100
101 let mut type_counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
102
103 for i in 0..50 {
104 let context = DecisionContext {
105 project_path: PathBuf::from("/project"),
106 file_path: PathBuf::from("/project/src/main.rs"),
107 line_number: i as u32,
108 agent_type: "agent".to_string(),
109 };
110
111 let decision_type = format!("type_{}", i % 4);
112 *type_counts.entry(decision_type.clone()).or_insert(0) += 1;
113
114 let decision = Decision::new(
115 context,
116 decision_type,
117 serde_json::json!({}),
118 serde_json::json!({}),
119 );
120
121 let result = logger.log_decision(decision).await;
122 assert!(result.is_ok());
123 }
124
125 for (decision_type, expected_count) in type_counts {
127 let filtered = logger.get_history_by_type(&decision_type).await;
128 assert_eq!(filtered.len(), expected_count);
129
130 for decision in filtered {
132 assert_eq!(decision.decision_type, decision_type);
133 }
134 }
135 }
136
137 #[tokio::test]
140 async fn prop_decisions_filterable_by_context() {
141 let logger = DecisionLogger::new();
142
143 let context1 = DecisionContext {
144 project_path: PathBuf::from("/project1"),
145 file_path: PathBuf::from("/project1/src/main.rs"),
146 line_number: 10,
147 agent_type: "agent1".to_string(),
148 };
149
150 let context2 = DecisionContext {
151 project_path: PathBuf::from("/project2"),
152 file_path: PathBuf::from("/project2/src/lib.rs"),
153 line_number: 20,
154 agent_type: "agent2".to_string(),
155 };
156
157 for i in 0..25 {
159 let decision = Decision::new(
160 context1.clone(),
161 format!("type_{}", i % 3),
162 serde_json::json!({}),
163 serde_json::json!({}),
164 );
165 let result = logger.log_decision(decision).await;
166 assert!(result.is_ok());
167 }
168
169 for i in 0..25 {
171 let decision = Decision::new(
172 context2.clone(),
173 format!("type_{}", i % 3),
174 serde_json::json!({}),
175 serde_json::json!({}),
176 );
177 let result = logger.log_decision(decision).await;
178 assert!(result.is_ok());
179 }
180
181 let context1_decisions = logger.get_history_by_context(&context1).await;
183 assert_eq!(context1_decisions.len(), 25);
184 for decision in context1_decisions {
185 assert_eq!(decision.context.project_path, context1.project_path);
186 assert_eq!(decision.context.file_path, context1.file_path);
187 }
188
189 let context2_decisions = logger.get_history_by_context(&context2).await;
190 assert_eq!(context2_decisions.len(), 25);
191 for decision in context2_decisions {
192 assert_eq!(decision.context.project_path, context2.project_path);
193 assert_eq!(decision.context.file_path, context2.file_path);
194 }
195 }
196
197 #[tokio::test]
200 async fn prop_replay_preserves_decision_order() {
201 let logger = DecisionLogger::new();
202
203 let mut decision_ids = Vec::new();
204 for i in 0..50 {
205 let context = DecisionContext {
206 project_path: PathBuf::from("/project"),
207 file_path: PathBuf::from("/project/src/main.rs"),
208 line_number: i as u32,
209 agent_type: "agent".to_string(),
210 };
211
212 let decision = Decision::new(
213 context,
214 format!("type_{}", i % 4),
215 serde_json::json!({}),
216 serde_json::json!({}),
217 );
218
219 decision_ids.push(decision.id.clone());
220 let result = logger.log_decision(decision).await;
221 assert!(result.is_ok());
222 }
223
224 let replayed = logger.replay_decisions().await;
226 assert_eq!(replayed.len(), decision_ids.len());
227
228 for (i, decision_id) in decision_ids.iter().enumerate() {
229 assert_eq!(replayed[i].id, *decision_id);
230 }
231 }
232
233 #[tokio::test]
236 async fn prop_decision_count_matches_captured() {
237 let logger = DecisionLogger::new();
238
239 assert_eq!(logger.decision_count().await, 0);
240
241 for i in 0..50 {
242 let context = DecisionContext {
243 project_path: PathBuf::from("/project"),
244 file_path: PathBuf::from("/project/src/main.rs"),
245 line_number: i as u32,
246 agent_type: "agent".to_string(),
247 };
248
249 let decision = Decision::new(
250 context,
251 "type".to_string(),
252 serde_json::json!({}),
253 serde_json::json!({}),
254 );
255
256 let result = logger.log_decision(decision).await;
257 assert!(result.is_ok());
258 assert_eq!(logger.decision_count().await, i + 1);
259 }
260
261 assert_eq!(logger.decision_count().await, 50);
262 }
263
264 #[tokio::test]
267 async fn prop_statistics_accurately_reflect_decisions() {
268 let logger = DecisionLogger::new();
269
270 let mut expected_type_counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
271 let mut expected_agent_counts: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
272
273 for i in 0..50 {
274 let context = DecisionContext {
275 project_path: PathBuf::from("/project"),
276 file_path: PathBuf::from("/project/src/main.rs"),
277 line_number: i as u32,
278 agent_type: format!("agent_{}", i % 3),
279 };
280
281 let decision_type = format!("type_{}", i % 4);
282
283 *expected_type_counts.entry(decision_type.clone()).or_insert(0) += 1;
284 *expected_agent_counts
285 .entry(context.agent_type.clone())
286 .or_insert(0) += 1;
287
288 let decision = Decision::new(
289 context,
290 decision_type,
291 serde_json::json!({}),
292 serde_json::json!({}),
293 );
294
295 let result = logger.log_decision(decision).await;
296 assert!(result.is_ok());
297 }
298
299 let stats = logger.get_statistics().await;
301
302 assert_eq!(stats.total_decisions, 50);
304
305 for (decision_type, expected_count) in expected_type_counts {
307 assert_eq!(stats.decision_types.get(&decision_type), Some(&expected_count));
308 }
309
310 for (agent_type, expected_count) in expected_agent_counts {
312 assert_eq!(stats.agent_types.get(&agent_type), Some(&expected_count));
313 }
314 }
315}