use super::*;
use crate::messages::{CONNECTIVITY_LOST_CODE, FARM_OK_CODES};
#[test]
fn test_is_benign_connectivity_notice() {
for code in FARM_OK_CODES {
let notice = Notice::synthesized(code, "farm OK".into());
assert!(is_benign_connectivity_notice(¬ice), "code {code} should be benign");
}
let notice = Notice::synthesized(CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE, "restored, data maintained".into());
assert!(is_benign_connectivity_notice(¬ice), "code 1102 should be benign");
for code in [
2100,
2103, 2105, 2157, 2107,
2108, 2119, 2169,
200, CONNECTIVITY_LOST_CODE, CONNECTIVITY_RESTORED_DATA_LOST_CODE, ] {
let notice = Notice::synthesized(code, "not benign".into());
assert!(!is_benign_connectivity_notice(¬ice), "code {code} should not be benign");
}
}
#[test]
fn test_log_unrouted_notice_traverses_all_severities() {
log_unrouted_notice(&Notice::synthesized(FARM_OK_CODES[0], "farm OK".into()));
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_RESTORED_DATA_MAINTAINED_CODE, "1102 info".into()));
log_unrouted_notice(&Notice::synthesized(2103, "farm broken".into()));
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_RESTORED_DATA_LOST_CODE, "1101 warn".into()));
log_unrouted_notice(&Notice::synthesized(CONNECTIVITY_LOST_CODE, "1100 error".into()));
log_unrouted_notice(&Notice::synthesized(200, "no security definition".into()));
}
#[test]
fn test_validate_frame_length_accepts_the_legal_range() {
for length in [MIN_FRAME_LENGTH, MIN_FRAME_LENGTH + 1, MAX_FRAME_LENGTH - 1, MAX_FRAME_LENGTH] {
assert_eq!(validate_frame_length(length).unwrap(), length, "length {length} should be accepted");
}
}
#[test]
fn test_validate_frame_length_rejects_out_of_range_lengths() {
for length in [0, MIN_FRAME_LENGTH - 1, MAX_FRAME_LENGTH + 1, u32::MAX as usize] {
let err = validate_frame_length(length).expect_err("an out-of-range length must be rejected");
assert!(
matches!(err, Error::InvalidFrame(_)),
"length {length} must raise InvalidFrame, got {err:?}"
);
assert!(err.is_connection_lost(), "a desynchronized stream must drive a reconnect");
}
}
#[derive(Default)]
struct CapturingSink {
notices: std::sync::Mutex<Vec<Notice>>,
}
impl NoticeSink for CapturingSink {
fn deliver(&self, notice: Notice) {
self.notices.lock().unwrap().push(notice);
}
}
#[test]
fn test_report_unroutable_frame_raises_a_notice_for_an_unknown_kind() {
let message = ResponseMessage::from("9999\01\0");
assert_eq!(message.message_type(), IncomingMessages::NotValid, "fixture must be unroutable");
let sink = CapturingSink::default();
report_unroutable_frame(&message, &sink);
let notices = sink.notices.lock().unwrap();
assert_eq!(notices.len(), 1, "an unknown kind must be observable, not just logged");
assert_eq!(notices[0].code, UNKNOWN_MESSAGE_TYPE_CODE);
assert!(
notices[0].message.contains("9999"),
"notice must name the offending id, got {:?}",
notices[0].message
);
}
#[test]
fn test_report_unroutable_frame_stays_quiet_for_a_known_kind() {
let message = ResponseMessage::from("15\01\0DU1234567\0");
assert_eq!(message.message_type(), IncomingMessages::ManagedAccounts);
let sink = CapturingSink::default();
report_unroutable_frame(&message, &sink);
assert!(
sink.notices.lock().unwrap().is_empty(),
"a known kind with no listener is routine and must raise no notice"
);
}
#[test]
fn test_fibonacci_backoff() {
let mut backoff = FibonacciBackoff::new(10);
assert_eq!(backoff.next_delay(), Duration::from_secs(1));
assert_eq!(backoff.next_delay(), Duration::from_secs(2));
assert_eq!(backoff.next_delay(), Duration::from_secs(3));
assert_eq!(backoff.next_delay(), Duration::from_secs(5));
assert_eq!(backoff.next_delay(), Duration::from_secs(8));
assert_eq!(backoff.next_delay(), Duration::from_secs(10)); assert_eq!(backoff.next_delay(), Duration::from_secs(10)); }
#[test]
fn test_fibonacci_backoff_never_overflows() {
let mut backoff = FibonacciBackoff::new(30);
for _ in 0..100 {
assert!(backoff.next_delay() <= Duration::from_secs(30));
}
assert_eq!(backoff.next_delay(), Duration::from_secs(30));
}
#[test]
fn test_fibonacci_backoff_never_overflows_with_huge_max() {
let mut backoff = FibonacciBackoff::new(u64::MAX);
for _ in 0..100 {
backoff.next_delay();
}
assert_eq!(backoff.next_delay(), Duration::from_secs(u64::MAX));
}
#[test]
fn test_fibonacci_backoff_zero_max() {
let mut backoff = FibonacciBackoff::new(0);
assert_eq!(backoff.next_delay(), Duration::ZERO);
assert_eq!(backoff.next_delay(), Duration::ZERO);
}