diff options
| author | tv <tv@krebsco.de> | 2026-11-01 19:57:54 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-01 19:57:54 +0100 |
| commit | 15a44cb7c8f08d2ddfe9fdf3812f4c9fd5f0aef8 (patch) | |
| tree | ed2449d6e8426676b8820740360b68a915e2e92a /src/simtest | |
| parent | 9ce725c5a2ded3f9b585568003c9e75c5934158a (diff) | |
add restart tests
Diffstat (limited to 'src/simtest')
| -rw-r--r-- | src/simtest/mod.rs | 24 | ||||
| -rw-r--r-- | src/simtest/timing.rs | 24 |
2 files changed, 47 insertions, 1 deletions
diff --git a/src/simtest/mod.rs b/src/simtest/mod.rs index 81bddb5..61c81d1 100644 --- a/src/simtest/mod.rs +++ b/src/simtest/mod.rs @@ -144,6 +144,30 @@ impl Sim { } } +impl Sim { + /// Restarts node `id`: its process is gone, as if it crashed (`SimNet::restart`: + /// what it still sends is lost), and a new one starts on the same ID, as + /// [`start_with`](Self::start_with) starts one. Nothing carries over but what + /// `adjust` gives it (its node key, under the identity layer, by its name). + pub(crate) fn restart_with( + &self, + id: &str, + seeds: &[&str], + start: Start, + adjust: impl FnOnce(&mut NodeConfig), + ) -> AppState { + { + let mut nodes = self.nodes.lock().unwrap(); + for (_, old) in nodes.iter().filter(|(name, _)| name == id) { + old.closing.send_replace(true); + } + nodes.retain(|(name, _)| name != id); + } + self.net.restart(id); + self.start_with(id, seeds, start, adjust) + } +} + /// Logs to the test output if `RUST_LOG` is set. fn init_logging() { if std::env::var_os("RUST_LOG").is_some() { diff --git a/src/simtest/timing.rs b/src/simtest/timing.rs index fd76a17..daa588f 100644 --- a/src/simtest/timing.rs +++ b/src/simtest/timing.rs @@ -1,5 +1,5 @@ //! The timing rules of `peers.md` on whole nodes: joining and converging, dormancy and -//! return, and late messages that must not undo what newer ones said. +//! return, a restart, and late messages that must not undo what newer ones said. use std::time::Duration; @@ -46,6 +46,28 @@ async fn three_nodes_join_and_converge() { } } +/// A node that restarts without a document file (its process gone, a new one on the +/// same ID) joins again and gets the document; what its earlier run sends on is lost. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn restarted_node_joins_again() { + let sim = Sim::new(9); + let a = sim.start("a", &[], Start::Genesis); + let b = sim.start("b", &["a"], Start::Join); + sim.wait_ready(&[&a, &b], SOON).await; + write(&a, "before", json!(1)).await; + sim.wait_converged(&[&a, &b], SOON).await; + + let b = sim.restart_with("b", &["a"], Start::Join, |_| {}); + sim.wait_ready(&[&b], SOON).await; + let b = &b; + sim.wait_until("b has the document again", SOON, move || async move { + read(b, "before").await == Some(json!(1)) + }) + .await; + write(b, "after", json!(2)).await; + sim.wait_converged(&[&a, b], SOON).await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn converges_with_duplicated_and_reordered_messages() { let sim = Sim::new(2); |
