1use crate::error::{CleanroomError, Result};
6use crate::validation::span_validator::SpanData;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct OrderExpectation {
12 pub must_precede: Vec<(String, String)>,
14 pub must_follow: Vec<(String, String)>,
16}
17
18impl Default for OrderExpectation {
19 fn default() -> Self {
20 Self::new()
21 }
22}
23
24impl OrderExpectation {
25 pub fn new() -> Self {
27 Self {
28 must_precede: Vec::new(),
29 must_follow: Vec::new(),
30 }
31 }
32
33 pub fn with_must_precede(mut self, edges: Vec<(String, String)>) -> Self {
35 self.must_precede = edges;
36 self
37 }
38
39 pub fn with_must_follow(mut self, edges: Vec<(String, String)>) -> Self {
41 self.must_follow = edges;
42 self
43 }
44
45 pub fn validate(&self, spans: &[SpanData]) -> Result<()> {
47 for (first_name, second_name) in &self.must_precede {
49 self.validate_precedes(spans, first_name, second_name)?;
50 }
51
52 for (first_name, second_name) in &self.must_follow {
54 self.validate_follows(spans, first_name, second_name)?;
55 }
56
57 Ok(())
58 }
59
60 fn validate_precedes(&self, spans: &[SpanData], first: &str, second: &str) -> Result<()> {
61 let first_spans: Vec<_> = spans.iter().filter(|s| s.name == first).collect();
62 let second_spans: Vec<_> = spans.iter().filter(|s| s.name == second).collect();
63
64 if first_spans.is_empty() {
65 return Err(CleanroomError::validation_error(format!(
66 "Order validation failed: span '{}' not found for must_precede constraint",
67 first
68 )));
69 }
70
71 if second_spans.is_empty() {
72 return Err(CleanroomError::validation_error(format!(
73 "Order validation failed: span '{}' not found for must_precede constraint",
74 second
75 )));
76 }
77
78 let mut found_valid_order = false;
80 for first_span in &first_spans {
81 for second_span in &second_spans {
82 if self.span_precedes(first_span, second_span)? {
83 found_valid_order = true;
84 break;
85 }
86 }
87 if found_valid_order {
88 break;
89 }
90 }
91
92 if !found_valid_order {
93 return Err(CleanroomError::validation_error(format!(
94 "Order validation failed: '{}' must precede '{}' but no valid ordering found",
95 first, second
96 )));
97 }
98
99 Ok(())
100 }
101
102 fn validate_follows(&self, spans: &[SpanData], first: &str, second: &str) -> Result<()> {
103 self.validate_precedes(spans, second, first)
105 }
106
107 fn span_precedes(&self, first: &SpanData, second: &SpanData) -> Result<bool> {
108 let first_end = first.end_time_unix_nano.ok_or_else(|| {
109 CleanroomError::validation_error(format!(
110 "Span '{}' missing end timestamp for order validation",
111 first.name
112 ))
113 })?;
114
115 let second_start = second.start_time_unix_nano.ok_or_else(|| {
116 CleanroomError::validation_error(format!(
117 "Span '{}' missing start timestamp for order validation",
118 second.name
119 ))
120 })?;
121
122 Ok(first_end <= second_start)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use std::collections::HashMap;
131
132 fn create_span(name: &str, start: u64, end: u64) -> SpanData {
133 SpanData {
134 name: name.to_string(),
135 trace_id: "test".to_string(),
136 span_id: format!("span_{}", name),
137 parent_span_id: None,
138 start_time_unix_nano: Some(start),
139 end_time_unix_nano: Some(end),
140 attributes: HashMap::new(),
141 kind: None,
142 events: None,
143 resource_attributes: HashMap::new(),
144 }
145 }
146
147 #[test]
148 fn test_valid_precede_ordering() -> Result<()> {
149 let spans = vec![create_span("A", 1000, 2000), create_span("B", 2000, 3000)];
150
151 let expectation =
152 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
153
154 expectation.validate(&spans)?;
155 Ok(())
156 }
157
158 #[test]
159 fn test_invalid_precede_ordering() {
160 let spans = vec![create_span("A", 2000, 3000), create_span("B", 1000, 2000)];
161
162 let expectation =
163 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
164
165 assert!(expectation.validate(&spans).is_err());
166 }
167
168 #[test]
169 fn test_valid_follow_ordering() -> Result<()> {
170 let spans = vec![create_span("A", 1000, 2000), create_span("B", 2000, 3000)];
171
172 let expectation =
173 OrderExpectation::new().with_must_follow(vec![("B".to_string(), "A".to_string())]);
174
175 expectation.validate(&spans)?;
176 Ok(())
177 }
178
179 #[test]
180 fn test_missing_first_span() {
181 let spans = vec![create_span("B", 2000, 3000)];
182
183 let expectation =
184 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
185
186 let result = expectation.validate(&spans);
187 assert!(result.is_err());
188 assert!(result
189 .unwrap_err()
190 .to_string()
191 .contains("span 'A' not found"));
192 }
193
194 #[test]
195 fn test_missing_second_span() {
196 let spans = vec![create_span("A", 1000, 2000)];
197
198 let expectation =
199 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
200
201 let result = expectation.validate(&spans);
202 assert!(result.is_err());
203 assert!(result
204 .unwrap_err()
205 .to_string()
206 .contains("span 'B' not found"));
207 }
208
209 #[test]
210 fn test_missing_end_timestamp() {
211 let mut span_a = create_span("A", 1000, 2000);
212 span_a.end_time_unix_nano = None;
213 let span_b = create_span("B", 2000, 3000);
214
215 let spans = vec![span_a, span_b];
216
217 let expectation =
218 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
219
220 let result = expectation.validate(&spans);
221 assert!(result.is_err());
222 assert!(result
223 .unwrap_err()
224 .to_string()
225 .contains("missing end timestamp"));
226 }
227
228 #[test]
229 fn test_missing_start_timestamp() {
230 let span_a = create_span("A", 1000, 2000);
231 let mut span_b = create_span("B", 2000, 3000);
232 span_b.start_time_unix_nano = None;
233
234 let spans = vec![span_a, span_b];
235
236 let expectation =
237 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
238
239 let result = expectation.validate(&spans);
240 assert!(result.is_err());
241 assert!(result
242 .unwrap_err()
243 .to_string()
244 .contains("missing start timestamp"));
245 }
246
247 #[test]
248 fn test_overlapping_spans_invalid_precede() {
249 let spans = vec![create_span("A", 1000, 2500), create_span("B", 2000, 3000)];
250
251 let expectation =
252 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
253
254 assert!(expectation.validate(&spans).is_err());
256 }
257
258 #[test]
259 fn test_exact_boundary_valid_precede() -> Result<()> {
260 let spans = vec![create_span("A", 1000, 2000), create_span("B", 2000, 3000)];
261
262 let expectation =
263 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
264
265 expectation.validate(&spans)?;
267 Ok(())
268 }
269
270 #[test]
271 fn test_multiple_precede_constraints() -> Result<()> {
272 let spans = vec![
273 create_span("A", 1000, 2000),
274 create_span("B", 2000, 3000),
275 create_span("C", 3000, 4000),
276 ];
277
278 let expectation = OrderExpectation::new().with_must_precede(vec![
279 ("A".to_string(), "B".to_string()),
280 ("B".to_string(), "C".to_string()),
281 ]);
282
283 expectation.validate(&spans)?;
284 Ok(())
285 }
286
287 #[test]
288 fn test_multiple_follow_constraints() -> Result<()> {
289 let spans = vec![
290 create_span("A", 1000, 2000),
291 create_span("B", 2000, 3000),
292 create_span("C", 3000, 4000),
293 ];
294
295 let expectation = OrderExpectation::new().with_must_follow(vec![
296 ("B".to_string(), "A".to_string()),
297 ("C".to_string(), "B".to_string()),
298 ]);
299
300 expectation.validate(&spans)?;
301 Ok(())
302 }
303
304 #[test]
305 fn test_multiple_spans_with_same_name() -> Result<()> {
306 let spans = vec![
307 create_span("A", 1000, 1500),
308 create_span("A", 2000, 2500), create_span("B", 3000, 4000),
310 ];
311
312 let expectation =
313 OrderExpectation::new().with_must_precede(vec![("A".to_string(), "B".to_string())]);
314
315 expectation.validate(&spans)?;
317 Ok(())
318 }
319
320 #[test]
321 fn test_empty_constraints() -> Result<()> {
322 let spans = vec![create_span("A", 1000, 2000), create_span("B", 2000, 3000)];
323
324 let expectation = OrderExpectation::new();
325
326 expectation.validate(&spans)?;
328 Ok(())
329 }
330
331 #[test]
332 fn test_default_implementation() {
333 let expectation = OrderExpectation::default();
334 assert!(expectation.must_precede.is_empty());
335 assert!(expectation.must_follow.is_empty());
336 }
337}