1use std::{
5 cell::RefCell,
6 collections::{BTreeMap, HashMap, HashSet},
7 ops::Bound,
8 sync::Arc,
9};
10
11use postcard::to_stdvec;
12use reifydb_core::{
13 common::CommitVersion,
14 encoded::{key::EncodedKey, row::EncodedRow},
15};
16use reifydb_runtime::sync::mutex::Mutex;
17use reifydb_value::{
18 util::cowvec::CowVec,
19 value::{Value, value_type::ValueType},
20};
21
22thread_local! {
23
24
25
26
27
28 static SHARED_DICTS: RefCell<Option<Arc<Mutex<DictionaryData>>>> = const { RefCell::new(None) };
29}
30
31fn shared_dicts() -> Arc<Mutex<DictionaryData>> {
32 SHARED_DICTS.with(|s| {
33 let mut s = s.borrow_mut();
34 if s.is_none() {
35 *s = Some(Arc::new(Mutex::new(DictionaryData::default())));
36 }
37 s.as_ref().unwrap().clone()
38 })
39}
40
41pub fn seed_test_dictionary(name: &str, id: u64, id_type: ValueType, entries: Vec<(u128, Value)>) {
42 let store = shared_dicts();
43 let mut store = store.lock();
44 store.register(name, id, id_type, &entries);
45
46 store.auto_intern.insert(id);
47}
48
49pub fn clear_test_dictionaries() {
50 SHARED_DICTS.with(|s| *s.borrow_mut() = None);
51}
52
53#[derive(Default)]
54struct DictionaryData {
55 by_name: HashMap<String, (u64, u8)>,
56 id_type_by_dict: HashMap<u64, u8>,
57 find: HashMap<(u64, Vec<u8>), (u128, u8)>,
58 get: HashMap<(u64, u128), Vec<u8>>,
59 next_id: HashMap<u64, u128>,
60 auto_intern: HashSet<u64>,
61}
62
63impl DictionaryData {
64 fn register(&mut self, name: &str, id: u64, id_type: ValueType, entries: &[(u128, Value)]) {
65 let id_type_byte = id_type.to_u8();
66 self.by_name.insert(name.to_string(), (id, id_type_byte));
67 self.id_type_by_dict.insert(id, id_type_byte);
68 let mut next = self.next_id.get(&id).copied().unwrap_or(0);
69 for (entry_id, value) in entries {
70 let value_bytes = to_stdvec(value).expect("serialize dictionary value");
71 self.find.insert((id, value_bytes.clone()), (*entry_id, id_type_byte));
72 self.get.insert((id, *entry_id), value_bytes);
73 next = next.max(*entry_id + 1);
74 }
75 self.next_id.insert(id, next);
76 }
77
78 fn find_or_intern(&mut self, dictionary: u64, value_bytes: &[u8]) -> Option<(u128, u8)> {
79 if let Some(v) = self.find.get(&(dictionary, value_bytes.to_vec())) {
80 return Some(*v);
81 }
82 if !self.auto_intern.contains(&dictionary) {
83 return None;
84 }
85 let id_type_byte = *self.id_type_by_dict.get(&dictionary)?;
86 let entry_id = self.next_id.get(&dictionary).copied().unwrap_or(0);
87 self.find.insert((dictionary, value_bytes.to_vec()), (entry_id, id_type_byte));
88 self.get.insert((dictionary, entry_id), value_bytes.to_vec());
89 self.next_id.insert(dictionary, entry_id + 1);
90 Some((entry_id, id_type_byte))
91 }
92}
93
94#[derive(Clone)]
95pub struct TestContext {
96 state_store: Arc<Mutex<HashMap<EncodedKey, EncodedRow>>>,
97 store: Arc<Mutex<BTreeMap<EncodedKey, EncodedRow>>>,
98 dictionaries: Arc<Mutex<DictionaryData>>,
99 version: CommitVersion,
100 logs: Arc<Mutex<Vec<String>>>,
101}
102
103impl Default for TestContext {
104 fn default() -> Self {
105 Self::new(CommitVersion(1))
106 }
107}
108
109impl TestContext {
110 pub fn new(version: CommitVersion) -> Self {
111 let dictionaries = SHARED_DICTS
112 .with(|s| s.borrow().clone())
113 .unwrap_or_else(|| Arc::new(Mutex::new(DictionaryData::default())));
114 Self {
115 state_store: Arc::new(Mutex::new(HashMap::new())),
116 store: Arc::new(Mutex::new(BTreeMap::new())),
117 dictionaries,
118 version,
119 logs: Arc::new(Mutex::new(Vec::new())),
120 }
121 }
122
123 pub fn seed_dictionary(&self, name: &str, id: u64, id_type: ValueType, entries: &[(u128, Value)]) {
124 self.dictionaries.lock().register(name, id, id_type, entries);
125 }
126
127 pub fn seed_dictionary_interning(&self, name: &str, id: u64, id_type: ValueType, entries: &[(u128, Value)]) {
128 let mut d = self.dictionaries.lock();
129 d.register(name, id, id_type, entries);
130 d.auto_intern.insert(id);
131 }
132
133 pub fn dictionary_id_by_name(&self, name: &str) -> Option<u64> {
134 self.dictionaries.lock().by_name.get(name).map(|(id, _)| *id)
135 }
136
137 pub fn dictionary_find(&self, dictionary: u64, value_bytes: &[u8]) -> Option<(u128, u8)> {
138 self.dictionaries.lock().find_or_intern(dictionary, value_bytes)
139 }
140
141 pub fn dictionary_get(&self, dictionary: u64, id: u128) -> Option<Vec<u8>> {
142 self.dictionaries.lock().get.get(&(dictionary, id)).cloned()
143 }
144
145 pub fn state_store(&self) -> &Arc<Mutex<HashMap<EncodedKey, EncodedRow>>> {
146 &self.state_store
147 }
148
149 pub fn logs(&self) -> Vec<String> {
150 self.logs.lock().clone()
151 }
152
153 pub fn clear_logs(&self) {
154 self.logs.lock().clear();
155 }
156
157 pub fn version(&self) -> CommitVersion {
158 self.version
159 }
160
161 pub fn set_version(&mut self, version: CommitVersion) {
162 self.version = version;
163 }
164
165 pub fn get_state(&self, key: &EncodedKey) -> Option<Vec<u8>> {
166 self.state_store.lock().get(key).map(|v| v.0.to_vec())
167 }
168
169 pub fn set_state(&self, key: EncodedKey, value: Vec<u8>) {
170 self.state_store.lock().insert(key, EncodedRow(CowVec::new(value)));
171 }
172
173 pub fn remove_state(&self, key: &EncodedKey) -> Option<Vec<u8>> {
174 self.state_store.lock().remove(key).map(|v| v.0.to_vec())
175 }
176
177 pub fn has_state(&self, key: &EncodedKey) -> bool {
178 self.state_store.lock().contains_key(key)
179 }
180
181 pub fn state_count(&self) -> usize {
182 self.state_store.lock().len()
183 }
184
185 pub fn clear_state(&self) {
186 self.state_store.lock().clear();
187 }
188
189 pub fn state_keys(&self) -> Vec<EncodedKey> {
190 self.state_store.lock().keys().cloned().collect()
191 }
192
193 pub fn store(&self) -> &Arc<Mutex<BTreeMap<EncodedKey, EncodedRow>>> {
194 &self.store
195 }
196
197 pub fn get_store(&self, key: &EncodedKey) -> Option<EncodedRow> {
198 self.store.lock().get(key).cloned()
199 }
200
201 pub fn set_store(&self, key: EncodedKey, value: EncodedRow) {
202 self.store.lock().insert(key, value);
203 }
204
205 pub fn store_range(&self, start: Bound<EncodedKey>, end: Bound<EncodedKey>) -> Vec<(EncodedKey, EncodedRow)> {
206 self.store.lock().range((start, end)).map(|(k, v)| (k.clone(), v.clone())).collect()
207 }
208
209 pub fn store_prefix(&self, prefix: &EncodedKey) -> Vec<(EncodedKey, EncodedRow)> {
210 self.store
211 .lock()
212 .iter()
213 .filter(|(k, _)| k.as_slice().starts_with(prefix.as_slice()))
214 .map(|(k, v)| (k.clone(), v.clone()))
215 .collect()
216 }
217}
218
219#[cfg(test)]
220pub mod tests {
221 use super::*;
222 use crate::testing::helpers::encode_key;
223
224 #[test]
225 fn test_context_state_operations() {
226 let ctx = TestContext::default();
227 let key = encode_key("test_key");
228 let value = vec![1, 2, 3];
229
230 ctx.set_state(key.clone(), value.clone());
232 assert_eq!(ctx.get_state(&key), Some(value.clone()));
233 assert!(ctx.has_state(&key));
234
235 let removed = ctx.remove_state(&key);
237 assert_eq!(removed, Some(value));
238 assert!(!ctx.has_state(&key));
239 assert_eq!(ctx.get_state(&key), None);
240 }
241
242 #[test]
243 fn test_context_logs() {
244 let ctx = TestContext::default();
245
246 ctx.logs.lock().push("Log 1".to_string());
248 ctx.logs.lock().push("Log 2".to_string());
249
250 let logs = ctx.logs();
251 assert_eq!(logs.len(), 2);
252 assert_eq!(logs[0], "Log 1");
253 assert_eq!(logs[1], "Log 2");
254
255 ctx.clear_logs();
256 assert_eq!(ctx.logs().len(), 0);
257 }
258
259 #[test]
260 fn test_context_state_inspection() {
261 let ctx = TestContext::default();
262
263 ctx.set_state(encode_key("key1"), vec![1]);
264 ctx.set_state(encode_key("key2"), vec![2]);
265 ctx.set_state(encode_key("key3"), vec![3]);
266
267 assert_eq!(ctx.state_count(), 3);
268
269 let keys = ctx.state_keys();
270 assert_eq!(keys.len(), 3);
271
272 ctx.clear_state();
273 assert_eq!(ctx.state_count(), 0);
274 }
275}