Skip to content

Commit 76d2f03

Browse files
committed
Refactor test code and introduce TestSyncStore
.. which asserts that all `KVStore` implementations operatate synchronously, i.e., yield identical results given the same inputs.
1 parent 89e97ab commit 76d2f03

File tree

2 files changed

+153
-134
lines changed

2 files changed

+153
-134
lines changed

src/test/functional_tests.rs

Lines changed: 12 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::builder::NodeBuilder;
22
use crate::io::KVStore;
33
use crate::test::utils::*;
4-
use crate::test::utils::{expect_event, random_config};
4+
use crate::test::utils::{expect_event, random_config, setup_two_nodes};
55
use crate::{Error, Event, Node, PaymentDirection, PaymentStatus};
66

77
use bitcoin::Amount;
@@ -11,45 +11,14 @@ use electrsd::ElectrsD;
1111
#[test]
1212
fn channel_full_cycle() {
1313
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
14-
println!("== Node A ==");
15-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
16-
let config_a = random_config();
17-
let mut builder_a = NodeBuilder::from_config(config_a);
18-
builder_a.set_esplora_server(esplora_url.clone());
19-
let node_a = builder_a.build().unwrap();
20-
node_a.start().unwrap();
21-
22-
println!("\n== Node B ==");
23-
let config_b = random_config();
24-
let mut builder_b = NodeBuilder::from_config(config_b);
25-
builder_b.set_esplora_server(esplora_url);
26-
let node_b = builder_b.build().unwrap();
27-
node_b.start().unwrap();
28-
14+
let (node_a, node_b) = setup_two_nodes(&electrsd, false);
2915
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, false);
3016
}
3117

3218
#[test]
3319
fn channel_full_cycle_0conf() {
3420
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
35-
println!("== Node A ==");
36-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
37-
let config_a = random_config();
38-
let mut builder_a = NodeBuilder::from_config(config_a);
39-
builder_a.set_esplora_server(esplora_url.clone());
40-
let node_a = builder_a.build().unwrap();
41-
node_a.start().unwrap();
42-
43-
println!("\n== Node B ==");
44-
let mut config_b = random_config();
45-
config_b.trusted_peers_0conf.push(node_a.node_id());
46-
47-
let mut builder_b = NodeBuilder::from_config(config_b);
48-
builder_b.set_esplora_server(esplora_url.clone());
49-
let node_b = builder_b.build().unwrap();
50-
51-
node_b.start().unwrap();
52-
21+
let (node_a, node_b) = setup_two_nodes(&electrsd, true);
5322
do_channel_full_cycle(node_a, node_b, &bitcoind, &electrsd, true)
5423
}
5524

