use super::super::redial::{CAP, FIRST, PREDECESSOR, next, redial};
use super::{
advertised, aside, echo, entry_at, flapping_at, receipt, refusal, restored, row, set, work,
};
use crate::channel::entries::Entry;
use crate::test_support::{Notices, Scratch, Waits, unwaited};
use std::time::Duration;
#[test]
fn the_wait_doubles_from_its_floor_and_stops_at_the_cap() {
let mut series = FIRST;
let mut waited = Vec::new();
for _ in 0..9 {
let (wait, then) = next(series, false, false);
waited.push(wait);
series = then;
}
assert_eq!(
waited,
[1, 2, 4, 8, 16, 32, 64, 64, 64].map(Duration::from_secs)
);
assert_eq!(*waited.last().expect("a wait"), CAP);
}
#[test]
fn a_predecessor_refusal_waits_past_one_hold_s_width() {
assert_eq!(next(FIRST, true, false).0, PREDECESSOR);
assert!(
PREDECESSOR >= Duration::from_secs(30),
"REMOTE §5.1 states the claim's life as one hold's width, thirty seconds"
);
}
#[test]
fn a_predecessor_floor_is_overtaken_by_the_series() {
let mut series = FIRST;
for _ in 0..8 {
series = next(series, true, false).1;
}
assert_eq!(next(series, true, false).0, CAP);
}
#[test]
fn a_channel_that_served_returns_the_wait_to_its_floor() {
assert_eq!(next(CAP, false, true), (FIRST, FIRST * 2));
}
#[test]
fn a_dropped_channel_is_dialled_again() {
let scratch = Scratch::new();
let engine = flapping_at(scratch.path(), vec![None, Some(refusal("stop"))]);
let (waits, pause) = Waits::new();
let (notices, sink) = Notices::new();
assert_eq!(
redial(&entry_at(scratch.path()), &set(), echo, &sink, &pause),
"stop"
);
assert_eq!(waits.heard(), [FIRST]);
let ops: Vec<String> = engine
.heard()
.iter()
.filter_map(|v| v.get("op")?.as_str().map(str::to_owned))
.collect();
assert_eq!(ops, ["advertise", "advertise"], "it presented again");
let said = notices.heard();
assert_eq!(said.len(), 1, "{said:?}");
assert!(
said[0].contains("the channel to the engine failed"),
"{said:?}"
);
assert!(said[0].contains("again in 1s"), "{said:?}");
}
#[test]
fn a_refused_read_is_waited_out_and_named() {
let scratch = Scratch::new();
let refused = "client \"foot-1\" already holds a read on this engine";
let _engine = flapping_at(
scratch.path(),
vec![
Some(advertised()),
Some(refusal(refused)),
Some(refusal("stop")),
],
);
let (waits, pause) = Waits::new();
let (notices, sink) = Notices::new();
assert_eq!(
redial(&entry_at(scratch.path()), &set(), echo, &sink, &pause),
"stop"
);
assert_eq!(waits.heard(), [PREDECESSOR]);
let said = notices.heard();
assert_eq!(said.len(), 1, "{said:?}");
assert!(said[0].starts_with(refused), "{said:?}");
assert!(said[0].contains("own connection still dying"), "{said:?}");
assert!(said[0].contains("again in 32s"), "{said:?}");
}
#[test]
fn an_ending_that_is_over_stops_the_channel_with_no_wait() {
let scratch = Scratch::new();
let _engine = flapping_at(
scratch.path(),
vec![Some(refusal("a rival holds this set"))],
);
let (waits, pause) = Waits::new();
let (notices, sink) = Notices::new();
assert_eq!(
redial(&entry_at(scratch.path()), &set(), echo, &sink, &pause),
"a rival holds this set"
);
assert_eq!(waits.heard(), Vec::<Duration>::new());
assert_eq!(notices.heard(), Vec::<String>::new());
}
#[test]
fn an_entry_that_cannot_be_opened_is_over_before_any_dial() {
let (waits, pause) = Waits::new();
let entry = Entry {
leaf: "engine".to_owned(),
channel: Err("this entry is empty".to_owned()),
};
assert_eq!(
redial(&entry, &set(), echo, &aside(), &pause),
"this entry is empty"
);
assert_eq!(waits.heard(), Vec::<Duration>::new());
}
#[test]
fn a_second_drop_backs_off_unless_the_channel_served() {
let backed_off = waits_over(vec![
Some(advertised()),
None,
Some(advertised()),
None,
Some(refusal("stop")),
]);
assert_eq!(backed_off, [FIRST, FIRST * 2]);
let reset = waits_over(vec![
Some(advertised()),
None,
Some(advertised()),
Some(work(vec![])),
None,
Some(refusal("stop")),
]);
assert_eq!(reset, [FIRST, FIRST]);
}
fn waits_over(script: Vec<Option<serde_json::Value>>) -> Vec<Duration> {
let scratch = Scratch::new();
let _engine = flapping_at(scratch.path(), script);
let (waits, pause) = Waits::new();
assert_eq!(
redial(&entry_at(scratch.path()), &set(), echo, &aside(), &pause),
"stop"
);
waits.heard()
}
#[test]
fn a_disarming_is_not_remembered_across_a_redial() {
let scratch = Scratch::new();
let _engine = flapping_at(
scratch.path(),
vec![
Some(restored()),
None,
Some(restored()),
Some(work(vec![row("i-1", "Bash")])),
Some(receipt("i-1")),
Some(refusal("stop")),
],
);
let (notices, sink) = Notices::new();
assert_eq!(
redial(&entry_at(scratch.path()), &set(), echo, &sink, &unwaited()),
"stop"
);
let said = notices.heard();
assert_eq!(said.len(), 1, "only the redial spoke: {said:?}");
assert!(said[0].contains("Dialling this engine again"), "{said:?}");
assert!(
!said[0].contains("was not the set in force"),
"a fresh channel's first presentation says nothing: {said:?}"
);
}