|
1 | | -use shared::tokio; |
| 1 | +#![cfg(feature = "nats_integration_tests")] |
| 2 | +#![cfg(feature = "node_integration_tests")] |
| 3 | + |
| 4 | +use ipc_extractor::{self, Args}; |
| 5 | +use shared::{ |
| 6 | + async_nats, |
| 7 | + bitcoin::hex::DisplayHex, |
| 8 | + corepc_node, |
| 9 | + futures::StreamExt, |
| 10 | + log::{self, info}, |
| 11 | + nats_util::NatsArgs, |
| 12 | + prost::Message, |
| 13 | + protobuf::{ |
| 14 | + event::{Event, event::PeerObserverEvent}, |
| 15 | + ipc_extractor::ipc::IpcEvent::BlockTip, |
| 16 | + }, |
| 17 | + simple_logger::SimpleLogger, |
| 18 | + testing::nats_server::NatsServerForTesting, |
| 19 | + tokio::{self, sync::watch, task::LocalSet, time::sleep}, |
| 20 | +}; |
| 21 | +use std::sync::Once; |
| 22 | +use std::time::Duration; |
| 23 | + |
| 24 | +pub const QUERY_INTERVAL_SECONDS: u64 = 1; |
| 25 | + |
| 26 | +// 5 second check() timeout. |
| 27 | +const TEST_TIMEOUT_SECONDS: u64 = 5; |
| 28 | + |
| 29 | +pub fn make_test_args(nats_port: u16, ipc_socket_path: String) -> Args { |
| 30 | + Args::new( |
| 31 | + NatsArgs { |
| 32 | + address: format!("127.0.0.1:{}", nats_port), |
| 33 | + username: None, |
| 34 | + password: None, |
| 35 | + password_file: None, |
| 36 | + }, |
| 37 | + log::Level::Trace, |
| 38 | + ipc_socket_path, |
| 39 | + QUERY_INTERVAL_SECONDS, |
| 40 | + ) |
| 41 | +} |
| 42 | + |
| 43 | +static INIT: Once = Once::new(); |
| 44 | + |
| 45 | +pub fn setup() { |
| 46 | + INIT.call_once(|| { |
| 47 | + SimpleLogger::new() |
| 48 | + .with_level(log::LevelFilter::Trace) |
| 49 | + .init() |
| 50 | + .unwrap(); |
| 51 | + }); |
| 52 | +} |
| 53 | + |
| 54 | +pub fn setup_node(conf: corepc_node::Conf) -> corepc_node::Node { |
| 55 | + info!( |
| 56 | + "env BITCOIN_NODE_EXE={:?}", |
| 57 | + std::env::var("BITCOIN_NODE_EXE") |
| 58 | + ); |
| 59 | + let exe_path = std::env::var("BITCOIN_NODE_EXE").unwrap(); |
| 60 | + |
| 61 | + info!("Using bitcoin-node at '{}'", exe_path); |
| 62 | + corepc_node::Node::with_conf(exe_path, &conf).unwrap() |
| 63 | +} |
| 64 | + |
| 65 | +fn configure_node() -> corepc_node::Node { |
| 66 | + let mut node_conf = corepc_node::Conf::default(); |
| 67 | + node_conf.args = vec!["-regtest", "-ipcbind=unix"]; |
| 68 | + // enabling this is useful for debugging, but enabling this by default will |
| 69 | + // be quite spammy. |
| 70 | + node_conf.view_stdout = false; |
| 71 | + // node_conf.wallet is `true` by default, but since `bitcoin-node` binary doesn't |
| 72 | + // have wallet capabilities we disable it |
| 73 | + node_conf.wallet = None; |
| 74 | + |
| 75 | + setup_node(node_conf) |
| 76 | +} |
| 77 | + |
| 78 | +async fn check(check_expected: fn(PeerObserverEvent) -> ()) { |
| 79 | + setup(); |
| 80 | + let node = configure_node(); |
| 81 | + let nats_server = NatsServerForTesting::new(&[]).await; |
| 82 | + |
| 83 | + let ipc_socket_path = node |
| 84 | + .workdir() |
| 85 | + .as_path() |
| 86 | + .join("regtest") |
| 87 | + .join("node.sock") |
| 88 | + .to_str() |
| 89 | + .unwrap() |
| 90 | + .into(); |
| 91 | + |
| 92 | + let args = make_test_args(nats_server.port, ipc_socket_path); |
| 93 | + |
| 94 | + let local = LocalSet::new(); |
| 95 | + local |
| 96 | + .run_until(async move { |
| 97 | + let (shutdown_tx, shutdown_rx) = watch::channel(false); |
| 98 | + |
| 99 | + let ipc_extractor_future = ipc_extractor::run(args, shutdown_rx.clone()); |
| 100 | + tokio::pin!(ipc_extractor_future); |
| 101 | + |
| 102 | + let nc = async_nats::connect(format!("127.0.0.1:{}", nats_server.port)) |
| 103 | + .await |
| 104 | + .unwrap(); |
| 105 | + let mut sub = nc.subscribe("*").await.unwrap(); |
| 106 | + |
| 107 | + tokio::select! { |
| 108 | + _ = sleep(Duration::from_secs(TEST_TIMEOUT_SECONDS)) => { |
| 109 | + panic!("timed out waiting for check() to complete"); |
| 110 | + } |
| 111 | + msg = sub.next() => { |
| 112 | + if let Some(msg) = msg { |
| 113 | + let unwrapped = Event::decode(msg.payload).unwrap(); |
| 114 | + if let Some(event) = unwrapped.peer_observer_event { |
| 115 | + check_expected(event); |
| 116 | + } |
| 117 | + } else { |
| 118 | + panic!("subscription ended"); |
| 119 | + } |
| 120 | + } |
| 121 | + result = &mut ipc_extractor_future => { |
| 122 | + panic!("ipc_extractor stopped unexpectedly: {:?}", result); |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + shutdown_tx.send(true).unwrap(); |
| 127 | + ipc_extractor_future.await.unwrap(); |
| 128 | + }) |
| 129 | + .await; |
| 130 | +} |
2 | 131 |
|
3 | 132 | #[tokio::test] |
4 | | -async fn test_integration_ipc_foo() { |
5 | | - println!("test that we receive foo IPC events"); |
| 133 | +#[ignore] // ignored until we have a proper CI setup for running a `bitcoin-node` process |
| 134 | +async fn test_integration_ipc() { |
| 135 | + println!("test that we receive BlockTip IPC events"); |
6 | 136 |
|
7 | | - assert_eq!(1, 1) |
| 137 | + check(|event| match event { |
| 138 | + PeerObserverEvent::IpcExtractor(i) => { |
| 139 | + if let Some(ref e) = i.ipc_event { |
| 140 | + match e { |
| 141 | + BlockTip(t) => { |
| 142 | + assert_eq!(t.height, 0); |
| 143 | + assert_eq!(t.hash.len(), 32); |
| 144 | + assert_eq!( |
| 145 | + t.hash.to_lower_hex_string(), |
| 146 | + // genesis blockhash in Regtest |
| 147 | + "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" |
| 148 | + ); |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + _ => panic!("unexpected event {:?}", event), |
| 154 | + }) |
| 155 | + .await; |
8 | 156 | } |
0 commit comments