reifydb_flow_operator_sdk/testing/
state.rs1use std::collections::HashMap;
5
6use reifydb_core::value::encoded::{EncodedKey, EncodedValues, EncodedValuesLayout, EncodedValuesNamedLayout};
7use reifydb_type::Value;
8
9#[derive(Debug, Clone, Default)]
11pub struct TestStateStore {
12 data: HashMap<EncodedKey, EncodedValues>,
13}
14
15impl TestStateStore {
16 pub fn new() -> Self {
18 Self {
19 data: HashMap::new(),
20 }
21 }
22
23 pub fn get(&self, key: &EncodedKey) -> Option<&EncodedValues> {
25 self.data.get(key)
26 }
27
28 pub fn set(&mut self, key: EncodedKey, value: EncodedValues) {
30 self.data.insert(key, value);
31 }
32
33 pub fn remove(&mut self, key: &EncodedKey) -> Option<EncodedValues> {
35 self.data.remove(key)
36 }
37
38 pub fn contains(&self, key: &EncodedKey) -> bool {
40 self.data.contains_key(key)
41 }
42
43 pub fn len(&self) -> usize {
45 self.data.len()
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.data.is_empty()
51 }
52
53 pub fn clear(&mut self) {
55 self.data.clear();
56 }
57
58 pub fn keys(&self) -> Vec<&EncodedKey> {
60 self.data.keys().collect()
61 }
62
63 pub fn entries(&self) -> Vec<(&EncodedKey, &EncodedValues)> {
65 self.data.iter().map(|(k, v)| (k, v)).collect()
66 }
67
68 pub fn decode_value(&self, key: &EncodedKey, layout: &EncodedValuesLayout) -> Option<Vec<Value>> {
70 self.get(key).map(|encoded| super::helpers::get_values(layout, encoded))
71 }
72
73 pub fn decode_named_value(
75 &self,
76 key: &EncodedKey,
77 layout: &EncodedValuesNamedLayout,
78 ) -> Option<HashMap<String, Value>> {
79 self.get(key).map(|encoded| {
80 let values = super::helpers::get_values(layout.layout(), encoded);
81 layout.names().iter().map(|n| n.as_str().to_string()).zip(values).collect()
82 })
83 }
84
85 pub fn set_value(&mut self, key: EncodedKey, values: &[Value], layout: &EncodedValuesLayout) {
87 let mut encoded = layout.allocate();
88 layout.set_values(&mut encoded, values);
89 self.set(key, encoded);
90 }
91
92 pub fn set_named_value(
94 &mut self,
95 key: EncodedKey,
96 values: &HashMap<String, Value>,
97 layout: &EncodedValuesNamedLayout,
98 ) {
99 let mut encoded = layout.layout().allocate();
100
101 let ordered_values: Vec<Value> = layout
103 .names()
104 .iter()
105 .map(|name| values.get(name.as_str()).cloned().unwrap_or(Value::Undefined))
106 .collect();
107
108 layout.layout().set_values(&mut encoded, &ordered_values);
109 self.set(key, encoded);
110 }
111
112 pub fn snapshot(&self) -> HashMap<EncodedKey, EncodedValues> {
114 self.data.clone()
115 }
116
117 pub fn restore(&mut self, snapshot: HashMap<EncodedKey, EncodedValues>) {
119 self.data = snapshot;
120 }
121
122 pub fn assert_value(&self, key: &EncodedKey, expected: &[Value], layout: &EncodedValuesLayout) {
124 let actual = self.decode_value(key, layout).expect(&format!("Key {:?} not found in state", key));
125 assert_eq!(actual, expected, "State value mismatch for key {:?}", key);
126 }
127
128 pub fn assert_exists(&self, key: &EncodedKey) {
130 assert!(self.contains(key), "Expected key {:?} to exist in state", key);
131 }
132
133 pub fn assert_not_exists(&self, key: &EncodedKey) {
135 assert!(!self.contains(key), "Expected key {:?} to not exist in state", key);
136 }
137
138 pub fn assert_count(&self, expected: usize) {
140 assert_eq!(self.len(), expected, "Expected {} entries in state, found {}", expected, self.len());
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use reifydb_type::Type;
147
148 use super::*;
149 use crate::testing::helpers::encode_key;
150
151 #[test]
152 fn test_state_store_basic_operations() {
153 use reifydb_core::CowVec;
154 let mut store = TestStateStore::new();
155 let key = encode_key("test_key");
156 let value = EncodedValues(CowVec::new(vec![1, 2, 3, 4]));
157
158 assert!(store.is_empty());
159
160 store.set(key.clone(), value.clone());
161 assert_eq!(store.get(&key), Some(&value));
162 assert!(store.contains(&key));
163 assert_eq!(store.len(), 1);
164
165 let removed = store.remove(&key);
166 assert_eq!(removed, Some(value));
167 assert!(store.is_empty());
168 }
169
170 #[test]
171 fn test_state_store_with_layout() {
172 let mut store = TestStateStore::new();
173 let layout = EncodedValuesLayout::new(&[Type::Int8, Type::Utf8]);
174 let key = encode_key("test_key");
175 let values = vec![Value::Int8(42i64), Value::Utf8("hello".into())];
176
177 store.set_value(key.clone(), &values, &layout);
178
179 let decoded = store.decode_value(&key, &layout).unwrap();
180 assert_eq!(decoded, values);
181 }
182
183 #[test]
184 fn test_state_store_with_named_layout() {
185 let mut store = TestStateStore::new();
186 let layout = EncodedValuesNamedLayout::new(vec![
187 ("count".to_string(), Type::Int8),
188 ("name".to_string(), Type::Utf8),
189 ]);
190 let key = encode_key("test_key");
191
192 let mut values = HashMap::new();
193 values.insert("count".to_string(), Value::Int8(10i64));
194 values.insert("name".to_string(), Value::Utf8("test".into()));
195
196 store.set_named_value(key.clone(), &values, &layout);
197
198 let decoded = store.decode_named_value(&key, &layout).unwrap();
199 assert_eq!(decoded, values);
200 }
201
202 #[test]
203 fn test_state_store_snapshot_and_restore() {
204 use reifydb_core::CowVec;
205 let mut store = TestStateStore::new();
206 let key1 = encode_key("key1");
207 let key2 = encode_key("key2");
208
209 store.set(key1.clone(), EncodedValues(CowVec::new(vec![1])));
210 store.set(key2.clone(), EncodedValues(CowVec::new(vec![2])));
211
212 let snapshot = store.snapshot();
213 assert_eq!(snapshot.len(), 2);
214
215 store.clear();
216 assert!(store.is_empty());
217
218 store.restore(snapshot);
219 assert_eq!(store.len(), 2);
220 assert_eq!(store.get(&key1), Some(&EncodedValues(CowVec::new(vec![1]))));
221 assert_eq!(store.get(&key2), Some(&EncodedValues(CowVec::new(vec![2]))));
222 }
223
224 #[test]
225 fn test_state_store_assertions() {
226 let mut store = TestStateStore::new();
227 let layout = EncodedValuesLayout::new(&[Type::Int8]);
228 let key = encode_key("test_key");
229 let values = vec![Value::Int8(100i64)];
230
231 store.set_value(key.clone(), &values, &layout);
232
233 store.assert_exists(&key);
234 store.assert_value(&key, &values, &layout);
235 store.assert_count(1);
236
237 let missing_key = encode_key("missing");
238 store.assert_not_exists(&missing_key);
239 }
240}