@@ -271,21 +240,9 @@ fn do_channel_full_cycle<K: KVStore + Sync + Send>(
271240
#[test]
272241
fn channel_open_fails_when_funds_insufficient() {
273242
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
274-
println!("== Node A ==");
275-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
276-
let config_a = random_config();
277-
let mut builder_a = NodeBuilder::from_config(config_a);
278-
builder_a.set_esplora_server(esplora_url.clone());
279-
let node_a = builder_a.build().unwrap();
280-
node_a.start().unwrap();
281-
let addr_a = node_a.new_onchain_address().unwrap();
243+
let (node_a, node_b) = setup_two_nodes(&electrsd, false);
282244

283-
println!("\n== Node B ==");
284-
let config_b = random_config();
285-
let mut builder_b = NodeBuilder::from_config(config_b);
286-
builder_b.set_esplora_server(esplora_url);
287-
let node_b = builder_b.build().unwrap();
288-
node_b.start().unwrap();
245+
let addr_a = node_a.new_onchain_address().unwrap();
289246
let addr_b = node_b.new_onchain_address().unwrap();
290247

291248
let premine_amount_sat = 100_000;
@@ -329,26 +286,22 @@ fn connect_to_public_testnet_esplora() {
329286
#[test]
330287
fn start_stop_reinit() {
331288
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
332-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
333289
let config = random_config();
334-
let mut builder = NodeBuilder::from_config(config.clone());
335-
builder.set_esplora_server(esplora_url.clone());
336-
let node = builder.build().unwrap();
290+
let node = setup_node(&electrsd, config.clone());
291+
337292
let expected_node_id = node.node_id();
293+
assert_eq!(node.start(), Err(Error::AlreadyRunning));
338294

339295
let funding_address = node.new_onchain_address().unwrap();
340296
let expected_amount = Amount::from_sat(100000);
341297

342298
premine_and_distribute_funds(&bitcoind, &electrsd, vec![funding_address], expected_amount);
343299
assert_eq!(node.total_onchain_balance_sats().unwrap(), 0);
344300

345-
node.start().unwrap();
346-
assert_eq!(node.start(), Err(Error::AlreadyRunning));
347-
348301
node.sync_wallets().unwrap();
349302
assert_eq!(node.spendable_onchain_balance_sats().unwrap(), expected_amount.to_sat());
350303

351-
let log_file_symlink = format!("{}/logs/ldk_node_latest.log", config.storage_dir_path);
304+
let log_file_symlink = format!("{}/logs/ldk_node_latest.log", config.clone().storage_dir_path);
352305
assert!(std::path::Path::new(&log_file_symlink).is_symlink());
353306

354307
node.stop().unwrap();
@@ -361,66 +314,9 @@ fn start_stop_reinit() {
361314
assert_eq!(node.stop(), Err(Error::NotRunning));
362315
drop(node);
363316

364-
let mut new_builder = NodeBuilder::from_config(config);
365-
new_builder.set_esplora_server(esplora_url);
366-
let reinitialized_node = builder.build().unwrap();
367-
assert_eq!(reinitialized_node.node_id(), expected_node_id);
368-
369-
reinitialized_node.start().unwrap();
370-
371-
assert_eq!(
372-
reinitialized_node.spendable_onchain_balance_sats().unwrap(),
373-
expected_amount.to_sat()
374-
);
375-
376-
reinitialized_node.sync_wallets().unwrap();
377-
assert_eq!(
378-
reinitialized_node.spendable_onchain_balance_sats().unwrap(),
379-
expected_amount.to_sat()
380-
);
381-
382-
reinitialized_node.stop().unwrap();
383-
}
384-
385-
#[test]
386-
fn start_stop_reinit_fs_store() {
387-
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
388-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
389-
let config = random_config();
390-
let mut builder = NodeBuilder::from_config(config.clone());
391-
builder.set_esplora_server(esplora_url.clone());
392-
let node = builder.build_with_fs_store().unwrap();
393-
let expected_node_id = node.node_id();
394-
395-
let funding_address = node.new_onchain_address().unwrap();
396-
let expected_amount = Amount::from_sat(100000);
397-
398-
premine_and_distribute_funds(&bitcoind, &electrsd, vec![funding_address], expected_amount);
399-
assert_eq!(node.total_onchain_balance_sats().unwrap(), 0);
400-
401-
node.start().unwrap();
402-
assert_eq!(node.start(), Err(Error::AlreadyRunning));
403-
404-
node.sync_wallets().unwrap();
405-
assert_eq!(node.spendable_onchain_balance_sats().unwrap(), expected_amount.to_sat());
406-
407-
node.stop().unwrap();
408-
assert_eq!(node.stop(), Err(Error::NotRunning));
409-
410-
node.start().unwrap();
411-
assert_eq!(node.start(), Err(Error::AlreadyRunning));
412-
413-
node.stop().unwrap();
414-
assert_eq!(node.stop(), Err(Error::NotRunning));
415-
drop(node);
416-
417-
let mut new_builder = NodeBuilder::from_config(config);
418-
new_builder.set_esplora_server(esplora_url);
419-
let reinitialized_node = builder.build_with_fs_store().unwrap();
317+
let reinitialized_node = setup_node(&electrsd, config);
420318
assert_eq!(reinitialized_node.node_id(), expected_node_id);
421319

422-
reinitialized_node.start().unwrap();
423-
424320
assert_eq!(
425321
reinitialized_node.spendable_onchain_balance_sats().unwrap(),
426322
expected_amount.to_sat()
@@ -438,20 +334,9 @@ fn start_stop_reinit_fs_store() {
438334
#[test]
439335
fn onchain_spend_receive() {
440336
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
441-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
337+
let (node_a, node_b) = setup_two_nodes(&electrsd, false);
442338

443-
let config_a = random_config();
444-
let mut builder_a = NodeBuilder::from_config(config_a);
445-
builder_a.set_esplora_server(esplora_url.clone());
446-
let node_a = builder_a.build().unwrap();
447-
node_a.start().unwrap();
448339
let addr_a = node_a.new_onchain_address().unwrap();
449-
450-
let config_b = random_config();
451-
let mut builder_b = NodeBuilder::from_config(config_b);
452-
builder_b.set_esplora_server(esplora_url);
453-
let node_b = builder_b.build().unwrap();
454-
node_b.start().unwrap();
455340
let addr_b = node_b.new_onchain_address().unwrap();
456341

457342
premine_and_distribute_funds(
@@ -494,13 +379,8 @@ fn onchain_spend_receive() {
494379
#[test]
495380
fn sign_verify_msg() {
496381
let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd();
497-
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
498382
let config = random_config();
499-
let mut builder = NodeBuilder::from_config(config.clone());
500-
builder.set_esplora_server(esplora_url.clone());
501-
let node = builder.build().unwrap();
502-
503-
node.start().unwrap();
383+
let node = setup_node(&electrsd, config);
504384

505385
// Tests arbitrary message signing and later verification
506386
let msg = "OK computer".as_bytes();

src/test/utils.rs

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use crate::io::KVStore;
2-
use crate::Config;
1+
use crate::builder::NodeBuilder;
2+
use crate::io::{FilesystemStore, KVStore, SqliteStore};
3+
use crate::{Config, Node};
34
use lightning::util::logger::{Level, Logger, Record};
45
use lightning::util::persist::KVStorePersister;
56
use lightning::util::ser::Writeable;
@@ -133,6 +134,118 @@ impl KVStorePersister for TestStore {
133134
}
134135
}
135136

137+
// A `KVStore` impl for testing purposes that wraps all our `KVStore`s and asserts their synchronicity.
138+
pub(crate) struct TestSyncStore {
139+
fs_store: FilesystemStore,
140+
sqlite_store: SqliteStore,
141+
}
142+
143+
impl TestSyncStore {
144+
pub(crate) fn new(dest_dir: PathBuf) -> Self {
145+
let fs_store = FilesystemStore::new(dest_dir.clone());
146+
let sqlite_store = SqliteStore::new(dest_dir);
147+
Self { fs_store, sqlite_store }
148+
}
149+
}
150+
151+
impl KVStore for TestSyncStore {
152+
type Reader = io::Cursor<Vec<u8>>;
153+
154+
fn read(&self, namespace: &str, key: &str) -> std::io::Result<Self::Reader> {
155+
// For now, we only assert `Ok` with the `fs_reader` here, as it's too complicated to track
156+
// the read status of both seperately, however, the `Reader` concept is going away anyways
157+
// at which point we can assert on simply on the returned values of `KVStore::read`.
158+
let fs_res = self.fs_store.read(namespace, key);
159+
let sqlite_res = self.sqlite_store.read(namespace, key);
160+
161+
match sqlite_res {
162+
Ok(read) => {
163+
assert!(fs_res.is_ok());
164+
Ok(read)
165+
}
166+
Err(e) => {
167+
assert!(fs_res.is_err());
168+
assert_eq!(e.kind(), unsafe { fs_res.unwrap_err_unchecked().kind() });
169+
Err(e)
170+
}
171+
}
172+
}
173+
174+
fn write(&self, namespace: &str, key: &str, buf: &[u8]) -> std::io::Result<()> {
175+
let fs_res = self.fs_store.write(namespace, key, buf);
176+
let sqlite_res = self.sqlite_store.write(namespace, key, buf);
177+
178+
assert!(self.list(namespace).unwrap().contains(&key.to_string()));
179+
180+
match fs_res {
181+
Ok(()) => {
182+
assert!(sqlite_res.is_ok());
183+
Ok(())
184+
}
185+
Err(e) => {
186+
assert!(sqlite_res.is_err());
187+
Err(e)
188+
}
189+
}
190+
}
191+
192+
fn remove(&self, namespace: &str, key: &str) -> std::io::Result<()> {
193+
let fs_res = self.fs_store.remove(namespace, key);
194+
let sqlite_res = self.sqlite_store.remove(namespace, key);
195+
196+
match fs_res {
197+
Ok(()) => {
198+
assert!(sqlite_res.is_ok());
199+
Ok(())
200+
}
201+
Err(e) => {
202+
assert!(sqlite_res.is_err());
203+
Err(e)
204+
}
205+
}
206+
}
207+
208+
fn list(&self, namespace: &str) -> std::io::Result<Vec<String>> {
209+
let fs_res = self.fs_store.list(namespace);
210+
let sqlite_res = self.sqlite_store.list(namespace);
211+
212+
match fs_res {
213+
Ok(list) => {
214+
assert_eq!(list, sqlite_res.unwrap());
215+
Ok(list)
216+
}
217+
Err(e) => {
218+
assert!(sqlite_res.is_err());
219+
Err(e)
220+
}
221+
}
222+
}
223+
}
224+
225+
impl KVStorePersister for TestSyncStore {
226+
fn persist<W: Writeable>(&self, prefixed_key: &str, object: &W) -> std::io::Result<()> {
227+
let msg = format!("Could not persist file for key {}.", prefixed_key);
228+
let dest_file = PathBuf::from_str(prefixed_key).map_err(|_| {
229+
lightning::io::Error::new(lightning::io::ErrorKind::InvalidInput, msg.clone())
230+
})?;
231+
232+
let parent_directory = dest_file.parent().ok_or(lightning::io::Error::new(
233+
lightning::io::ErrorKind::InvalidInput,
234+
msg.clone(),
235+
))?;
236+
let namespace = parent_directory.display().to_string();
237+
238+
let dest_without_namespace = dest_file
239+
.strip_prefix(&namespace)
240+
.map_err(|_| lightning::io::Error::new(lightning::io::ErrorKind::InvalidInput, msg))?;
241+
let key = dest_without_namespace.display().to_string();
242+
243+
let data = object.encode();
244+
self.write(&namespace, &key, &data)?;
245+
Ok(())
246+
}
247+
}
248+
136249
// Copied over from upstream LDK
137250
#[allow(dead_code)]
138251
pub struct TestLogger {
@@ -268,6 +381,32 @@ pub fn setup_bitcoind_and_electrsd() -> (BitcoinD, ElectrsD) {
268381
(bitcoind, electrsd)
269382
}
270383

384+
pub(crate) fn setup_two_nodes(
385+
electrsd: &ElectrsD, allow_0conf: bool,
386+
) -> (Node<TestSyncStore>, Node<TestSyncStore>) {
387+
println!("== Node A ==");
388+
let config_a = random_config();
389+
let node_a = setup_node(electrsd, config_a);
390+
391+
println!("\n== Node B ==");
392+
let mut config_b = random_config();
393+
if allow_0conf {
394+
config_b.trusted_peers_0conf.push(node_a.node_id());
395+
}
396+
let node_b = setup_node(electrsd, config_b);
397+
(node_a, node_b)
398+
}
399+
400+
pub(crate) fn setup_node(electrsd: &ElectrsD, config: Config) -> Node<TestSyncStore> {
401+
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
402+
let mut builder = NodeBuilder::from_config(config.clone());
403+
builder.set_esplora_server(esplora_url.clone());
404+
let test_sync_store = Arc::new(TestSyncStore::new(config.storage_dir_path.into()));
405+
let node = builder.build_with_store(test_sync_store).unwrap();
406+
node.start().unwrap();
407+
node
408+
}
409+
271410
pub fn generate_blocks_and_wait(bitcoind: &BitcoinD, electrsd: &ElectrsD, num: usize) {
272411
let cur_height = bitcoind.client.get_block_count().expect("failed to get current block height");
273412
let address = bitcoind

0 commit comments

Comments
 (0)