Skip to content

chore(apollo_l1_endpoint_monitor): add integration test #6341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: gilad/05-06-chore_apollo_l1_endpoint_monitor_extract_rpc_call_to_const_and_add_tests
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/apollo_l1_endpoint_monitor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ tracing.workspace = true
url = { workspace = true, features = ["serde"] }

[dev-dependencies]
alloy = { workspace = true, features = ["node-bindings"] }
mockito.workspace = true
papyrus_base_layer = { workspace = true, features = ["testing"] }
tokio.workspace = true

[lints]
Expand Down
69 changes: 69 additions & 0 deletions crates/apollo_l1_endpoint_monitor/tests/happy_flow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use apollo_l1_endpoint_monitor::monitor::{
L1EndpointMonitor,
L1EndpointMonitorConfig,
L1EndpointMonitorError,
};
use papyrus_base_layer::test_utils::anvil;
use url::Url;

/// Integration test: two Anvil nodes plus a bogus endpoint to exercise cycling and failure.
#[tokio::test]
async fn end_to_end_cycle_and_recovery() {
// Spin up two ephemeral Anvil nodes.
// IMPORTANT: This is one of the only cases where two anvil nodes are needed simultaneously,
// since we are flow testing two separate L1 nodes. Other tests should never use more than one
// at a time!
let good_node_1 = anvil(None);
let good_url_1 = good_node_1.endpoint_url();
let good_node_2 = anvil(None);
let good_url_2 = good_node_2.endpoint_url();

// Bogus endpoint on port 1 that is likely to be unbound, see the unit tests for more details.
let bad_node_url = Url::parse("http://localhost:1").unwrap();

// Initialize monitor starting at the bad index.
let mut monitor = L1EndpointMonitor {
current_l1_endpoint_index: 0,
config: L1EndpointMonitorConfig {
ordered_l1_endpoint_urls: vec![
bad_node_url.clone(),
good_url_1.clone(),
good_url_2.clone(),
],
},
};

// 1) First call: skip bad and take the first good one.
let active1 = monitor.get_active_l1_endpoint().await.unwrap();
assert_eq!(active1, good_url_1);
assert_eq!(monitor.current_l1_endpoint_index, 1);

// 2) Anvil 1 is going down.
drop(good_node_1);

// Next call: now the first good node is down, switch to second good node.
let active2 = monitor.get_active_l1_endpoint().await.unwrap();
assert_eq!(active2, good_url_2);
assert_eq!(monitor.current_l1_endpoint_index, 2);

// 3) Anvil 2 is now also down!
drop(good_node_2);

// All endpoints are now down --> error. Do this twice for idempotency.
for _ in 0..2 {
let result = monitor.get_active_l1_endpoint().await;
assert_eq!(result, Err(L1EndpointMonitorError::NoActiveL1Endpoint));
assert_eq!(monitor.current_l1_endpoint_index, 2);
}

// ANVIL node 1 has risen!
let good_node_1 = anvil(None);
// Anvil is configured to use an ephemeral port, so this new node will be bound to a fresh port.
// We cannot reuse the previous URL since the old port may no longer be available.
let good_url_1 = good_node_1.endpoint_url();
monitor.config.ordered_l1_endpoint_urls[1] = good_url_1.clone();
// Index wraps around from 2 to 0, 0 is still down so 1 is picked, which is operational now.
let active3 = monitor.get_active_l1_endpoint().await.unwrap();
assert_eq!(active3, good_url_1);
assert_eq!(monitor.current_l1_endpoint_index, 1);
}
4 changes: 2 additions & 2 deletions crates/papyrus_base_layer/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ pub fn get_test_ethereum_node() -> (TestEthereumNodeHandle, EthereumContractAddr

// TODO(Arni): Make port non-optional.
// Spin up Anvil instance, a local Ethereum node, dies when dropped.
fn anvil(port: Option<u16>) -> AnvilInstance {
pub fn anvil(port: Option<u16>) -> AnvilInstance {
let mut anvil = Anvil::new();
// If the port is not set explicitly, a random value will be used.
// If the port is not set explicitly, a random ephemeral port is bound and used.
if let Some(port) = port {
anvil = anvil.port(port);
}
Expand Down
Loading