Skip to main content

fedimint_core/db/
test_utils.rs

1use std::collections::BTreeMap;
2use std::time::Duration;
3
4use fedimint_core::db::DbMigrationFnContext;
5use futures::future::ready;
6use futures::{Future, FutureExt, StreamExt};
7use rand::Rng;
8use tokio::join;
9
10use super::{
11    Database, DatabaseTransaction, DatabaseVersion, DatabaseVersionKey, DatabaseVersionKeyV0,
12    DbMigrationError, DbMigrationFn, apply_migrations, apply_migrations_dbtx,
13    create_database_version_dbtx,
14};
15use crate::core::ModuleKind;
16use crate::db::mem_impl::MemDatabase;
17use crate::db::{IDatabaseTransactionOps, IDatabaseTransactionOpsCoreTyped, MODULE_GLOBAL_PREFIX};
18use crate::encoding::{Decodable, Encodable};
19use crate::module::registry::ModuleDecoderRegistry;
20
21pub async fn future_returns_shortly<F: Future>(fut: F) -> Option<F::Output> {
22    crate::runtime::timeout(Duration::from_millis(10), fut)
23        .await
24        .ok()
25}
26
27#[repr(u8)]
28#[derive(Clone)]
29pub enum TestDbKeyPrefix {
30    Test = 0x42,
31    AltTest = 0x43,
32    PercentTestKey = 0x25,
33}
34
35#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
36pub(super) struct TestKey(pub u64);
37
38#[derive(Debug, Encodable, Decodable)]
39struct DbPrefixTestPrefix;
40
41impl_db_record!(
42    key = TestKey,
43    value = TestVal,
44    db_prefix = TestDbKeyPrefix::Test,
45    notify_on_modify = true,
46);
47impl_db_lookup!(key = TestKey, query_prefix = DbPrefixTestPrefix);
48
49#[derive(Debug, Encodable, Decodable)]
50struct TestKeyV0(u64, u64);
51
52#[derive(Debug, Encodable, Decodable)]
53struct DbPrefixTestPrefixV0;
54
55impl_db_record!(
56    key = TestKeyV0,
57    value = TestVal,
58    db_prefix = TestDbKeyPrefix::Test,
59);
60impl_db_lookup!(key = TestKeyV0, query_prefix = DbPrefixTestPrefixV0);
61
62#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Encodable, Decodable)]
63struct AltTestKey(u64);
64
65#[derive(Debug, Encodable, Decodable)]
66struct AltDbPrefixTestPrefix;
67
68impl_db_record!(
69    key = AltTestKey,
70    value = TestVal,
71    db_prefix = TestDbKeyPrefix::AltTest,
72);
73impl_db_lookup!(key = AltTestKey, query_prefix = AltDbPrefixTestPrefix);
74
75#[derive(Debug, Encodable, Decodable)]
76struct PercentTestKey(u64);
77
78#[derive(Debug, Encodable, Decodable)]
79struct PercentPrefixTestPrefix;
80
81impl_db_record!(
82    key = PercentTestKey,
83    value = TestVal,
84    db_prefix = TestDbKeyPrefix::PercentTestKey,
85);
86
87impl_db_lookup!(key = PercentTestKey, query_prefix = PercentPrefixTestPrefix);
88#[derive(Debug, Encodable, Decodable, Eq, PartialEq, PartialOrd, Ord)]
89pub(super) struct TestVal(pub u64);
90
91const TEST_MODULE_PREFIX: u16 = 1;
92const ALT_MODULE_PREFIX: u16 = 2;
93
94pub async fn verify_insert_elements(db: Database) {
95    let mut dbtx = db.begin_transaction().await;
96    assert!(dbtx.insert_entry(&TestKey(1), &TestVal(2)).await.is_none());
97    assert!(dbtx.insert_entry(&TestKey(2), &TestVal(3)).await.is_none());
98    dbtx.commit_tx().await;
99
100    // Test values were persisted
101    let mut dbtx = db.begin_transaction().await;
102    assert_eq!(dbtx.get_value(&TestKey(1)).await, Some(TestVal(2)));
103    assert_eq!(dbtx.get_value(&TestKey(2)).await, Some(TestVal(3)));
104    dbtx.commit_tx().await;
105
106    // Test overwrites work as expected
107    let mut dbtx = db.begin_transaction().await;
108    assert_eq!(
109        dbtx.insert_entry(&TestKey(1), &TestVal(4)).await,
110        Some(TestVal(2))
111    );
112    assert_eq!(
113        dbtx.insert_entry(&TestKey(2), &TestVal(5)).await,
114        Some(TestVal(3))
115    );
116    dbtx.commit_tx().await;
117
118    let mut dbtx = db.begin_transaction().await;
119    assert_eq!(dbtx.get_value(&TestKey(1)).await, Some(TestVal(4)));
120    assert_eq!(dbtx.get_value(&TestKey(2)).await, Some(TestVal(5)));
121    dbtx.commit_tx().await;
122}
123
124pub async fn verify_remove_nonexisting(db: Database) {
125    let mut dbtx = db.begin_transaction().await;
126    assert_eq!(dbtx.get_value(&TestKey(1)).await, None);
127    let removed = dbtx.remove_entry(&TestKey(1)).await;
128    assert!(removed.is_none());
129
130    // Commit to suppress the warning message
131    dbtx.commit_tx().await;
132}
133
134pub async fn verify_remove_existing(db: Database) {
135    let mut dbtx = db.begin_transaction().await;
136
137    assert!(dbtx.insert_entry(&TestKey(1), &TestVal(2)).await.is_none());
138
139    assert_eq!(dbtx.get_value(&TestKey(1)).await, Some(TestVal(2)));
140
141    let removed = dbtx.remove_entry(&TestKey(1)).await;
142    assert_eq!(removed, Some(TestVal(2)));
143    assert_eq!(dbtx.get_value(&TestKey(1)).await, None);
144
145    // Commit to suppress the warning message
146    dbtx.commit_tx().await;
147}
148
149pub async fn verify_read_own_writes(db: Database) {
150    let mut dbtx = db.begin_transaction().await;
151
152    assert!(dbtx.insert_entry(&TestKey(1), &TestVal(2)).await.is_none());
153
154    assert_eq!(dbtx.get_value(&TestKey(1)).await, Some(TestVal(2)));
155
156    // Commit to suppress the warning message
157    dbtx.commit_tx().await;
158}
159
160pub async fn verify_prevent_dirty_reads(db: Database) {
161    let mut dbtx = db.begin_transaction().await;
162
163    assert!(dbtx.insert_entry(&TestKey(1), &TestVal(2)).await.is_none());
164
165    // dbtx2 should not be able to see uncommitted changes
166    let mut dbtx2 = db.begin_transaction().await;
167    assert_eq!(dbtx2.get_value(&TestKey(1)).await, None);
168
169    // Commit to suppress the warning message
170    dbtx.commit_tx().await;
171}
172
173pub async fn verify_find_by_range(db: Database) {
174    let mut dbtx = db.begin_transaction().await;
175    dbtx.insert_entry(&TestKey(55), &TestVal(9999)).await;
176    dbtx.insert_entry(&TestKey(54), &TestVal(8888)).await;
177    dbtx.insert_entry(&TestKey(56), &TestVal(7777)).await;
178
179    dbtx.insert_entry(&AltTestKey(55), &TestVal(7777)).await;
180    dbtx.insert_entry(&AltTestKey(54), &TestVal(6666)).await;
181
182    {
183        let mut module_dbtx = dbtx.to_ref_with_prefix_module_id(2).0;
184        module_dbtx
185            .insert_entry(&TestKey(300), &TestVal(3000))
186            .await;
187    }
188
189    dbtx.commit_tx().await;
190
191    // Verify finding by prefix returns the correct set of key pairs
192    let mut dbtx = db.begin_transaction_nc().await;
193
194    let returned_keys = dbtx
195        .find_by_range(TestKey(55)..TestKey(56))
196        .await
197        .collect::<Vec<_>>()
198        .await;
199
200    let expected = vec![(TestKey(55), TestVal(9999))];
201
202    assert_eq!(returned_keys, expected);
203
204    let returned_keys = dbtx
205        .find_by_range(TestKey(54)..TestKey(56))
206        .await
207        .collect::<Vec<_>>()
208        .await;
209
210    let expected = vec![(TestKey(54), TestVal(8888)), (TestKey(55), TestVal(9999))];
211    assert_eq!(returned_keys, expected);
212
213    let returned_keys = dbtx
214        .find_by_range(TestKey(54)..TestKey(57))
215        .await
216        .collect::<Vec<_>>()
217        .await;
218
219    let expected = vec![
220        (TestKey(54), TestVal(8888)),
221        (TestKey(55), TestVal(9999)),
222        (TestKey(56), TestVal(7777)),
223    ];
224    assert_eq!(returned_keys, expected);
225
226    let mut module_dbtx = dbtx.with_prefix_module_id(2).0;
227    let test_range = module_dbtx
228        .find_by_range(TestKey(300)..TestKey(301))
229        .await
230        .collect::<Vec<_>>()
231        .await;
232    assert!(test_range.len() == 1);
233}
234
235pub async fn verify_find_by_prefix(db: Database) {
236    let mut dbtx = db.begin_transaction().await;
237    dbtx.insert_entry(&TestKey(55), &TestVal(9999)).await;
238    dbtx.insert_entry(&TestKey(54), &TestVal(8888)).await;
239
240    dbtx.insert_entry(&AltTestKey(55), &TestVal(7777)).await;
241    dbtx.insert_entry(&AltTestKey(54), &TestVal(6666)).await;
242    dbtx.commit_tx().await;
243
244    // Verify finding by prefix returns the correct set of key pairs
245    let mut dbtx = db.begin_transaction().await;
246
247    let returned_keys = dbtx
248        .find_by_prefix(&DbPrefixTestPrefix)
249        .await
250        .collect::<Vec<_>>()
251        .await;
252
253    let expected = vec![(TestKey(54), TestVal(8888)), (TestKey(55), TestVal(9999))];
254    assert_eq!(returned_keys, expected);
255
256    let reversed = dbtx
257        .find_by_prefix_sorted_descending(&DbPrefixTestPrefix)
258        .await
259        .collect::<Vec<_>>()
260        .await;
261    let mut reversed_expected = expected;
262    reversed_expected.reverse();
263    assert_eq!(reversed, reversed_expected);
264
265    let returned_keys = dbtx
266        .find_by_prefix(&AltDbPrefixTestPrefix)
267        .await
268        .collect::<Vec<_>>()
269        .await;
270
271    let expected = vec![
272        (AltTestKey(54), TestVal(6666)),
273        (AltTestKey(55), TestVal(7777)),
274    ];
275    assert_eq!(returned_keys, expected);
276
277    let reversed = dbtx
278        .find_by_prefix_sorted_descending(&AltDbPrefixTestPrefix)
279        .await
280        .collect::<Vec<_>>()
281        .await;
282    let mut reversed_expected = expected;
283    reversed_expected.reverse();
284    assert_eq!(reversed, reversed_expected);
285}
286
287pub async fn verify_commit(db: Database) {
288    let mut dbtx = db.begin_transaction().await;
289
290    assert!(dbtx.insert_entry(&TestKey(1), &TestVal(2)).await.is_none());
291    dbtx.commit_tx().await;
292
293    // Verify dbtx2 can see committed transactions
294    let mut dbtx2 = db.begin_transaction().await;
295    assert_eq!(dbtx2.get_value(&TestKey(1)).await, Some(TestVal(2)));
296}
297
298pub async fn verify_prevent_nonrepeatable_reads(db: Database) {
299    let mut dbtx = db.begin_transaction().await;
300    assert_eq!(dbtx.get_value(&TestKey(100)).await, None);
301
302    let mut dbtx2 = db.begin_transaction().await;
303
304    dbtx2.insert_entry(&TestKey(100), &TestVal(101)).await;
305
306    assert_eq!(dbtx.get_value(&TestKey(100)).await, None);
307
308    dbtx2.commit_tx().await;
309
310    // dbtx should still read None because it is operating over a snapshot
311    // of the data when the transaction started
312    assert_eq!(dbtx.get_value(&TestKey(100)).await, None);
313
314    let expected_keys = 0;
315    let returned_keys = dbtx
316        .find_by_prefix(&DbPrefixTestPrefix)
317        .await
318        .fold(0, |returned_keys, (key, value)| async move {
319            if key == TestKey(100) {
320                assert!(value.eq(&TestVal(101)));
321            }
322            returned_keys + 1
323        })
324        .await;
325
326    assert_eq!(returned_keys, expected_keys);
327}
328
329pub async fn verify_snapshot_isolation(db: Database) {
330    async fn random_yield() {
331        let times = if rand::thread_rng().gen_bool(0.5) {
332            0
333        } else {
334            10
335        };
336        for _ in 0..times {
337            tokio::task::yield_now().await;
338        }
339    }
340
341    // This scenario is taken straight out of https://github.com/fedimint/fedimint/issues/5195 bug
342    for i in 0..1000 {
343        let base_key = i * 2;
344        let tx_accepted_key = base_key;
345        let spent_input_key = base_key + 1;
346
347        join!(
348            async {
349                random_yield().await;
350                let mut dbtx = db.begin_transaction().await;
351
352                random_yield().await;
353                let a = dbtx.get_value(&TestKey(tx_accepted_key)).await;
354                random_yield().await;
355                // we have 4 operations that can give you the db key,
356                // try all of them
357                let s = match i % 5 {
358                    0 => dbtx.get_value(&TestKey(spent_input_key)).await,
359                    1 => dbtx.remove_entry(&TestKey(spent_input_key)).await,
360                    2 => {
361                        dbtx.insert_entry(&TestKey(spent_input_key), &TestVal(200))
362                            .await
363                    }
364                    3 => {
365                        dbtx.find_by_prefix(&DbPrefixTestPrefix)
366                            .await
367                            .filter(|(k, _v)| ready(k == &TestKey(spent_input_key)))
368                            .map(|(_k, v)| v)
369                            .next()
370                            .await
371                    }
372                    4 => {
373                        dbtx.find_by_prefix_sorted_descending(&DbPrefixTestPrefix)
374                            .await
375                            .filter(|(k, _v)| ready(k == &TestKey(spent_input_key)))
376                            .map(|(_k, v)| v)
377                            .next()
378                            .await
379                    }
380                    _ => {
381                        panic!("woot?");
382                    }
383                };
384
385                match (a, s) {
386                    (None, None) | (Some(_), Some(_)) => {}
387                    (None, Some(_)) => panic!("none some?! {i}"),
388                    (Some(_), None) => panic!("some none?! {i}"),
389                }
390            },
391            async {
392                random_yield().await;
393
394                let mut dbtx = db.begin_transaction().await;
395                random_yield().await;
396                assert_eq!(dbtx.get_value(&TestKey(tx_accepted_key)).await, None);
397
398                random_yield().await;
399                assert_eq!(
400                    dbtx.insert_entry(&TestKey(spent_input_key), &TestVal(100))
401                        .await,
402                    None
403                );
404
405                random_yield().await;
406                assert_eq!(
407                    dbtx.insert_entry(&TestKey(tx_accepted_key), &TestVal(100))
408                        .await,
409                    None
410                );
411                random_yield().await;
412                dbtx.commit_tx().await;
413            }
414        );
415    }
416}
417
418pub async fn verify_phantom_entry(db: Database) {
419    let mut dbtx = db.begin_transaction().await;
420
421    dbtx.insert_entry(&TestKey(100), &TestVal(101)).await;
422
423    dbtx.insert_entry(&TestKey(101), &TestVal(102)).await;
424
425    dbtx.commit_tx().await;
426
427    let mut dbtx = db.begin_transaction().await;
428    let expected_keys = 2;
429    let returned_keys = dbtx
430        .find_by_prefix(&DbPrefixTestPrefix)
431        .await
432        .fold(0, |returned_keys, (key, value)| async move {
433            match key {
434                TestKey(100) => {
435                    assert!(value.eq(&TestVal(101)));
436                }
437                TestKey(101) => {
438                    assert!(value.eq(&TestVal(102)));
439                }
440                _ => {}
441            }
442            returned_keys + 1
443        })
444        .await;
445
446    assert_eq!(returned_keys, expected_keys);
447
448    let mut dbtx2 = db.begin_transaction().await;
449
450    dbtx2.insert_entry(&TestKey(102), &TestVal(103)).await;
451
452    dbtx2.commit_tx().await;
453
454    let returned_keys = dbtx
455        .find_by_prefix(&DbPrefixTestPrefix)
456        .await
457        .fold(0, |returned_keys, (key, value)| async move {
458            match key {
459                TestKey(100) => {
460                    assert!(value.eq(&TestVal(101)));
461                }
462                TestKey(101) => {
463                    assert!(value.eq(&TestVal(102)));
464                }
465                _ => {}
466            }
467            returned_keys + 1
468        })
469        .await;
470
471    assert_eq!(returned_keys, expected_keys);
472}
473
474pub async fn expect_write_conflict(db: Database) {
475    let mut dbtx = db.begin_transaction().await;
476    dbtx.insert_entry(&TestKey(100), &TestVal(101)).await;
477    dbtx.commit_tx().await;
478
479    let mut dbtx2 = db.begin_transaction().await;
480    let mut dbtx3 = db.begin_transaction().await;
481
482    dbtx2.insert_entry(&TestKey(100), &TestVal(102)).await;
483
484    // Depending on if the database implementation supports optimistic or
485    // pessimistic transactions, this test should generate an error here
486    // (pessimistic) or at commit time (optimistic)
487    dbtx3.insert_entry(&TestKey(100), &TestVal(103)).await;
488
489    dbtx2.commit_tx().await;
490    dbtx3.commit_tx_result().await.expect_err("Expecting an error to be returned because this transaction is in a write-write conflict with dbtx");
491}
492
493pub async fn verify_string_prefix(db: Database) {
494    let mut dbtx = db.begin_transaction().await;
495    dbtx.insert_entry(&PercentTestKey(100), &TestVal(101)).await;
496
497    assert_eq!(
498        dbtx.get_value(&PercentTestKey(100)).await,
499        Some(TestVal(101))
500    );
501
502    dbtx.insert_entry(&PercentTestKey(101), &TestVal(100)).await;
503
504    dbtx.insert_entry(&PercentTestKey(101), &TestVal(100)).await;
505
506    dbtx.insert_entry(&PercentTestKey(101), &TestVal(100)).await;
507
508    // If the wildcard character ('%') is not handled properly, this will make
509    // find_by_prefix return 5 results instead of 4
510    dbtx.insert_entry(&TestKey(101), &TestVal(100)).await;
511
512    let expected_keys = 4;
513    let returned_keys = dbtx
514        .find_by_prefix(&PercentPrefixTestPrefix)
515        .await
516        .fold(0, |returned_keys, (key, value)| async move {
517            if matches!(key, PercentTestKey(101)) {
518                assert!(value.eq(&TestVal(100)));
519            }
520            returned_keys + 1
521        })
522        .await;
523
524    assert_eq!(returned_keys, expected_keys);
525}
526
527pub async fn verify_remove_by_prefix(db: Database) {
528    let mut dbtx = db.begin_transaction().await;
529
530    dbtx.insert_entry(&TestKey(100), &TestVal(101)).await;
531
532    dbtx.insert_entry(&TestKey(101), &TestVal(102)).await;
533
534    dbtx.commit_tx().await;
535
536    let mut remove_dbtx = db.begin_transaction().await;
537    remove_dbtx.remove_by_prefix(&DbPrefixTestPrefix).await;
538    remove_dbtx.commit_tx().await;
539
540    let mut dbtx = db.begin_transaction().await;
541    let expected_keys = 0;
542    let returned_keys = dbtx
543        .find_by_prefix(&DbPrefixTestPrefix)
544        .await
545        .fold(0, |returned_keys, (key, value)| async move {
546            match key {
547                TestKey(100) => {
548                    assert!(value.eq(&TestVal(101)));
549                }
550                TestKey(101) => {
551                    assert!(value.eq(&TestVal(102)));
552                }
553                _ => {}
554            }
555            returned_keys + 1
556        })
557        .await;
558
559    assert_eq!(returned_keys, expected_keys);
560}
561
562pub async fn verify_module_db(db: Database, module_db: Database) {
563    let mut dbtx = db.begin_transaction().await;
564
565    dbtx.insert_entry(&TestKey(100), &TestVal(101)).await;
566
567    dbtx.insert_entry(&TestKey(101), &TestVal(102)).await;
568
569    dbtx.commit_tx().await;
570
571    // verify module_dbtx can only read key/value pairs from its own module
572    let mut module_dbtx = module_db.begin_transaction().await;
573    assert_eq!(module_dbtx.get_value(&TestKey(100)).await, None);
574
575    assert_eq!(module_dbtx.get_value(&TestKey(101)).await, None);
576
577    // verify module_dbtx can read key/value pairs that it wrote
578    let mut dbtx = db.begin_transaction().await;
579    assert_eq!(dbtx.get_value(&TestKey(100)).await, Some(TestVal(101)));
580
581    assert_eq!(dbtx.get_value(&TestKey(101)).await, Some(TestVal(102)));
582
583    let mut module_dbtx = module_db.begin_transaction().await;
584
585    module_dbtx.insert_entry(&TestKey(100), &TestVal(103)).await;
586
587    module_dbtx.insert_entry(&TestKey(101), &TestVal(104)).await;
588
589    module_dbtx.commit_tx().await;
590
591    let expected_keys = 2;
592    let mut dbtx = db.begin_transaction().await;
593    let returned_keys = dbtx
594        .find_by_prefix(&DbPrefixTestPrefix)
595        .await
596        .fold(0, |returned_keys, (key, value)| async move {
597            match key {
598                TestKey(100) => {
599                    assert!(value.eq(&TestVal(101)));
600                }
601                TestKey(101) => {
602                    assert!(value.eq(&TestVal(102)));
603                }
604                _ => {}
605            }
606            returned_keys + 1
607        })
608        .await;
609
610    assert_eq!(returned_keys, expected_keys);
611
612    let removed = dbtx.remove_entry(&TestKey(100)).await;
613    assert_eq!(removed, Some(TestVal(101)));
614    assert_eq!(dbtx.get_value(&TestKey(100)).await, None);
615
616    let mut module_dbtx = module_db.begin_transaction().await;
617    assert_eq!(
618        module_dbtx.get_value(&TestKey(100)).await,
619        Some(TestVal(103))
620    );
621}
622
623pub async fn verify_module_prefix(db: Database) {
624    let mut test_dbtx = db.begin_transaction().await;
625    {
626        let mut test_module_dbtx = test_dbtx.to_ref_with_prefix_module_id(TEST_MODULE_PREFIX).0;
627
628        test_module_dbtx
629            .insert_entry(&TestKey(100), &TestVal(101))
630            .await;
631
632        test_module_dbtx
633            .insert_entry(&TestKey(101), &TestVal(102))
634            .await;
635    }
636
637    test_dbtx.commit_tx().await;
638
639    let mut alt_dbtx = db.begin_transaction().await;
640    {
641        let mut alt_module_dbtx = alt_dbtx.to_ref_with_prefix_module_id(ALT_MODULE_PREFIX).0;
642
643        alt_module_dbtx
644            .insert_entry(&TestKey(100), &TestVal(103))
645            .await;
646
647        alt_module_dbtx
648            .insert_entry(&TestKey(101), &TestVal(104))
649            .await;
650    }
651
652    alt_dbtx.commit_tx().await;
653
654    // verify test_module_dbtx can only see key/value pairs from its own module
655    let mut test_dbtx = db.begin_transaction().await;
656    let mut test_module_dbtx = test_dbtx.to_ref_with_prefix_module_id(TEST_MODULE_PREFIX).0;
657    assert_eq!(
658        test_module_dbtx.get_value(&TestKey(100)).await,
659        Some(TestVal(101))
660    );
661
662    assert_eq!(
663        test_module_dbtx.get_value(&TestKey(101)).await,
664        Some(TestVal(102))
665    );
666
667    let expected_keys = 2;
668    let returned_keys = test_module_dbtx
669        .find_by_prefix(&DbPrefixTestPrefix)
670        .await
671        .fold(0, |returned_keys, (key, value)| async move {
672            match key {
673                TestKey(100) => {
674                    assert!(value.eq(&TestVal(101)));
675                }
676                TestKey(101) => {
677                    assert!(value.eq(&TestVal(102)));
678                }
679                _ => {}
680            }
681            returned_keys + 1
682        })
683        .await;
684
685    assert_eq!(returned_keys, expected_keys);
686
687    let removed = test_module_dbtx.remove_entry(&TestKey(100)).await;
688    assert_eq!(removed, Some(TestVal(101)));
689    assert_eq!(test_module_dbtx.get_value(&TestKey(100)).await, None);
690
691    // test_dbtx on its own wont find the key because it does not use a module
692    // prefix
693    let mut test_dbtx = db.begin_transaction().await;
694    assert_eq!(test_dbtx.get_value(&TestKey(101)).await, None);
695
696    test_dbtx.commit_tx().await;
697}
698
699#[cfg(test)]
700#[tokio::test]
701pub async fn verify_test_migration() {
702    // Insert a bunch of old dummy data that needs to be migrated to a new version
703    let db = Database::new(MemDatabase::new(), ModuleDecoderRegistry::default());
704    let expected_test_keys_size: usize = 100;
705    let mut dbtx = db.begin_transaction().await;
706    for i in 0..expected_test_keys_size {
707        dbtx.insert_new_entry(&TestKeyV0(i as u64, (i + 1) as u64), &TestVal(i as u64))
708            .await;
709    }
710
711    // Will also be migrated to `DatabaseVersionKey`
712    dbtx.insert_new_entry(&DatabaseVersionKeyV0, &DatabaseVersion(0))
713        .await;
714    dbtx.commit_tx().await;
715
716    let mut migrations: BTreeMap<DatabaseVersion, DbMigrationFn<()>> = BTreeMap::new();
717
718    migrations.insert(
719        DatabaseVersion(0),
720        Box::new(|ctx| migrate_test_db_version_0(ctx).boxed()),
721    );
722
723    apply_migrations(&db, (), "TestModule".to_string(), migrations, None, None)
724        .await
725        .expect("Error applying migrations for TestModule");
726
727    // Verify that the migrations completed successfully
728    let mut dbtx = db.begin_transaction().await;
729
730    // Verify that the old `DatabaseVersion` under `DatabaseVersionKeyV0` migrated
731    // to `DatabaseVersionKey`
732    assert!(
733        dbtx.get_value(&DatabaseVersionKey(MODULE_GLOBAL_PREFIX.into()))
734            .await
735            .is_some()
736    );
737
738    // Verify Dummy module migration
739    let test_keys = dbtx
740        .find_by_prefix(&DbPrefixTestPrefix)
741        .await
742        .collect::<Vec<_>>()
743        .await;
744    let test_keys_size = test_keys.len();
745    assert_eq!(test_keys_size, expected_test_keys_size);
746    for (key, val) in test_keys {
747        assert_eq!(key.0, val.0 + 1);
748    }
749}
750
751#[cfg(test)]
752#[tokio::test]
753async fn apply_migrations_rejects_newer_on_disk_version() {
754    let db = Database::new(MemDatabase::new(), ModuleDecoderRegistry::default());
755    let mut dbtx = db.begin_transaction().await;
756
757    // Pretend the code that wrote this database was at version 5.
758    create_database_version_dbtx(
759        &mut dbtx.to_ref_nc(),
760        DatabaseVersion(5),
761        None,
762        "test".to_owned(),
763        true,
764    )
765    .await;
766
767    // This code knows no migrations at all, i.e. it is at version 0.
768    let err = apply_migrations_dbtx(
769        &mut dbtx.to_ref_nc(),
770        (),
771        "test".to_owned(),
772        BTreeMap::new(),
773        None,
774        None,
775    )
776    .await
777    .expect_err("on-disk version 5 must not be accepted by code at version 0");
778
779    assert!(matches!(
780        err,
781        DbMigrationError::VersionTooHigh {
782            on_disk: DatabaseVersion(5),
783            target: DatabaseVersion(0),
784            ..
785        }
786    ));
787}
788
789#[allow(dead_code)]
790async fn migrate_test_db_version_0(
791    mut ctx: DbMigrationFnContext<'_, ()>,
792) -> Result<(), DbMigrationError> {
793    let mut dbtx = ctx.dbtx();
794    let example_keys_v0 = dbtx
795        .find_by_prefix(&DbPrefixTestPrefixV0)
796        .await
797        .collect::<Vec<_>>()
798        .await;
799    dbtx.remove_by_prefix(&DbPrefixTestPrefixV0).await;
800    for (key, val) in example_keys_v0 {
801        let key_v2 = TestKey(key.1);
802        dbtx.insert_new_entry(&key_v2, &val).await;
803    }
804    Ok(())
805}
806
807#[cfg(test)]
808#[tokio::test]
809async fn test_autocommit() {
810    use std::marker::PhantomData;
811    use std::ops::Range;
812    use std::path::Path;
813
814    use async_trait::async_trait;
815
816    use crate::ModuleDecoderRegistry;
817    use crate::db::{
818        AutocommitError, BaseDatabaseTransaction, DatabaseError, DatabaseResult,
819        IDatabaseTransaction, IDatabaseTransactionOps, IDatabaseTransactionOpsCore, IRawDatabase,
820        IRawDatabaseTransaction,
821    };
822
823    #[derive(Debug)]
824    struct FakeDatabase;
825
826    #[async_trait]
827    impl IRawDatabase for FakeDatabase {
828        type Transaction<'a> = FakeTransaction<'a>;
829        async fn begin_transaction(&self) -> FakeTransaction {
830            FakeTransaction(PhantomData)
831        }
832
833        fn checkpoint(&self, _backup_path: &Path) -> DatabaseResult<()> {
834            Ok(())
835        }
836    }
837
838    #[derive(Debug)]
839    struct FakeTransaction<'a>(PhantomData<&'a ()>);
840
841    #[async_trait]
842    impl IDatabaseTransactionOpsCore for FakeTransaction<'_> {
843        async fn raw_insert_bytes(
844            &mut self,
845            _key: &[u8],
846            _value: &[u8],
847        ) -> DatabaseResult<Option<Vec<u8>>> {
848            unimplemented!()
849        }
850
851        async fn raw_get_bytes(&mut self, _key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
852            unimplemented!()
853        }
854
855        async fn raw_remove_entry(&mut self, _key: &[u8]) -> DatabaseResult<Option<Vec<u8>>> {
856            unimplemented!()
857        }
858
859        async fn raw_find_by_range(
860            &mut self,
861            _key_range: Range<&[u8]>,
862        ) -> DatabaseResult<crate::db::PrefixStream<'_>> {
863            unimplemented!()
864        }
865
866        async fn raw_find_by_prefix(
867            &mut self,
868            _key_prefix: &[u8],
869        ) -> DatabaseResult<crate::db::PrefixStream<'_>> {
870            unimplemented!()
871        }
872
873        async fn raw_remove_by_prefix(&mut self, _key_prefix: &[u8]) -> DatabaseResult<()> {
874            unimplemented!()
875        }
876
877        async fn raw_find_by_prefix_sorted_descending(
878            &mut self,
879            _key_prefix: &[u8],
880        ) -> DatabaseResult<crate::db::PrefixStream<'_>> {
881            unimplemented!()
882        }
883    }
884
885    impl IDatabaseTransactionOps for FakeTransaction<'_> {}
886
887    #[async_trait]
888    impl IRawDatabaseTransaction for FakeTransaction<'_> {
889        async fn commit_tx(self) -> DatabaseResult<()> {
890            use crate::db::DatabaseError;
891
892            Err(DatabaseError::backend(std::io::Error::other(
893                "Can't commit!",
894            )))
895        }
896    }
897
898    let db = Database::new(FakeDatabase, ModuleDecoderRegistry::default());
899    let err = db
900        .autocommit::<_, _, ()>(|_dbtx, _| Box::pin(async { Ok(()) }), Some(5))
901        .await
902        .unwrap_err();
903
904    match err {
905        AutocommitError::CommitFailed {
906            attempts: failed_attempts,
907            ..
908        } => {
909            assert_eq!(failed_attempts, 5);
910        }
911        AutocommitError::ClosureError { .. } => panic!("Closure did not return error"),
912    }
913}