use super::*;
use crate::common::test_utils::helpers::CapturingSink;
use crate::messages::UNKNOWN_MESSAGE_TYPE_CODE;
#[test]
fn 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 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");
}
}
#[test]
fn 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();
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 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().is_empty(),
"a known kind with no listener is routine and must raise no notice"
);
}
#[test]
fn 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 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 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 fibonacci_backoff_zero_max() {
let mut backoff = FibonacciBackoff::new(0);
assert_eq!(backoff.next_delay(), Duration::ZERO);
assert_eq!(backoff.next_delay(), Duration::ZERO);
}
#[test]
fn lease_is_live_while_any_holder_is() {
let lease = Lease::new();
let lease_ref = lease.downgrade();
let clone = lease.clone();
drop(lease);
assert!(lease_ref.is_live(), "a clone still holds the lease");
drop(clone);
assert!(!lease_ref.is_live(), "no holder left");
}
#[test]
fn lease_ref_identifies_its_lease() {
let lease = Lease::new();
let other = Lease::new();
assert!(lease.downgrade().is(&lease.downgrade()));
assert!(lease.downgrade().is(&lease.clone().downgrade()), "clones share identity");
assert!(!lease.downgrade().is(&other.downgrade()));
let lease_ref = lease.downgrade();
drop(lease);
assert!(lease_ref.is(&lease_ref.clone()));
assert!(!lease_ref.is(&other.downgrade()));
}
#[test]
fn lock_recovers_from_a_poisoned_mutex() {
let mutex = Mutex::new(1);
poison_with(|| mutex.lock().unwrap());
assert!(mutex.is_poisoned());
*lock(&mutex) += 1;
assert_eq!(*lock(&mutex), 2);
}
#[test]
fn read_and_write_lock_recover_from_a_poisoned_rwlock() {
let rwlock = RwLock::new(1);
poison_with(|| rwlock.write().unwrap());
assert!(rwlock.is_poisoned());
*write_lock(&rwlock) += 1;
assert_eq!(*read_lock(&rwlock), 2);
}
#[test]
fn session_state_verdicts() {
let cases = [
(SessionState::Connected, None, None),
(SessionState::Disconnected, Some("ConnectionReset"), None),
(SessionState::ShutDown, Some("Shutdown"), Some("Shutdown")),
(SessionState::Failed, Some("ConnectionFailed"), Some("ConnectionFailed")),
];
for (state, check, check_ended) in cases {
assert_eq!(state.check().err().map(|e| e.to_string()).as_deref(), check, "{state:?} check");
assert_eq!(
state.check_ended().err().map(|e| e.to_string()).as_deref(),
check_ended,
"{state:?} check_ended"
);
assert_eq!(state.is_terminal(), check_ended.is_some(), "{state:?} is_terminal");
}
}