use wireframe::fragment::{FragmentError, MessageId, ReassembledMessage, ReassemblyError};
use super::assert_helpers::{assert_body_eq, assert_usize_field};
use crate::integration_helpers::TestResult;
#[derive(Clone, Copy, Debug)]
pub struct FragmentReassemblySnapshot<'a> {
last_reassembled: Option<&'a ReassembledMessage>,
last_error: Option<&'a ReassemblyError>,
evicted_ids: &'a [MessageId],
buffered_messages: usize,
}
impl<'a> FragmentReassemblySnapshot<'a> {
#[must_use]
pub fn new(
last_reassembled: Option<&'a ReassembledMessage>,
last_error: Option<&'a ReassemblyError>,
evicted_ids: &'a [MessageId],
buffered_messages: usize,
) -> Self {
Self {
last_reassembled,
last_error,
evicted_ids,
buffered_messages,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FragmentReassemblyErrorExpectation {
MessageTooLarge {
message_id: MessageId,
},
MessageTooLargeAny,
IndexMismatch,
MessageMismatch,
SeriesComplete,
IndexOverflow,
}
pub fn assert_fragment_reassembly_absent(snapshot: FragmentReassemblySnapshot<'_>) -> TestResult {
if snapshot.last_reassembled.is_none() {
Ok(())
} else {
Err("unexpected reassembled message present".into())
}
}
pub fn assert_fragment_reassembly_completed_len(
snapshot: FragmentReassemblySnapshot<'_>,
expected_len: usize,
) -> TestResult {
let Some(message) = snapshot.last_reassembled else {
return Err("no message reassembled".into());
};
assert_usize_field(message.payload().len(), expected_len, "payload length")
}
pub fn assert_fragment_reassembly_completed_bytes(
snapshot: FragmentReassemblySnapshot<'_>,
expected: &[u8],
) -> TestResult {
let Some(message) = snapshot.last_reassembled else {
return Err("no message reassembled".into());
};
assert_body_eq(message.payload(), expected, "reassembled payload")
}
pub fn assert_fragment_reassembly_error(
snapshot: FragmentReassemblySnapshot<'_>,
expected: FragmentReassemblyErrorExpectation,
) -> TestResult {
let Some(err) = snapshot.last_error else {
return Err("no reassembly error captured".into());
};
if matches_fragment_error(err, expected) {
Ok(())
} else {
Err(format!("expected {expected:?}, got {err:?}").into())
}
}
pub fn assert_fragment_reassembly_buffered_messages(
snapshot: FragmentReassemblySnapshot<'_>,
expected: usize,
) -> TestResult {
assert_usize_field(snapshot.buffered_messages, expected, "buffered messages")
}
pub fn assert_fragment_reassembly_evicted(
snapshot: FragmentReassemblySnapshot<'_>,
message_id: MessageId,
) -> TestResult {
if snapshot.evicted_ids.contains(&message_id) {
Ok(())
} else {
Err(format!("message {} was not evicted", message_id.get()).into())
}
}
fn matches_fragment_error(
err: &ReassemblyError,
expected: FragmentReassemblyErrorExpectation,
) -> bool {
match expected {
FragmentReassemblyErrorExpectation::MessageTooLarge { message_id } => {
matches!(
err,
ReassemblyError::MessageTooLarge {
message_id: actual_id,
..
} if *actual_id == message_id
)
}
FragmentReassemblyErrorExpectation::MessageTooLargeAny => {
matches!(err, ReassemblyError::MessageTooLarge { .. })
}
FragmentReassemblyErrorExpectation::IndexMismatch => matches!(
err,
ReassemblyError::Fragment(FragmentError::IndexMismatch { .. })
),
FragmentReassemblyErrorExpectation::MessageMismatch => matches!(
err,
ReassemblyError::Fragment(FragmentError::MessageMismatch { .. })
),
FragmentReassemblyErrorExpectation::SeriesComplete => matches!(
err,
ReassemblyError::Fragment(FragmentError::SeriesComplete)
),
FragmentReassemblyErrorExpectation::IndexOverflow => matches!(
err,
ReassemblyError::Fragment(FragmentError::IndexOverflow { .. })
),
}
}