#![allow(clippy::expect_used, clippy::panic, clippy::too_many_lines)]
use crate::wire::{
AttachAttemptToken, AttachEnvelope, AttachSecret, CredentialAttachRequest,
CredentialAttachResponse, EnrollmentEnvelope, EnrollmentReceiptCapacityScope,
EnrollmentRequest, EnrollmentResponse, EnrollmentToken, Generation, IdentityCapacityExceeded,
IdentityCapacityScope, ReceiptCapacityScope,
};
use super::capacity::{
BindingSlotDecision, BindingSlotOccupancy, CapacityCounter, CapacityCounterInvariantError,
ConnectionConversationTracking, CredentialAttachCapacityCounters,
CredentialAttachCapacityDecision, EnrollmentCapacityCounters, EnrollmentCapacityDecision,
FreshParticipantCapacityCounter, FreshParticipantCapacityCounterInvariantError,
SemanticConnectionCapacityDecision, select_credential_attach_binding_slot,
select_credential_attach_capacity, select_enrollment_binding_slot, select_enrollment_capacity,
select_semantic_connection_capacity,
};
fn counter(limit: u64, occupied: u64) -> CapacityCounter {
CapacityCounter::try_new(limit, occupied).expect("test counter must be valid")
}
fn fresh_counter(limit: u64) -> FreshParticipantCapacityCounter {
FreshParticipantCapacityCounter::try_new(limit, 0)
.expect("fresh test participant counter must be valid")
}
fn generation(value: u64) -> Generation {
Generation::new(value).expect("test generation must be nonzero")
}
fn enrollment() -> EnrollmentRequest {
EnrollmentRequest {
conversation_id: 41,
enrollment_token: EnrollmentToken::new([4; 16]),
}
}
fn enrollment_envelope() -> EnrollmentEnvelope {
let request = enrollment();
EnrollmentEnvelope {
conversation_id: request.conversation_id,
enrollment_token: request.enrollment_token,
}
}
fn attach() -> CredentialAttachRequest {
CredentialAttachRequest {
conversation_id: 42,
participant_id: 73,
capability_generation: generation(7),
attach_secret: AttachSecret::new([8; 32]),
attach_attempt_token: AttachAttemptToken::new([9; 16]),
accept_marker_delivery_seq: Some(101),
}
}
fn attach_envelope() -> AttachEnvelope {
let request = attach();
AttachEnvelope {
conversation_id: request.conversation_id,
participant_id: request.participant_id,
capability_generation: request.capability_generation,
attach_attempt_token: request.attach_attempt_token,
accept_marker_delivery_seq: request.accept_marker_delivery_seq,
}
}
fn enrollment_counters(values: [CapacityCounter; 5]) -> EnrollmentCapacityCounters {
EnrollmentCapacityCounters::new(
values[0],
values[1],
values[2],
fresh_counter(31),
values[3],
values[4],
fresh_counter(32),
)
}
fn attach_counters(values: [CapacityCounter; 5]) -> CredentialAttachCapacityCounters {
CredentialAttachCapacityCounters::new(values[0], values[1], values[2], values[3], values[4])
}
#[test]
fn capacity_counter_requires_a_nonzero_bounded_state() {
assert_eq!(
CapacityCounter::try_new(0, 0),
Err(CapacityCounterInvariantError::ZeroLimit),
);
assert_eq!(
CapacityCounter::try_new(3, 4),
Err(CapacityCounterInvariantError::OccupiedExceedsLimit {
occupied: 4,
limit: 3,
}),
);
let full = counter(3, 3);
assert_eq!(full.limit(), 3);
assert_eq!(full.occupied(), 3);
assert!(full.is_full());
}
#[test]
fn fresh_participant_counters_reject_nonempty_restored_state() {
assert_eq!(
FreshParticipantCapacityCounter::try_new(2, 1),
Err(FreshParticipantCapacityCounterInvariantError::Nonempty { occupied: 1 }),
);
assert_eq!(
FreshParticipantCapacityCounter::try_new(0, 0),
Err(FreshParticipantCapacityCounterInvariantError::Capacity(
CapacityCounterInvariantError::ZeroLimit,
)),
);
}
#[test]
fn already_tracked_semantic_conversation_consumes_zero_capacity_at_full_limit() {
let decision = select_semantic_connection_capacity(
ConnectionConversationTracking::AlreadyTracked,
counter(2, 2),
);
let SemanticConnectionCapacityDecision::Commit(commit) = decision else {
panic!("an already tracked conversation must preserve full capacity");
};
assert_eq!(commit.resulting(), counter(2, 2));
assert!(!commit.newly_tracked());
}
#[test]
fn first_untracked_semantic_conversation_returns_exact_capacity_refusal() {
assert_eq!(
select_semantic_connection_capacity(
ConnectionConversationTracking::Untracked,
counter(2, 2),
),
SemanticConnectionCapacityDecision::Respond { limit: 2 },
);
let decision = select_semantic_connection_capacity(
ConnectionConversationTracking::Untracked,
counter(2, 1),
);
let SemanticConnectionCapacityDecision::Commit(commit) = decision else {
panic!("one free connection slot must commit");
};
assert_eq!(commit.resulting(), counter(2, 2));
assert!(commit.newly_tracked());
}
#[test]
fn binding_slot_selectors_return_exact_origin_specific_outcomes() {
let enrollment_request = enrollment();
assert_eq!(
select_enrollment_binding_slot(
&enrollment_request,
BindingSlotOccupancy::Occupied {
participant_id: 999,
},
),
BindingSlotDecision::Respond(
EnrollmentResponse::connection_conversation_binding_occupied(&enrollment_envelope()),
),
);
assert_eq!(
select_enrollment_binding_slot(&enrollment_request, BindingSlotOccupancy::Empty),
BindingSlotDecision::Available,
);
let attach_request = attach();
assert_eq!(
select_credential_attach_binding_slot(
&attach_request,
BindingSlotOccupancy::Occupied {
participant_id: attach_request.participant_id,
},
),
BindingSlotDecision::Available,
);
assert_eq!(
select_credential_attach_binding_slot(
&attach_request,
BindingSlotOccupancy::Occupied { participant_id: 74 },
),
BindingSlotDecision::Respond(
CredentialAttachResponse::connection_conversation_binding_occupied(&attach_envelope()),
),
);
}
#[test]
fn enrollment_runtime_capacity_uses_the_exact_five_scope_precedence() {
let request = enrollment();
for failing_index in 0..5 {
let mut values = [counter(2, 1); 5];
for (index, value) in values.iter_mut().enumerate().skip(failing_index) {
let limit = 10 + u64::try_from(index).expect("five indices fit u64");
*value = counter(limit, limit);
}
let expected = match failing_index {
0 => EnrollmentResponse::identity_capacity_exceeded(IdentityCapacityExceeded {
request: enrollment_envelope(),
scope: IdentityCapacityScope::Server,
limit: 10,
occupied: 10,
}),
1 => EnrollmentResponse::identity_capacity_exceeded(IdentityCapacityExceeded {
request: enrollment_envelope(),
scope: IdentityCapacityScope::Conversation,
limit: 11,
occupied: 11,
}),
2 => EnrollmentResponse::receipt_capacity_exceeded(
enrollment_envelope(),
EnrollmentReceiptCapacityScope::LiveReceiptServer,
12,
12,
),
3 => EnrollmentResponse::receipt_capacity_exceeded(
enrollment_envelope(),
EnrollmentReceiptCapacityScope::ProvenanceServer,
13,
13,
),
4 => EnrollmentResponse::receipt_capacity_exceeded(
enrollment_envelope(),
EnrollmentReceiptCapacityScope::ProvenanceConversation,
14,
14,
),
_ => panic!("five enrollment scopes are exhaustive"),
};
assert_eq!(
select_enrollment_capacity(&request, enrollment_counters(values)),
EnrollmentCapacityDecision::Respond(expected),
);
}
}
#[test]
fn enrollment_success_carries_every_incremented_counter_atomically() {
let current = EnrollmentCapacityCounters::new(
counter(11, 1),
counter(12, 2),
counter(13, 3),
fresh_counter(16),
counter(14, 4),
counter(15, 5),
fresh_counter(17),
);
let decision = select_enrollment_capacity(&enrollment(), current);
let EnrollmentCapacityDecision::Commit(commit) = decision else {
panic!("all enrollment counters have capacity");
};
let resulting = commit.resulting();
assert_eq!(resulting.identity_server(), counter(11, 2));
assert_eq!(resulting.identity_conversation(), counter(12, 3));
assert_eq!(resulting.live_receipt_server(), counter(13, 4));
assert_eq!(resulting.live_receipt_participant(), counter(16, 1));
assert_eq!(resulting.provenance_server(), counter(14, 5));
assert_eq!(resulting.provenance_conversation(), counter(15, 6));
assert_eq!(resulting.provenance_participant(), counter(17, 1));
assert_eq!(current.identity_server(), counter(11, 1));
assert_eq!(current.live_receipt_participant().occupied(), 0);
assert_eq!(current.provenance_conversation(), counter(15, 5));
assert_eq!(current.provenance_participant().occupied(), 0);
}
#[test]
fn credential_attach_capacity_uses_all_five_receipt_scopes_in_order() {
let request = attach();
let scopes = [
ReceiptCapacityScope::LiveReceiptServer,
ReceiptCapacityScope::LiveReceiptParticipant,
ReceiptCapacityScope::ProvenanceServer,
ReceiptCapacityScope::ProvenanceConversation,
ReceiptCapacityScope::ProvenanceParticipant,
];
for (failing_index, scope) in scopes.into_iter().enumerate() {
let mut values = [counter(2, 1); 5];
for (index, value) in values.iter_mut().enumerate().skip(failing_index) {
let limit = 20 + u64::try_from(index).expect("five indices fit u64");
*value = counter(limit, limit);
}
let limit = 20 + u64::try_from(failing_index).expect("five indices fit u64");
assert_eq!(
select_credential_attach_capacity(&request, attach_counters(values)),
CredentialAttachCapacityDecision::Respond(
CredentialAttachResponse::receipt_capacity_exceeded(
attach_envelope(),
scope,
limit,
limit,
),
),
);
}
}
#[test]
fn credential_attach_success_carries_every_incremented_counter_atomically() {
let current = CredentialAttachCapacityCounters::new(
counter(21, 1),
counter(22, 2),
counter(23, 3),
counter(24, 4),
counter(25, 5),
);
let decision = select_credential_attach_capacity(&attach(), current);
let CredentialAttachCapacityDecision::Commit(commit) = decision else {
panic!("all credential-attach counters have capacity");
};
let resulting = commit.resulting();
assert_eq!(resulting.live_receipt_server(), counter(21, 2));
assert_eq!(resulting.live_receipt_participant(), counter(22, 3));
assert_eq!(resulting.provenance_server(), counter(23, 4));
assert_eq!(resulting.provenance_conversation(), counter(24, 5));
assert_eq!(resulting.provenance_participant(), counter(25, 6));
assert_eq!(current.live_receipt_server(), counter(21, 1));
assert_eq!(current.provenance_participant(), counter(25, 5));
}