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