1use reifydb_core::{
5 encoded::{key::EncodedKey, shape::RowShape},
6 interface::change::{Change, Diff},
7 row::Row,
8 value::column::columns::Columns,
9};
10use reifydb_type::value::{Value, row_number::RowNumber};
11
12use super::helpers::get_values;
13use crate::testing::state::TestStateStore;
14
15pub struct ChangeAssertion<'a> {
16 change: &'a Change,
17}
18
19impl<'a> ChangeAssertion<'a> {
20 pub fn new(change: &'a Change) -> Self {
21 Self {
22 change,
23 }
24 }
25
26 pub fn has_diffs(&self, count: usize) -> &Self {
27 assert_eq!(
28 self.change.diffs.len(),
29 count,
30 "Expected {} diffs, found {}",
31 count,
32 self.change.diffs.len()
33 );
34 self
35 }
36
37 pub fn is_empty(&self) -> &Self {
38 assert!(self.change.diffs.is_empty(), "Expected empty change, found {} diffs", self.change.diffs.len());
39 self
40 }
41
42 pub fn has_insert(&self) -> &Self {
43 let has_insert = self.change.diffs.iter().any(|d| matches!(d, Diff::Insert { .. }));
44 assert!(has_insert, "Expected at least one insert diff");
45 self
46 }
47
48 pub fn has_update(&self) -> &Self {
49 let has_update = self.change.diffs.iter().any(|d| matches!(d, Diff::Update { .. }));
50 assert!(has_update, "Expected at least one update diff");
51 self
52 }
53
54 pub fn has_remove(&self) -> &Self {
55 let has_remove = self.change.diffs.iter().any(|d| matches!(d, Diff::Remove { .. }));
56 assert!(has_remove, "Expected at least one remove diff");
57 self
58 }
59
60 pub fn diff_at(&self, index: usize) -> DiffAssertion<'_> {
61 assert!(
62 index < self.change.diffs.len(),
63 "Diff index {} out of range (total: {})",
64 index,
65 self.change.diffs.len()
66 );
67 DiffAssertion::new(&self.change.diffs[index])
68 }
69
70 pub fn inserts(&self) -> Vec<&Columns> {
71 self.change
72 .diffs
73 .iter()
74 .filter_map(|d| match d {
75 Diff::Insert {
76 post,
77 ..
78 } => Some(post.as_ref()),
79 _ => None,
80 })
81 .collect()
82 }
83
84 pub fn updates(&self) -> Vec<(&Columns, &Columns)> {
85 self.change
86 .diffs
87 .iter()
88 .filter_map(|d| match d {
89 Diff::Update {
90 pre,
91 post,
92 ..
93 } => Some((pre.as_ref(), post.as_ref())),
94 _ => None,
95 })
96 .collect()
97 }
98
99 pub fn removes(&self) -> Vec<&Columns> {
100 self.change
101 .diffs
102 .iter()
103 .filter_map(|d| match d {
104 Diff::Remove {
105 pre,
106 ..
107 } => Some(pre.as_ref()),
108 _ => None,
109 })
110 .collect()
111 }
112
113 pub fn has_inserts(&self, count: usize) -> &Self {
114 let actual = self.inserts().len();
115 assert_eq!(actual, count, "Expected {} inserts, found {}", count, actual);
116 self
117 }
118
119 pub fn has_updates(&self, count: usize) -> &Self {
120 let actual = self.updates().len();
121 assert_eq!(actual, count, "Expected {} updates, found {}", count, actual);
122 self
123 }
124
125 pub fn has_removes(&self, count: usize) -> &Self {
126 let actual = self.removes().len();
127 assert_eq!(actual, count, "Expected {} removes, found {}", count, actual);
128 self
129 }
130}
131
132pub struct DiffAssertion<'a> {
133 diff: &'a Diff,
134}
135
136impl<'a> DiffAssertion<'a> {
137 pub fn new(diff: &'a Diff) -> Self {
138 Self {
139 diff,
140 }
141 }
142
143 pub fn is_insert(&self) -> &Columns {
144 match self.diff {
145 Diff::Insert {
146 post,
147 ..
148 } => post,
149 _ => panic!("Expected insert diff, found {:?}", self.diff),
150 }
151 }
152
153 pub fn is_update(&self) -> (&Columns, &Columns) {
154 match self.diff {
155 Diff::Update {
156 pre,
157 post,
158 ..
159 } => (pre, post),
160 _ => panic!("Expected update diff, found {:?}", self.diff),
161 }
162 }
163
164 pub fn is_remove(&self) -> &Columns {
165 match self.diff {
166 Diff::Remove {
167 pre,
168 ..
169 } => pre,
170 _ => panic!("Expected remove diff, found {:?}", self.diff),
171 }
172 }
173}
174
175pub struct RowAssertion<'a> {
176 row: &'a Row,
177}
178
179impl<'a> RowAssertion<'a> {
180 pub fn new(row: &'a Row) -> Self {
181 Self {
182 row,
183 }
184 }
185
186 pub fn has_number(&self, number: impl Into<RowNumber>) -> &Self {
187 let expected = number.into();
188 assert_eq!(
189 self.row.number, expected,
190 "Expected row number {:?}, found {:?}",
191 expected, self.row.number
192 );
193 self
194 }
195
196 pub fn has_values(&self, expected: &[Value]) -> &Self {
197 let actual = get_values(&self.row.shape, &self.row.encoded);
198 assert_eq!(actual, expected, "Row values mismatch. Expected: {:?}, Actual: {:?}", expected, actual);
199 self
200 }
201
202 pub fn has_field(&self, field_name: &str, expected: Value) -> &Self {
203 let values = get_values(&self.row.shape, &self.row.encoded);
204 let field_index =
205 self.row.shape
206 .find_field_index(field_name)
207 .unwrap_or_else(|| panic!("Field '{}' not found in layout", field_name));
208
209 assert_eq!(
210 values[field_index], expected,
211 "Field '{}' mismatch. Expected: {:?}, Actual: {:?}",
212 field_name, expected, values[field_index]
213 );
214 self
215 }
216
217 pub fn values(&self) -> Vec<Value> {
218 get_values(&self.row.shape, &self.row.encoded)
219 }
220}
221
222pub struct StateAssertion<'a> {
223 store: &'a TestStateStore,
224}
225
226impl<'a> StateAssertion<'a> {
227 pub fn new(store: &'a TestStateStore) -> Self {
228 Self {
229 store,
230 }
231 }
232
233 pub fn is_empty(&self) -> &Self {
234 assert!(self.store.is_empty(), "Expected empty state, found {} entries", self.store.len());
235 self
236 }
237
238 pub fn has_entries(&self, count: usize) -> &Self {
239 self.store.assert_count(count);
240 self
241 }
242
243 pub fn has_key(&self, key: &EncodedKey) -> &Self {
244 self.store.assert_exists(key);
245 self
246 }
247
248 pub fn not_has_key(&self, key: &EncodedKey) -> &Self {
249 self.store.assert_not_exists(key);
250 self
251 }
252
253 pub fn key_has_values(&self, key: &EncodedKey, expected: &[Value], shape: &RowShape) -> &Self {
254 self.store.assert_value(key, expected, shape);
255 self
256 }
257
258 pub fn all_keys<F>(&self, predicate: F) -> &Self
259 where
260 F: Fn(&EncodedKey) -> bool,
261 {
262 for key in self.store.keys() {
263 assert!(predicate(key), "Key {:?} did not match predicate", key);
264 }
265 self
266 }
267}
268
269pub trait Assertable {
270 type Assertion<'a>
271 where
272 Self: 'a;
273
274 fn assert(&self) -> Self::Assertion<'_>;
275}
276
277impl Assertable for Change {
278 type Assertion<'a>
279 = ChangeAssertion<'a>
280 where
281 Self: 'a;
282
283 fn assert(&self) -> ChangeAssertion<'_> {
284 ChangeAssertion::new(self)
285 }
286}
287
288impl Assertable for Row {
289 type Assertion<'a>
290 = RowAssertion<'a>
291 where
292 Self: 'a;
293
294 fn assert(&self) -> RowAssertion<'_> {
295 RowAssertion::new(self)
296 }
297}
298
299impl Assertable for TestStateStore {
300 type Assertion<'a>
301 = StateAssertion<'a>
302 where
303 Self: 'a;
304
305 fn assert(&self) -> StateAssertion<'_> {
306 StateAssertion::new(self)
307 }
308}
309
310#[cfg(test)]
311pub mod tests {
312 use reifydb_core::encoded::shape::RowShape;
313 use reifydb_type::value::r#type::Type;
314
315 use super::*;
316 use crate::testing::{
317 builders::{TestChangeBuilder, TestRowBuilder},
318 helpers::encode_key,
319 state::TestStateStore,
320 };
321
322 #[test]
323 fn test_flow_change_assertions() {
324 let change = TestChangeBuilder::new()
325 .insert_row(1, vec![Value::Int8(10i64)])
326 .update_row(2, vec![Value::Int8(20i64)], vec![Value::Int8(30i64)])
327 .remove_row(3, vec![Value::Int8(40i64)])
328 .build();
329
330 change.assert()
331 .has_diffs(3)
332 .has_insert()
333 .has_update()
334 .has_remove()
335 .has_inserts(1)
336 .has_updates(1)
337 .has_removes(1);
338
339 let change_assert = change.assert();
341 let diff_assert = change_assert.diff_at(0);
342 let insert_columns = diff_assert.is_insert();
343 let insert_row = insert_columns.to_single_row();
345 insert_row.assert().has_number(1).has_values(&[Value::Int8(10i64)]);
346 }
347
348 #[test]
349 fn test_row_assertions() {
350 let row = TestRowBuilder::new(42)
351 .with_values(vec![Value::Int8(100i64), Value::Utf8("test".into())])
352 .build();
353
354 row.assert().has_number(42).has_values(&[Value::Int8(100i64), Value::Utf8("test".into())]);
355
356 assert_eq!(row.assert().values().len(), 2);
357 }
358
359 #[test]
360 fn test_state_assertions() {
361 let mut store = TestStateStore::new();
362 let shape = RowShape::testing(&[Type::Int8]);
363 let key1 = encode_key("key1");
364 let key2 = encode_key("key2");
365
366 store.set_value(key1.clone(), &[Value::Int8(10i64)], &shape);
367 store.set_value(key2.clone(), &[Value::Int8(20i64)], &shape);
368
369 store.assert()
370 .has_entries(2)
371 .has_key(&key1)
372 .has_key(&key2)
373 .key_has_values(&key1, &[Value::Int8(10i64)], &shape)
374 .all_keys(|k| k.len() == 6); }
376
377 #[test]
378 #[should_panic(expected = "Expected 5 diffs, found 1")]
379 fn test_assertion_failure() {
380 let change = TestChangeBuilder::new().insert_row(1, vec![Value::Int8(10i64)]).build();
381
382 change.assert().has_diffs(5);
383 }
384}