use alloc::vec;
use alloc::vec::Vec;
use super::gen_skip_supersession_tests::{
TestResult, bound_at, expected_exact_detach, generation, replay_envelope,
};
use super::*;
use crate::wire::{
BindingRequiredEnvelope, ConnectionConversationCapacityExceeded, DetachCommitted,
DetachStaleAuthority, NoBinding, ObserverBackpressure, ObserverBackpressureState,
ParticipantReferenceEnvelope, ParticipantUnknown, ResponseEnvelope, Retired, ServerValue,
StaleAuthority,
};
const REPLAY_GENERATION: u64 = 3;
const REPLAY_TOKEN: u8 = 0x91;
fn correlated_refusals_of_the_replayed_detach() -> TestResult<Vec<(&'static str, ServerValue)>> {
let request = replay_envelope(REPLAY_GENERATION, REPLAY_TOKEN)?;
Ok(vec![
(
"StaleAuthority::Detach(Live)",
ServerValue::StaleAuthority(StaleAuthority::Detach(DetachStaleAuthority::Live {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
detach_attempt_token: request.detach_attempt_token,
current_generation: generation(REPLAY_GENERATION + 1)?,
})),
),
(
"NoBinding(Detach)",
ServerValue::NoBinding(NoBinding {
request: BindingRequiredEnvelope::Detach(request.clone()),
}),
),
(
"ParticipantUnknown(Detach)",
ServerValue::ParticipantUnknown(ParticipantUnknown {
request: ParticipantReferenceEnvelope::Detach(request.clone()),
}),
),
(
"ConnectionConversationCapacityExceeded(SemanticRequest{Detach})",
ServerValue::ConnectionConversationCapacityExceeded(
ConnectionConversationCapacityExceeded::SemanticRequest {
request: ResponseEnvelope::Detach(request.clone()),
limit: 4,
},
),
),
(
"ObserverBackpressure::Detach",
ServerValue::ObserverBackpressure(ObserverBackpressure::Detach {
request: request.clone(),
committed_binding_epoch: super::gen_skip_supersession_tests::epoch(
REPLAY_GENERATION,
)?,
state: ObserverBackpressureState::initial(9),
}),
),
(
"Retired(Participant{Detach}) below the replay generation",
ServerValue::Retired(Retired::Participant {
request: ParticipantReferenceEnvelope::Detach(request),
retired_generation: generation(REPLAY_GENERATION - 1)?,
}),
),
])
}
fn coupled_in_flight_detach() -> TestResult<ClientParticipantAggregate> {
let mut aggregate = bound_at(REPLAY_GENERATION)?;
aggregate.expected = Some(expected_exact_detach(REPLAY_GENERATION, REPLAY_TOKEN)?);
aggregate.next_operation_authorization = 1;
aggregate.detach_replay.state = replay::DetachReplayState::Recorded {
request: replay_envelope(REPLAY_GENERATION, REPLAY_TOKEN)?,
status: DetachReplayStatus::InFlight,
};
Ok(aggregate)
}
fn retained_refusal(aggregate: &ClientParticipantAggregate) -> Option<&ServerValue> {
match aggregate.detach_replay.status() {
Some(DetachReplayStatus::Terminal(DetachReplayTerminal::AuthorityRefused(refused))) => {
Some(refused.value())
}
_ => None,
}
}
fn deliver(value: ServerValue) -> TestResult<ClientParticipantAggregate> {
let correlation = ClientResponseCorrelation { authorization: 1 };
let ClientCorrelatedInboundDecision::Applied(applied) =
decide_correlated_inbound(coupled_in_flight_detach()?, value, correlation)
else {
return Err("a value carrying the exact detach's wire identity must correlate");
};
let (aggregate, _) = applied.into_parts();
Ok(aggregate)
}
#[test]
fn every_correlated_refusal_settles_the_replay_and_stays_persistable() -> TestResult {
for (name, value) in correlated_refusals_of_the_replayed_detach()? {
let aggregate = deliver(value)?;
assert!(
aggregate.expected.is_none(),
"{name}: the correlated response must retire the expected slot"
);
assert!(
!matches!(
aggregate.detach_replay.status(),
Some(DetachReplayStatus::Parked | DetachReplayStatus::InFlight)
),
"#59 REPRODUCED: {name} cleared the expected detach and left the replay active"
);
let record = aggregate.resume_record();
assert!(
record.is_ok(),
"#59 REPRODUCED: {name} minted an aggregate that refuses to encode"
);
assert!(
record.is_ok_and(|record| record.restore().is_ok()),
"#59 REPRODUCED: {name} minted a record that no restore accepts"
);
}
Ok(())
}
#[test]
fn the_settled_terminal_retains_the_exact_refusing_value() -> TestResult {
for (name, value) in correlated_refusals_of_the_replayed_detach()? {
let aggregate = deliver(value.clone())?;
assert_eq!(
retained_refusal(&aggregate),
Some(&value),
"#59 REPRODUCED: {name} did not settle into a typed authority refusal \
retaining the exact value"
);
let restored = aggregate
.resume_record()
.map_err(|_| "a settled replay must encode")?
.restore()
.map_err(|_| "a settled replay must restore")?;
assert_eq!(
retained_refusal(&restored),
Some(&value),
"{name}: the typed refusal must survive the canonical round trip unprojected"
);
}
Ok(())
}
#[test]
fn an_answered_detach_keeps_its_own_terminal() -> TestResult {
let request = replay_envelope(REPLAY_GENERATION, REPLAY_TOKEN)?;
let committed = DetachCommitted::new(
request.conversation_id,
request.participant_id,
request.detach_attempt_token,
super::gen_skip_supersession_tests::epoch(REPLAY_GENERATION)?,
13,
);
let aggregate = deliver(ServerValue::DetachCommitted(committed))?;
assert!(matches!(
aggregate.detach_replay.status(),
Some(DetachReplayStatus::Terminal(
DetachReplayTerminal::DetachCommitted(_)
))
));
Ok(())
}
#[test]
fn a_retirement_above_the_replay_still_supersedes_it() -> TestResult {
let request = replay_envelope(REPLAY_GENERATION, REPLAY_TOKEN)?;
let aggregate = deliver(ServerValue::Retired(Retired::Participant {
request: ParticipantReferenceEnvelope::Detach(request),
retired_generation: generation(REPLAY_GENERATION)?,
}))?;
assert!(matches!(
aggregate.detach_replay.status(),
Some(DetachReplayStatus::LeaveSuperseded)
));
Ok(())
}
#[test]
fn a_refusal_never_settles_a_replay_it_does_not_name() -> TestResult {
let mut aggregate = bound_at(REPLAY_GENERATION)?;
aggregate.expected = Some(expected_exact_detach(REPLAY_GENERATION, REPLAY_TOKEN)?);
aggregate.next_operation_authorization = 1;
aggregate.detach_replay.state = replay::DetachReplayState::Recorded {
request: replay_envelope(REPLAY_GENERATION, 0x77)?,
status: DetachReplayStatus::InFlight,
};
let correlation = ClientResponseCorrelation { authorization: 1 };
let value = ServerValue::NoBinding(NoBinding {
request: BindingRequiredEnvelope::Detach(replay_envelope(REPLAY_GENERATION, REPLAY_TOKEN)?),
});
let ClientCorrelatedInboundDecision::Applied(applied) =
decide_correlated_inbound(aggregate, value, correlation)
else {
return Err("the refusal still names the expected detach and must correlate");
};
let (aggregate, _) = applied.into_parts();
assert!(
matches!(
aggregate.detach_replay.status(),
Some(DetachReplayStatus::InFlight)
),
"a refusal must not settle a replay whose retained detach it does not name"
);
Ok(())
}