The test files had grown their own copies of the same helpers: prefix() twelve times, established() seven, an UPDATE builder six, a seventeen field PeerConfig literal in fifteen places, a timeout constant in twenty one. Each is in crates/integration-tests/src once now: peer_config() and TestServer::with_peer for a configured peer, Announcement for building UPDATEs, established(), the waits (wait_until, wait_for, wait_for_routes, wait_for_sessions), get_json, and TIMEOUT. A new field on PeerConfig is one line to add, not fifteen. TestPeer::recv has a deadline, so a test that gets nothing fails instead of hanging; is_closed, which read one byte and took a KEEPALIVE for an open connection, is closed_within and closed_unanswered. Net 788 lines fewer; every test checks what it did. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
pub mod announcement;
|
|
pub mod test_cache;
|
|
pub mod test_peer;
|
|
pub mod test_server;
|
|
|
|
use std::time::Duration;
|
|
|
|
use bgp_primitives::ip_prefix::IpPrefix;
|
|
use bgp_server::address::Endpoint;
|
|
use bgp_server::config::PeerConfig;
|
|
use bgp_server::family::FamilySet;
|
|
use eyre::{Result, bail};
|
|
use serde_json::Value;
|
|
|
|
/// How long a test waits for something the server does on its own: a
|
|
/// route reaching the table, a session coming up, a message arriving.
|
|
/// Everything happens on the loopback, so it is generous.
|
|
pub const TIMEOUT: Duration = Duration::from_secs(5);
|
|
|
|
/// The policy a peer has with nothing configured, but for one thing: the
|
|
/// peers of these tests are all at 127.0.0.1, which no route can have as
|
|
/// its next hop, so their routes name another.
|
|
pub fn default_policy() -> bgp_server::policy::ImportPolicy {
|
|
bgp_server::policy::ImportPolicy {
|
|
accept_third_party_next_hop: true,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// A prefix written as text. The tests' prefixes are literals, so a bad
|
|
/// one is a mistake in the test.
|
|
pub fn prefix(s: &str) -> IpPrefix {
|
|
IpPrefix::try_from(s).unwrap()
|
|
}
|
|
|
|
/// A configured peer with nothing said about it but where it is and what
|
|
/// AS it is in: the default hold time, every family, the default policy of
|
|
/// these tests, and no role, limits, groups or ADD-PATH. A test says what
|
|
/// else with struct update syntax: `PeerConfig { role: Some(..),
|
|
/// ..peer_config(..) }`.
|
|
pub fn peer_config(name: &str, endpoint: impl Into<Endpoint>, remote_asn: u32) -> PeerConfig {
|
|
PeerConfig {
|
|
name: name.into(),
|
|
endpoint: endpoint.into(),
|
|
remote_asn,
|
|
hold_time_secs: 90,
|
|
local_address: None,
|
|
families: FamilySet::supported(),
|
|
add_path: None,
|
|
import: default_policy(),
|
|
role: None,
|
|
require_role: false,
|
|
export: Default::default(),
|
|
export_limits: None,
|
|
graceful_restart: Default::default(),
|
|
groups: Vec::new(),
|
|
reflector_client: false,
|
|
passive: false,
|
|
}
|
|
}
|
|
|
|
/// Waits until `done` holds, looking every few milliseconds, or fails
|
|
/// with `what` once [`TIMEOUT`] has passed.
|
|
pub async fn wait_until(what: &str, mut done: impl FnMut() -> bool) -> Result<()> {
|
|
let waited = tokio::time::timeout(TIMEOUT, async {
|
|
while !done() {
|
|
tokio::time::sleep(Duration::from_millis(5)).await;
|
|
}
|
|
})
|
|
.await;
|
|
if waited.is_err() {
|
|
bail!("timed out waiting until {what}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Waits until `get` has something, and returns it; fails with `what`
|
|
/// once [`TIMEOUT`] has passed.
|
|
pub async fn wait_for<T>(what: &str, mut get: impl FnMut() -> Option<T>) -> Result<T> {
|
|
let mut got = None;
|
|
wait_until(what, || {
|
|
got = get();
|
|
got.is_some()
|
|
})
|
|
.await?;
|
|
Ok(got.expect("the wait ended with something"))
|
|
}
|
|
|
|
/// `GET`s `url` and reads the JSON it answers with. An error status is an
|
|
/// error.
|
|
pub async fn get_json(client: &reqwest::Client, url: &str) -> Result<Value> {
|
|
Ok(client
|
|
.get(url)
|
|
.send()
|
|
.await?
|
|
.error_for_status()?
|
|
.json()
|
|
.await?)
|
|
}
|