ricecoder_learning/
pattern_extraction_property.rs1#[cfg(test)]
6mod tests {
7 use proptest::prelude::*;
8 use crate::{Decision, DecisionContext, PatternCapturer};
9 use std::path::PathBuf;
10
11 fn decision_context_strategy() -> impl Strategy<Value = DecisionContext> {
13 (
14 "/project",
15 "/project/src/main.rs",
16 0u32..1000,
17 "test_agent",
18 )
19 .prop_map(|(project, file, line, agent)| DecisionContext {
20 project_path: PathBuf::from(project),
21 file_path: PathBuf::from(file),
22 line_number: line,
23 agent_type: agent.to_string(),
24 })
25 }
26
27 fn json_value_strategy() -> impl Strategy<Value = serde_json::Value> {
29 prop_oneof![
30 Just(serde_json::json!({})),
31 Just(serde_json::json!({"key": "value"})),
32 Just(serde_json::json!({"number": 42})),
33 Just(serde_json::json!({"array": [1, 2, 3]})),
34 Just(serde_json::json!({"nested": {"inner": "value"}})),
35 ]
36 }
37
38 fn decision_strategy() -> impl Strategy<Value = Decision> {
40 (
41 decision_context_strategy(),
42 "code_generation|refactoring|analysis",
43 json_value_strategy(),
44 json_value_strategy(),
45 )
46 .prop_map(|(context, decision_type, input, output)| {
47 Decision::new(context, decision_type.to_string(), input, output)
48 })
49 }
50
51 #[test]
55 fn prop_pattern_extraction_consistency(
56 ) {
57 proptest!(|(decisions in prop::collection::vec(decision_strategy(), 2..20))| {
58 let capturer = PatternCapturer::new();
59
60 let patterns1 = capturer.extract_patterns(&decisions).expect("First extraction failed");
62 let patterns2 = capturer.extract_patterns(&decisions).expect("Second extraction failed");
63
64 prop_assert_eq!(
66 patterns1.len(),
67 patterns2.len(),
68 "Pattern count should be consistent"
69 );
70
71 let mut sorted1 = patterns1.clone();
73 let mut sorted2 = patterns2.clone();
74 sorted1.sort_by(|a, b| a.pattern_type.cmp(&b.pattern_type).then(b.occurrences.cmp(&a.occurrences)));
75 sorted2.sort_by(|a, b| a.pattern_type.cmp(&b.pattern_type).then(b.occurrences.cmp(&a.occurrences)));
76
77 for (p1, p2) in sorted1.iter().zip(sorted2.iter()) {
79 prop_assert_eq!(&p1.pattern_type, &p2.pattern_type, "Pattern types should match");
80 prop_assert_eq!(&p1.description, &p2.description, "Pattern descriptions should match");
81 prop_assert_eq!(p1.occurrences, p2.occurrences, "Pattern occurrences should match");
82 prop_assert_eq!(
83 p1.examples.len(),
84 p2.examples.len(),
85 "Pattern examples count should match"
86 );
87
88 prop_assert!(p1.confidence >= 0.0 && p1.confidence <= 1.0, "Confidence should be bounded");
90 prop_assert!(p2.confidence >= 0.0 && p2.confidence <= 1.0, "Confidence should be bounded");
91 }
92 });
93 }
94
95 #[test]
99 fn prop_pattern_extraction_deterministic() {
100 proptest!(|(decisions in prop::collection::vec(decision_strategy(), 2..20))| {
101 let capturer = PatternCapturer::new();
102
103 let patterns1 = capturer.extract_patterns(&decisions).expect("Extraction 1 failed");
105 let patterns2 = capturer.extract_patterns(&decisions).expect("Extraction 2 failed");
106 let patterns3 = capturer.extract_patterns(&decisions).expect("Extraction 3 failed");
107
108 prop_assert_eq!(patterns1.len(), patterns2.len(), "Extraction 1 and 2 should match");
110 prop_assert_eq!(patterns2.len(), patterns3.len(), "Extraction 2 and 3 should match");
111
112 let mut sorted1 = patterns1.clone();
114 let mut sorted2 = patterns2.clone();
115 let mut sorted3 = patterns3.clone();
116 sorted1.sort_by(|a, b| a.pattern_type.cmp(&b.pattern_type).then(b.occurrences.cmp(&a.occurrences)));
117 sorted2.sort_by(|a, b| a.pattern_type.cmp(&b.pattern_type).then(b.occurrences.cmp(&a.occurrences)));
118 sorted3.sort_by(|a, b| a.pattern_type.cmp(&b.pattern_type).then(b.occurrences.cmp(&a.occurrences)));
119
120 for (p1, p2) in sorted1.iter().zip(sorted2.iter()) {
122 prop_assert_eq!(&p1.pattern_type, &p2.pattern_type, "Type should match");
123 prop_assert_eq!(p1.occurrences, p2.occurrences, "Occurrences should match");
124 }
125
126 for (p2, p3) in sorted2.iter().zip(sorted3.iter()) {
127 prop_assert_eq!(&p2.pattern_type, &p3.pattern_type, "Type should match");
128 prop_assert_eq!(p2.occurrences, p3.occurrences, "Occurrences should match");
129 }
130 });
131 }
132
133 #[test]
136 fn prop_pattern_examples_from_decisions() {
137 proptest!(|(decisions in prop::collection::vec(decision_strategy(), 2..20))| {
138 let capturer = PatternCapturer::new();
139 let patterns = capturer.extract_patterns(&decisions).expect("Extraction failed");
140
141 for pattern in patterns {
142 for example in &pattern.examples {
144 let found = decisions.iter().any(|d| {
145 d.decision_type == pattern.pattern_type
146 && d.input == example.input
147 && d.output == example.output
148 });
149
150 prop_assert!(
151 found,
152 "Pattern example should come from original decisions"
153 );
154 }
155 }
156 });
157 }
158
159 #[test]
162 fn prop_pattern_extraction_empty_input() {
163 let capturer = PatternCapturer::new();
164 let patterns = capturer.extract_patterns(&[]).expect("Extraction failed");
165 assert!(patterns.is_empty(), "Empty input should produce empty patterns");
166 }
167
168 #[test]
171 fn prop_pattern_extraction_respects_minimum() {
172 proptest!(|(decision in decision_strategy())| {
173 let capturer = PatternCapturer::with_settings(5, 0.5);
174
175 let patterns = capturer.extract_patterns(&[decision]).expect("Extraction failed");
177 prop_assert!(patterns.is_empty(), "Single decision should not produce patterns");
178 });
179 }
180
181 #[test]
184 fn prop_pattern_confidence_consistency() {
185 proptest!(|(decisions in prop::collection::vec(decision_strategy(), 2..20))| {
186 let capturer = PatternCapturer::new();
187
188 let patterns1 = capturer.extract_patterns(&decisions).expect("Extraction 1 failed");
189 let patterns2 = capturer.extract_patterns(&decisions).expect("Extraction 2 failed");
190
191 for pattern in patterns1.iter().chain(patterns2.iter()) {
193 prop_assert!(
194 pattern.confidence >= 0.0 && pattern.confidence <= 1.0,
195 "Confidence should be bounded: {}",
196 pattern.confidence
197 );
198 }
199 });
200 }
201
202 #[test]
205 fn prop_pattern_extraction_order_independent() {
206 proptest!(|(decision in decision_strategy())| {
207 let capturer = PatternCapturer::new();
208
209 let decisions = vec![decision.clone(), decision.clone(), decision.clone()];
211
212 let patterns1 = capturer.extract_patterns(&decisions).expect("Extraction 1 failed");
214
215 let mut reversed = decisions.clone();
217 reversed.reverse();
218 let patterns2 = capturer.extract_patterns(&reversed).expect("Extraction 2 failed");
219
220 prop_assert_eq!(patterns1.len(), patterns2.len(), "Pattern count should match");
222
223 for (p1, p2) in patterns1.iter().zip(patterns2.iter()) {
224 prop_assert_eq!(&p1.pattern_type, &p2.pattern_type, "Pattern type should match");
225 prop_assert_eq!(p1.occurrences, p2.occurrences, "Occurrences should match");
226 }
227 });
228 }
229}