Skip to content

Commit de27ded

Browse files
committed
fix: modify persistence helper
Modified the persistence helper to return a `String` as error in order to be more expressive while keeping things simple. Also modified the helper to load and check the `ChangeSet` after each `ChangeSet` in `changesets` is persisted. It also checks if the `ChangeSet` loaded after reopening the `Store` matches the final aggregate `ChangeSet`.
1 parent f2a3f87 commit de27ded

4 files changed

Lines changed: 102 additions & 115 deletions

File tree

crates/chain/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ proptest = "1.2.0"
3030
bdk_testenv = { path = "../testenv" }
3131
criterion = { version = "0.7" }
3232
tempfile = "3"
33-
anyhow = "1.0.102"
33+
anyhow = "1"
3434

3535
[features]
3636
default = ["std", "miniscript"]
Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![cfg(feature = "rusqlite")]
2+
use anyhow::anyhow;
23
use bdk_chain::{keychain_txout, local_chain, tx_graph, ConfirmationBlockTime};
34
use bdk_testenv::persist_test_utils::{
45
assert_persist_changesets, keychain_txout_changesets, local_chain_changesets,
@@ -9,17 +10,17 @@ use bdk_testenv::persist_test_utils::{
910
fn txgraph_is_persisted() -> anyhow::Result<()> {
1011
let temp_dir = tempfile::tempdir().unwrap();
1112
let changesets = tx_graph_changesets();
12-
Ok(assert_persist_changesets(
13+
assert_persist_changesets(
1314
|| {
14-
Ok(rusqlite::Connection::open(
15-
temp_dir.path().join("wallet.sqlite"),
16-
)?)
15+
let mut db = rusqlite::Connection::open(temp_dir.path().join("wallet.sqlite"))?;
16+
let db_tx = db.transaction()?;
17+
tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(&db_tx)?;
18+
db_tx.commit()?;
19+
Ok(db)
1720
},
1821
|db| {
1922
let db_tx = db.transaction()?;
20-
tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(&db_tx)?;
2123
let changeset = tx_graph::ChangeSet::<ConfirmationBlockTime>::from_sqlite(&db_tx)?;
22-
db_tx.commit()?;
2324
Ok(changeset)
2425
},
2526
|db, changeset| {
@@ -29,24 +30,25 @@ fn txgraph_is_persisted() -> anyhow::Result<()> {
2930
Ok(())
3031
},
3132
&changesets,
32-
)?)
33+
)
34+
.map_err(|err| anyhow!(err))
3335
}
3436

3537
#[test]
3638
fn indexer_is_persisted() -> anyhow::Result<()> {
3739
let temp_dir = tempfile::tempdir().unwrap();
3840
let changesets = keychain_txout_changesets();
39-
Ok(assert_persist_changesets(
41+
assert_persist_changesets(
4042
|| {
41-
Ok(rusqlite::Connection::open(
42-
temp_dir.path().join("wallet.sqlite"),
43-
)?)
43+
let mut db = rusqlite::Connection::open(temp_dir.path().join("wallet.sqlite"))?;
44+
let db_tx = db.transaction()?;
45+
keychain_txout::ChangeSet::init_sqlite_tables(&db_tx)?;
46+
db_tx.commit()?;
47+
Ok(db)
4448
},
4549
|db| {
4650
let db_tx = db.transaction()?;
47-
keychain_txout::ChangeSet::init_sqlite_tables(&db_tx)?;
4851
let changeset = keychain_txout::ChangeSet::from_sqlite(&db_tx)?;
49-
db_tx.commit()?;
5052
Ok(changeset)
5153
},
5254
|db, changeset| {
@@ -56,24 +58,25 @@ fn indexer_is_persisted() -> anyhow::Result<()> {
5658
Ok(())
5759
},
5860
&changesets,
59-
)?)
61+
)
62+
.map_err(|err| anyhow!(err))
6063
}
6164

6265
#[test]
6366
fn local_chain_is_persisted() -> anyhow::Result<()> {
6467
let temp_dir = tempfile::tempdir().unwrap();
6568
let changesets = local_chain_changesets();
66-
Ok(assert_persist_changesets(
69+
assert_persist_changesets(
6770
|| {
68-
Ok(rusqlite::Connection::open(
69-
temp_dir.path().join("wallet.sqlite"),
70-
)?)
71+
let mut db = rusqlite::Connection::open(temp_dir.path().join("wallet.sqlite"))?;
72+
let db_tx = db.transaction()?;
73+
local_chain::ChangeSet::init_sqlite_tables(&db_tx)?;
74+
db_tx.commit()?;
75+
Ok(db)
7176
},
7277
|db| {
7378
let db_tx = db.transaction()?;
74-
local_chain::ChangeSet::init_sqlite_tables(&db_tx)?;
7579
let changeset = local_chain::ChangeSet::from_sqlite(&db_tx)?;
76-
db_tx.commit()?;
7780
Ok(changeset)
7881
},
7982
|db, changeset| {
@@ -83,5 +86,6 @@ fn local_chain_is_persisted() -> anyhow::Result<()> {
8386
Ok(())
8487
},
8588
&changesets,
86-
)?)
89+
)
90+
.map_err(|err| anyhow!(err))
8791
}

crates/file_store/src/store.rs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ mod test {
291291
io::{Seek, Write},
292292
};
293293

294+
use anyhow::anyhow;
295+
294296
const TEST_MAGIC_BYTES_LEN: usize = 12;
295297
const TEST_MAGIC_BYTES: [u8; TEST_MAGIC_BYTES_LEN] =
296298
[98, 100, 107, 102, 115, 49, 49, 49, 49, 49, 49, 49];
@@ -609,50 +611,38 @@ mod test {
609611
fn txgraph_is_persisted() -> anyhow::Result<()> {
610612
let temp_dir = tempfile::tempdir().unwrap();
611613
let changesets = tx_graph_changesets();
612-
Ok(assert_persist_changesets(
613-
|| {
614-
Ok(Store::create(
615-
&TEST_MAGIC_BYTES,
616-
temp_dir.path().join("store.db"),
617-
)?)
618-
},
614+
assert_persist_changesets(
615+
|| Ok(Store::load_or_create(&TEST_MAGIC_BYTES, temp_dir.path().join("store.db"))?.0),
619616
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
620617
|db, changeset| Ok(db.append(changeset)?),
621618
&changesets,
622-
)?)
619+
)
620+
.map_err(|err| anyhow!(err))
623621
}
624622

625623
#[test]
626624
fn indexer_is_persisted() -> anyhow::Result<()> {
627625
let temp_dir = tempfile::tempdir().unwrap();
628626
let changesets = keychain_txout_changesets();
629-
Ok(assert_persist_changesets(
630-
|| {
631-
Ok(Store::create(
632-
&TEST_MAGIC_BYTES,
633-
temp_dir.path().join("store.db"),
634-
)?)
635-
},
627+
assert_persist_changesets(
628+
|| Ok(Store::load_or_create(&TEST_MAGIC_BYTES, temp_dir.path().join("store.db"))?.0),
636629
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
637630
|db, changeset| Ok(db.append(changeset)?),
638631
&changesets,
639-
)?)
632+
)
633+
.map_err(|err| anyhow!(err))
640634
}
641635

642636
#[test]
643637
fn local_chain_is_persisted() -> anyhow::Result<()> {
644638
let temp_dir = tempfile::tempdir().unwrap();
645639
let changesets = local_chain_changesets();
646-
Ok(assert_persist_changesets(
647-
|| {
648-
Ok(Store::create(
649-
&TEST_MAGIC_BYTES,
650-
temp_dir.path().join("store.db"),
651-
)?)
652-
},
640+
assert_persist_changesets(
641+
|| Ok(Store::load_or_create(&TEST_MAGIC_BYTES, temp_dir.path().join("store.db"))?.0),
653642
|db| Ok(db.dump().map(Option::unwrap_or_default)?),
654643
|db, changeset| Ok(db.append(changeset)?),
655644
&changesets,
656-
)?)
645+
)
646+
.map_err(|err| anyhow!(err))
657647
}
658648
}

crates/testenv/src/persist_test_utils.rs

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ use bdk_chain::{
88

99
#[cfg(feature = "miniscript")]
1010
use bdk_chain::{indexer::keychain_txout, DescriptorExt, SpkIterator};
11-
use core::{
12-
cmp::PartialEq,
13-
fmt::{Debug, Display},
14-
};
11+
use core::{cmp::PartialEq, fmt::Debug};
1512

1613
use core::error::Error as Err;
1714

@@ -25,83 +22,79 @@ const ADDRS: [&str; 2] = [
2522
"bcrt1q8an5jfmpq8w2hr648nn34ecf9zdtxk0qyqtrfl",
2623
];
2724

28-
/// Errors caused by a failed persister test.
29-
#[derive(Debug)]
30-
pub enum PersistErr<C: Debug> {
31-
/// ChangeSet Mismatch
32-
ChangeSetMismatch {
33-
/// the resulting changeset
34-
got: Box<C>,
35-
/// the expected changeset
36-
expected: Box<C>,
37-
},
38-
/// Errors thrown by underlying persistence backend.
39-
Persister(Box<dyn Err + 'static + Send + Sync>),
40-
}
41-
42-
impl<C: Debug> From<Box<dyn Err + 'static + Send + Sync>> for PersistErr<C> {
43-
fn from(value: Box<dyn Err + 'static + Send + Sync>) -> Self {
44-
PersistErr::Persister(value)
45-
}
46-
}
47-
48-
impl<C: Debug> Display for PersistErr<C> {
49-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50-
match self {
51-
PersistErr::ChangeSetMismatch { got, expected } => write!(
52-
f,
53-
"ChangeSet mismatch! Got: {:?}, Expected: {:?}",
54-
got, expected
55-
),
56-
PersistErr::Persister(err) => write!(f, "{err}"),
57-
}
58-
}
59-
}
60-
61-
impl<C: Debug> Err for PersistErr<C> {}
62-
63-
/// Tests if `ChangeSet` is being persisted correctly.
25+
/// A helper to check if a custom persistence backend persists `ChangeSet`s correctly.
26+
///
27+
/// This first tries to create a `Store` using `init`, `load`s from it and checks if
28+
/// the result is an empty `ChangeSet`.
29+
/// It then tries to `persist` each `ChangeSet` in `changesets` one by one, doing a `load`
30+
/// each time and checks that the aggregated `ChangeSet` matches the one loaded.
6431
///
65-
/// We create a dummy `ChangeSet`, persist it and check if loaded `ChangeSet` matches
66-
/// the persisted one. We then create another such dummy `ChangeSet`, persist it and load it to
67-
/// check if merged `ChangeSet` is returned.
68-
pub fn assert_persist_changesets<CS, Store, CreateStore, Initialize, Persist>(
69-
create_store: CreateStore,
70-
initialize: Initialize,
32+
/// Finally it closes the `Store`, reopens it using `init`, `load`s from it and checks if the loaded
33+
/// `ChangeSet` matches the final aggregated `ChangeSet`.
34+
pub fn assert_persist_changesets<C, Store, Init, Load, Persist>(
35+
init: Init,
36+
load: Load,
7137
persist: Persist,
72-
changesets: &[CS],
73-
) -> Result<(), PersistErr<CS>>
38+
changesets: &[C],
39+
) -> Result<(), String>
7440
where
75-
CS: Debug + PartialEq + Default + Merge + Clone,
76-
CreateStore: Fn() -> Result<Store, Box<dyn Err + 'static + Send + Sync>>,
77-
Initialize: Fn(&mut Store) -> Result<CS, Box<dyn Err + 'static + Send + Sync>>,
78-
Persist: Fn(&mut Store, &CS) -> Result<(), Box<dyn Err + 'static + Send + Sync>>,
41+
C: Debug + PartialEq + Default + Merge + Clone,
42+
Init: Fn() -> Result<Store, Box<dyn Err>>,
43+
Load: Fn(&mut Store) -> Result<C, Box<dyn Err>>,
44+
Persist: Fn(&mut Store, &C) -> Result<(), Box<dyn Err>>,
7945
{
80-
let mut store = create_store()?;
46+
let mut merged_changeset = C::default();
47+
{
48+
let mut store = init().map_err(|err| {
49+
format!(
50+
"Encountered an error from the persister while initializing the store.\nGot:\n{}",
51+
err
52+
)
53+
})?;
54+
55+
let init_changeset = load(&mut store).map_err(|err| format!("Encountered an error from the persister while loading from the new store.\nGot:\n{}",err))?;
56+
57+
if init_changeset != C::default() {
58+
Err("Loading from a new store should return an empty changeset.")?;
59+
}
8160

82-
let init_changeset = initialize(&mut store)?;
61+
for (i, changeset) in changesets.iter().enumerate() {
62+
persist(&mut store, changeset).map_err(|err| format!("Persisting changeset no. {} failed. Got an error from the persister instead:\n{} ", i+1, err) )?;
8363

84-
if init_changeset != CS::default() {
85-
return Err(PersistErr::ChangeSetMismatch {
86-
expected: Box::new(CS::default()),
87-
got: Box::new(init_changeset),
88-
});
89-
}
64+
merged_changeset.merge(changeset.clone());
9065

91-
let mut merged_changeset = CS::default();
66+
let persisted_changeset = load(&mut store).map_err(|err| format!("Encountered an error from the persister while loading (after persisting changeset no. {}).\nGot:\n {}", i+1, err))?;
9267

93-
for changeset in changesets {
94-
persist(&mut store, changeset)?;
95-
merged_changeset.merge(changeset.clone());
68+
if persisted_changeset != merged_changeset {
69+
Err(format!(
70+
"Persisting changeset no. {} failed.\nExpected:\n\n{:?}\n\n\nLoaded:\n\n{:?};",
71+
i + 1,
72+
merged_changeset,
73+
persisted_changeset
74+
))?;
75+
}
76+
}
9677
}
9778

98-
let persisted_changeset = initialize(&mut store)?;
79+
let mut store = init().map_err(|err| {
80+
format!(
81+
"Encountered an error while reopening the store.\nGot:\n{}",
82+
err
83+
)
84+
})?;
85+
86+
let persisted_changeset = load(&mut store).map_err(|err| {
87+
format!(
88+
"Unable to load the persisted changeset after reopening the store.\nGot an error from the persister:\n{}",
89+
err
90+
)
91+
})?;
9992

10093
if persisted_changeset != merged_changeset {
101-
return Err(PersistErr::ChangeSetMismatch {
102-
expected: Box::new(merged_changeset),
103-
got: Box::new(persisted_changeset),
104-
});
94+
Err(format!(
95+
"Did not get the expected changeset after reopening the store and loading.\nExpected:\n\n{:?}\n\n\nLoaded:\n\n{:?};",
96+
merged_changeset, persisted_changeset
97+
))?;
10598
}
10699

107100
Ok(())

0 commit comments

Comments
 (0)