use super::*;
#[tokio::test]
async fn segmented_complex_ack_advances_after_negative_ack_zero() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(1);
let invoke_id = 0x41;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0x55; 128],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, true, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
handle.abort();
let _ = handle.await;
}
#[tokio::test]
async fn segmented_complex_ack_ignores_future_positive_segment_ack() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(2);
let invoke_id = 0x42;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0x66; 128],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 1)).await;
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
handle.abort();
let _ = handle.await;
}
#[tokio::test]
async fn segmented_complex_ack_retransmits_current_segment_after_negative_ack_previous_sequence() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(4);
let invoke_id = 0x44;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0x88; 192],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, true, 0)).await;
wait_for_sent_len(&sent, 3).await;
assert_eq!(complex_ack_sequence(&sent, 2), 1);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 1)).await;
wait_for_sent_len(&sent, 4).await;
assert_eq!(complex_ack_sequence(&sent, 3), 2);
handle.abort();
let _ = handle.await;
}
#[tokio::test]
async fn segmented_complex_ack_advances_when_retransmitted_current_segment_receives_negative_ack() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(14);
let invoke_id = 0x4E;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack_with_options(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac.clone(),
invoke_id,
vec![0x8E; 128],
SegmentedSendOptions {
segment_timeout: Duration::from_millis(100),
max_retries: 2,
},
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
dispatch_test_apdu(
&network,
&seg_ack_senders,
&source_mac,
Apdu::SegmentAck(segment_ack(invoke_id, false, 0)),
)
.await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
wait_for_sent_len(&sent, 3).await;
assert_eq!(complex_ack_sequence(&sent, 2), 1);
dispatch_test_apdu(
&network,
&seg_ack_senders,
&source_mac,
Apdu::SegmentAck(segment_ack(invoke_id, true, 1)),
)
.await;
wait_for_sent_len(&sent, 4).await;
assert_eq!(complex_ack_sequence(&sent, 3), 2);
dispatch_test_apdu(
&network,
&seg_ack_senders,
&source_mac,
Apdu::SegmentAck(segment_ack(invoke_id, false, 2)),
)
.await;
tokio::time::timeout(Duration::from_secs(1), handle)
.await
.expect("segmented response task should complete after receiver-style current NAK")
.expect("segmented response task should not panic");
assert!(
!seg_ack_senders.lock().contains_key(&key),
"SegmentAck sender entry should be removed after final ACK"
);
}
#[tokio::test]
async fn segmented_complex_ack_ignores_out_of_range_negative_segment_ack() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(5);
let invoke_id = 0x45;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0x99; 128],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, true, 255)).await;
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
handle.abort();
let _ = handle.await;
}
#[tokio::test]
async fn segmented_complex_ack_ignores_segment_ack_with_server_bit_set() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(6);
let invoke_id = 0x46;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0xAA; 128],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(
&seg_ack_senders,
&key,
server_segment_ack(invoke_id, false, 0),
)
.await;
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
handle.abort();
let _ = handle.await;
}
#[tokio::test]
async fn segmented_complex_ack_ignores_stale_positive_final_segment_ack() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(7);
let invoke_id = 0x47;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0xBB; 128],
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 1)).await;
wait_for_sent_len(&sent, 3).await;
assert_eq!(complex_ack_sequence(&sent, 2), 2);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 1)).await;
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, true, 1)).await;
wait_for_sent_len(&sent, 4).await;
assert_eq!(complex_ack_sequence(&sent, 3), 2);
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 2)).await;
tokio::time::timeout(Duration::from_secs(1), handle)
.await
.expect("segmented response task should exit after final ACK")
.expect("segmented response task should not panic");
assert!(
!seg_ack_senders.lock().contains_key(&key),
"SegmentAck sender entry should be removed after final ACK"
);
}
#[tokio::test]
async fn segmented_complex_ack_aborts_after_repeated_negative_segment_ack() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(3);
let invoke_id = 0x43;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0x77; 128],
);
wait_for_sent_len(&sent, 1).await;
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, false, 0)).await;
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 1);
for expected_len in 3..=(MAX_NEG_SEGMENT_ACK_RETRIES as usize + 3) {
send_segment_ack(&seg_ack_senders, &key, segment_ack(invoke_id, true, 0)).await;
wait_for_sent_len(&sent, expected_len).await;
}
assert_eq!(
abort_reason(&sent, sent_count(&sent) - 1),
AbortReason::TSM_TIMEOUT
);
tokio::time::timeout(Duration::from_secs(1), handle)
.await
.expect("segmented response task should exit after abort")
.expect("segmented response task should not panic");
assert!(
!seg_ack_senders.lock().contains_key(&key),
"SegmentAck sender entry should be removed after abort"
);
}
#[tokio::test]
async fn segmented_complex_ack_retransmits_after_segment_ack_timeout_then_goes_idle() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(8);
let invoke_id = 0x48;
let key: SegKey = (source_mac.clone(), None, invoke_id, None);
let handle = spawn_segmented_complex_ack_with_options(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0xCC; 128],
SegmentedSendOptions {
segment_timeout: Duration::from_millis(25),
max_retries: 1,
},
);
wait_for_sent_len(&sent, 1).await;
assert_eq!(complex_ack_sequence(&sent, 0), 0);
wait_for_sent_len(&sent, 2).await;
assert_eq!(complex_ack_sequence(&sent, 1), 0);
tokio::time::timeout(Duration::from_secs(1), handle)
.await
.expect("segmented response task should exit after timeout retry exhaustion")
.expect("segmented response task should not panic");
assert_eq!(
sent_count(&sent),
2,
"server should go idle after final segment timeout without sending Abort"
);
assert!(
(0..sent_count(&sent))
.all(|index| !matches!(decoded_sent_apdu(&sent, index), Apdu::Abort(_))),
"server must not send Abort after final segment timeout"
);
assert!(
!seg_ack_senders.lock().contains_key(&key),
"SegmentAck sender entry should be removed after timeout retry exhaustion"
);
}
#[tokio::test]
async fn segmented_complex_ack_sets_npdu_expecting_reply_for_segments() {
let (network, sent) = recording_network();
let seg_ack_senders = Arc::new(segmented_send::SegmentedSendRegistry::default());
let source_mac = test_mac(9);
let invoke_id = 0x49;
let handle = spawn_segmented_complex_ack(
Arc::clone(&network),
Arc::clone(&seg_ack_senders),
source_mac,
invoke_id,
vec![0xDD; 128],
);
wait_for_sent_len(&sent, 1).await;
assert!(sent_expecting_reply(&sent, 0));
handle.abort();
let _ = handle.await;
}