Align the replay-window test with the reliable sequence domain

Separating the sequence domains gated duplicate suppression on packet.reliable
in Rust, TypeScript and C# alike, so lossy datagrams are remembered nowhere and
a replayed one is delivered again. That is the intended contract -- lossy
traffic is fire-and-forget and its history is bounded by not existing -- but
replay_history_is_bounded_and_idle_hub_sessions_expire still asserted the old
behaviour with a lossy packet and had been left failing on the branch.

The three tests the change added in each runtime cover ordered-requires-
reliable, per-channel sequenced scope, and lossy gaps. None covers the bounded
replay window, which was this test's real subject, so removing the assertion
would have dropped the only coverage of a security property.

The lossy assertion now states the new contract, and the reliable domain gets
its own replay check: 4,100 acknowledged reliable sends, then the oldest
replayed and rejected by the window.

Rust 151, C# 148, TypeScript 48, Python 138 all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-04 00:36:36 +02:00
commit 85bc4c71b5

View file

@ -453,10 +453,42 @@ fn replay_history_is_bounded_and_idle_hub_sessions_expire() -> Result<()> {
}
assert_eq!(receiver.receive(&packet, sequence)?.delivered.len(), 1);
}
assert!(
// Separating the sequence domains moved duplicate suppression onto the
// reliable domain, so a replayed lossy datagram is delivered again rather
// than silently dropped. Lossy traffic is remembered nowhere, which is what
// bounds its history.
assert_eq!(
receiver
.receive(&oldest.expect("oldest packet"), 4_101)?
.delivered
.len(),
1
);
// The bounded replay window still guards the reliable domain, and nothing
// else asserts it once the lossy path stopped being remembered.
let mut oldest_reliable = None;
for step in 0..4_100u64 {
let packet = sender.send(
"schema",
vec![(step % 251) as u8],
CultNetRudpSendOptions {
reliable: true,
ordered: false,
sequenced: false,
now_ms: step,
},
)?;
if oldest_reliable.is_none() {
oldest_reliable = Some(packet.clone());
}
assert_eq!(receiver.receive(&packet, step)?.delivered.len(), 1);
sender.receive(&receiver.create_ack_for(packet.sequence), step)?;
}
assert!(
receiver
.receive(&oldest_reliable.expect("oldest reliable packet"), 4_101)?
.delivered
.is_empty()
);