ricecoder_agents/domain/
sequencing_properties.rs

1//! Property-based tests for operation sequencing in domain agent coordination
2//!
3//! **Feature: ricecoder-domain-agents, Property 7: Operation Sequencing**
4//! **Validates: Requirements 4.4**
5
6#[cfg(test)]
7mod tests {
8    use crate::domain::coordinator::{DomainCoordinator, Operation};
9
10    fn create_operation(id: &str, name: &str, priority: u32, dependencies: Vec<&str>) -> Operation {
11        Operation {
12            id: id.to_string(),
13            name: name.to_string(),
14            priority,
15            dependencies: dependencies.iter().map(|d| d.to_string()).collect(),
16        }
17    }
18
19    /// Property 7: Operation Sequencing
20    /// For any cross-domain task execution, the Domain Agent System SHALL sequence
21    /// operations in the correct order (e.g., infrastructure setup before deployment,
22    /// database schema before application deployment).
23    ///
24    /// This property tests that:
25    /// 1. Operations are sorted by priority
26    /// 2. Lower priority values execute first
27    /// 3. Sequencing is deterministic
28    /// 4. Dependencies are respected
29    #[test]
30    fn property_operation_sequencing_by_priority() {
31        let coordinator = DomainCoordinator::new();
32
33        // Create operations with different priorities
34        let operations = vec![
35            create_operation("deploy", "Deploy Application", 3, vec![]),
36            create_operation("setup", "Setup Infrastructure", 1, vec![]),
37            create_operation("migrate", "Database Migration", 2, vec!["setup"]),
38        ];
39
40        // Sequence multiple times to ensure determinism
41        for _ in 0..5 {
42            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
43
44            // Property: Operations should be sorted by priority
45            assert_eq!(sequenced[0].priority, 1, "First operation should have priority 1");
46            assert_eq!(sequenced[1].priority, 2, "Second operation should have priority 2");
47            assert_eq!(sequenced[2].priority, 3, "Third operation should have priority 3");
48
49            // Property: Setup should come first
50            assert_eq!(sequenced[0].id, "setup", "Setup should be first");
51
52            // Property: Migration should come second
53            assert_eq!(sequenced[1].id, "migrate", "Migration should be second");
54
55            // Property: Deploy should come last
56            assert_eq!(sequenced[2].id, "deploy", "Deploy should be last");
57        }
58    }
59
60    /// Property 7: Operation Sequencing (Deterministic)
61    /// For any set of operations, sequencing them multiple times SHALL produce
62    /// identical results.
63    #[test]
64    fn property_operation_sequencing_deterministic() {
65        let coordinator = DomainCoordinator::new();
66
67        let operations = vec![
68            create_operation("op1", "Operation 1", 5, vec![]),
69            create_operation("op2", "Operation 2", 2, vec![]),
70            create_operation("op3", "Operation 3", 8, vec![]),
71            create_operation("op4", "Operation 4", 1, vec![]),
72            create_operation("op5", "Operation 5", 3, vec![]),
73        ];
74
75        // Sequence multiple times
76        let mut results = Vec::new();
77        for _ in 0..5 {
78            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
79            results.push(sequenced);
80        }
81
82        // Property: All sequences should be identical
83        let first = &results[0];
84        for result in &results[1..] {
85            assert_eq!(result.len(), first.len(), "All sequences should have same length");
86
87            for (i, op) in result.iter().enumerate() {
88                assert_eq!(op.id, first[i].id, "Operation {} ID should be consistent", i);
89                assert_eq!(op.priority, first[i].priority, "Operation {} priority should be consistent", i);
90            }
91        }
92    }
93
94    /// Property 7: Operation Sequencing (Dependency Respect)
95    /// For any operations with dependencies, dependencies SHALL be satisfied
96    /// before dependent operations.
97    #[test]
98    fn property_operation_sequencing_respects_dependencies() {
99        let coordinator = DomainCoordinator::new();
100
101        // Create operations with explicit dependencies
102        let operations = vec![
103            create_operation("app-deploy", "Deploy Application", 3, vec!["db-migrate"]),
104            create_operation("db-migrate", "Database Migration", 2, vec!["infra-setup"]),
105            create_operation("infra-setup", "Setup Infrastructure", 1, vec![]),
106        ];
107
108        for _ in 0..5 {
109            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
110
111            // Property: Infrastructure setup should come first
112            assert_eq!(sequenced[0].id, "infra-setup", "Infrastructure setup should be first");
113
114            // Property: Database migration should come second
115            assert_eq!(sequenced[1].id, "db-migrate", "Database migration should be second");
116
117            // Property: Application deployment should come last
118            assert_eq!(sequenced[2].id, "app-deploy", "Application deployment should be last");
119
120            // Property: Each operation should come after its dependencies
121            let mut seen_ids = std::collections::HashSet::new();
122            for op in &sequenced {
123                for dep in &op.dependencies {
124                    assert!(
125                        seen_ids.contains(dep),
126                        "Dependency {} should be executed before {}",
127                        dep,
128                        op.id
129                    );
130                }
131                seen_ids.insert(op.id.clone());
132            }
133        }
134    }
135
136    /// Property 7: Operation Sequencing (Empty Input)
137    /// For any empty input, sequencing SHALL produce empty output.
138    #[test]
139    fn property_operation_sequencing_empty_input() {
140        let coordinator = DomainCoordinator::new();
141
142        for _ in 0..5 {
143            let sequenced = coordinator.sequence_operations(vec![]).unwrap();
144
145            // Property: Empty input should produce empty output
146            assert!(sequenced.is_empty(), "Empty input should produce empty output");
147        }
148    }
149
150    /// Property 7: Operation Sequencing (Single Operation)
151    /// For any single operation, sequencing SHALL return it unchanged.
152    #[test]
153    fn property_operation_sequencing_single_operation() {
154        let coordinator = DomainCoordinator::new();
155
156        let operation = create_operation("single", "Single Operation", 5, vec![]);
157
158        for _ in 0..5 {
159            let sequenced = coordinator.sequence_operations(vec![operation.clone()]).unwrap();
160
161            // Property: Single operation should be returned unchanged
162            assert_eq!(sequenced.len(), 1, "Should have one operation");
163            assert_eq!(sequenced[0].id, "single", "Operation ID should be preserved");
164            assert_eq!(sequenced[0].priority, 5, "Operation priority should be preserved");
165        }
166    }
167
168    /// Property 7: Operation Sequencing (Stable Sort)
169    /// For any operations with equal priority, their relative order SHALL be preserved.
170    #[test]
171    fn property_operation_sequencing_stable_sort() {
172        let coordinator = DomainCoordinator::new();
173
174        // Create operations with equal priorities
175        let operations = vec![
176            create_operation("op1", "Operation 1", 1, vec![]),
177            create_operation("op2", "Operation 2", 1, vec![]),
178            create_operation("op3", "Operation 3", 1, vec![]),
179        ];
180
181        for _ in 0..5 {
182            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
183
184            // Property: All operations should have same priority
185            for op in &sequenced {
186                assert_eq!(op.priority, 1, "All operations should have priority 1");
187            }
188
189            // Property: Relative order should be preserved (stable sort)
190            assert_eq!(sequenced[0].id, "op1", "First operation should be op1");
191            assert_eq!(sequenced[1].id, "op2", "Second operation should be op2");
192            assert_eq!(sequenced[2].id, "op3", "Third operation should be op3");
193        }
194    }
195
196    /// Property 7: Operation Sequencing (Complex Dependencies)
197    /// For any complex dependency graph, all dependencies SHALL be satisfied.
198    #[test]
199    fn property_operation_sequencing_complex_dependencies() {
200        let coordinator = DomainCoordinator::new();
201
202        // Create a complex dependency graph
203        let operations = vec![
204            create_operation("app", "Deploy App", 5, vec!["db", "cache"]),
205            create_operation("db", "Setup Database", 2, vec!["infra"]),
206            create_operation("cache", "Setup Cache", 3, vec!["infra"]),
207            create_operation("infra", "Setup Infrastructure", 1, vec![]),
208            create_operation("monitor", "Setup Monitoring", 4, vec!["infra"]),
209        ];
210
211        for _ in 0..3 {
212            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
213
214            // Property: Infrastructure should come first
215            assert_eq!(sequenced[0].id, "infra", "Infrastructure should be first");
216
217            // Property: All dependencies should be satisfied
218            let mut seen_ids = std::collections::HashSet::new();
219            for op in &sequenced {
220                for dep in &op.dependencies {
221                    assert!(
222                        seen_ids.contains(dep),
223                        "Dependency {} should be executed before {}",
224                        dep,
225                        op.id
226                    );
227                }
228                seen_ids.insert(op.id.clone());
229            }
230
231            // Property: App deployment should come last (depends on db and cache)
232            assert_eq!(sequenced[sequenced.len() - 1].id, "app", "App deployment should be last");
233        }
234    }
235
236    /// Property 7: Operation Sequencing (Priority Ordering)
237    /// For any operations, lower priority values SHALL execute before higher values.
238    #[test]
239    fn property_operation_sequencing_priority_ordering() {
240        let coordinator = DomainCoordinator::new();
241
242        // Create operations with various priorities
243        let operations = vec![
244            create_operation("op10", "Operation 10", 10, vec![]),
245            create_operation("op5", "Operation 5", 5, vec![]),
246            create_operation("op1", "Operation 1", 1, vec![]),
247            create_operation("op7", "Operation 7", 7, vec![]),
248            create_operation("op3", "Operation 3", 3, vec![]),
249        ];
250
251        for _ in 0..5 {
252            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
253
254            // Property: Operations should be in ascending priority order
255            for i in 0..sequenced.len() - 1 {
256                assert!(
257                    sequenced[i].priority <= sequenced[i + 1].priority,
258                    "Priority should be non-decreasing"
259                );
260            }
261
262            // Property: Specific order should be maintained
263            assert_eq!(sequenced[0].priority, 1);
264            assert_eq!(sequenced[1].priority, 3);
265            assert_eq!(sequenced[2].priority, 5);
266            assert_eq!(sequenced[3].priority, 7);
267            assert_eq!(sequenced[4].priority, 10);
268        }
269    }
270
271    /// Property 7: Operation Sequencing (Preservation of Data)
272    /// For any operations, all operation data SHALL be preserved during sequencing.
273    #[test]
274    fn property_operation_sequencing_data_preservation() {
275        let coordinator = DomainCoordinator::new();
276
277        let operations = vec![
278            create_operation("op1", "Operation 1", 3, vec!["dep1", "dep2"]),
279            create_operation("op2", "Operation 2", 1, vec![]),
280            create_operation("op3", "Operation 3", 2, vec!["dep3"]),
281        ];
282
283        for _ in 0..5 {
284            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
285
286            // Property: All operations should be present
287            assert_eq!(sequenced.len(), 3, "All operations should be present");
288
289            // Property: Operation data should be preserved
290            let op1 = sequenced.iter().find(|op| op.id == "op1").unwrap();
291            assert_eq!(op1.name, "Operation 1");
292            assert_eq!(op1.priority, 3);
293            assert_eq!(op1.dependencies.len(), 2);
294            assert!(op1.dependencies.contains(&"dep1".to_string()));
295            assert!(op1.dependencies.contains(&"dep2".to_string()));
296
297            let op2 = sequenced.iter().find(|op| op.id == "op2").unwrap();
298            assert_eq!(op2.name, "Operation 2");
299            assert_eq!(op2.priority, 1);
300            assert!(op2.dependencies.is_empty());
301
302            let op3 = sequenced.iter().find(|op| op.id == "op3").unwrap();
303            assert_eq!(op3.name, "Operation 3");
304            assert_eq!(op3.priority, 2);
305            assert_eq!(op3.dependencies.len(), 1);
306            assert!(op3.dependencies.contains(&"dep3".to_string()));
307        }
308    }
309
310    /// Property 7: Operation Sequencing (Large Number of Operations)
311    /// For any large number of operations, sequencing SHALL complete successfully
312    /// and maintain correct ordering.
313    #[test]
314    fn property_operation_sequencing_large_number() {
315        let coordinator = DomainCoordinator::new();
316
317        // Create 100 operations with random priorities
318        let mut operations = Vec::new();
319        for i in 0..100 {
320            let priority = (i * 7) % 100; // Pseudo-random priority
321            operations.push(create_operation(
322                &format!("op{}", i),
323                &format!("Operation {}", i),
324                priority as u32,
325                vec![],
326            ));
327        }
328
329        for _ in 0..3 {
330            let sequenced = coordinator.sequence_operations(operations.clone()).unwrap();
331
332            // Property: All operations should be present
333            assert_eq!(sequenced.len(), 100, "All 100 operations should be present");
334
335            // Property: Operations should be sorted by priority
336            for i in 0..sequenced.len() - 1 {
337                assert!(
338                    sequenced[i].priority <= sequenced[i + 1].priority,
339                    "Operations should be sorted by priority"
340                );
341            }
342        }
343    }
344}