extern crate alloc;
use alloc::boxed::Box;
use alloc::vec;
use faction::cluster_view::ClusterView;
use faction::command::Command;
use faction::config::Config;
use faction::faction::Faction;
use faction::no_op_observer::NoOpObserver;
use faction::outcome::Outcome;
use faction::peer_state::PeerState;
use faction::process_result::ProcessResult;
use faction::quorum_policy::QuorumPolicy;
use faction::state::State;
use faction::states::initial::Initial;
fn test_faction() -> Faction {
Faction::new(
Config::new(0, vec![0, 1, 2, 3, 4], QuorumPolicy::new(4)),
Box::new(NoOpObserver),
)
}
#[test]
fn process_accepts_participation_observed() {
let mut faction = test_faction();
let outcomes = match faction.process(Command::ParticipationObserved { peer_id: 1 }) {
ProcessResult::Accepted { outcomes, .. } => outcomes,
ProcessResult::Probed { .. } => unreachable!(),
ProcessResult::Rejected { .. } => panic!("expected accepted"),
};
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(
outcomes,
vec![Outcome::ParticipationAccepted { peer_id: 1 }]
);
assert_eq!(snap.peer_state(), PeerState::Pinging);
assert_eq!(snap.pinging_peers().len(), 1);
}
#[test]
fn process_accepts_ready_observed() {
let mut faction = test_faction();
let outcomes = match faction.process(Command::ReadyObserved { peer_id: 1 }) {
ProcessResult::Accepted { outcomes, .. } => outcomes,
ProcessResult::Probed { .. } => unreachable!(),
ProcessResult::Rejected { .. } => panic!("expected accepted"),
};
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(outcomes, vec![Outcome::ReadyAccepted { peer_id: 1 }]);
assert_eq!(snap.peer_state(), PeerState::Pinging);
assert_eq!(snap.collecting_peers().len(), 1);
}
#[test]
fn process_rejects_local_participation_completed() {
let mut faction = test_faction();
assert!(matches!(
faction.process(Command::LocalParticipationCompleted),
ProcessResult::Rejected { .. }
));
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(snap.peer_state(), PeerState::Fresh);
assert_eq!(snap.collecting_peers().len(), 0);
}
#[test]
fn process_rejects_deadline_expired() {
let mut faction = test_faction();
assert!(matches!(
faction.process(Command::DeadlineExpired),
ProcessResult::Rejected { .. }
));
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(snap.peer_state(), PeerState::Fresh);
assert_eq!(snap.conclusion(), None);
}
#[test]
fn process_stays_in_initial_after_rejected() {
let mut faction = test_faction();
assert!(matches!(
faction.process(Command::DeadlineExpired),
ProcessResult::Rejected { .. }
));
let outcomes = match faction.process(Command::ParticipationObserved { peer_id: 1 }) {
ProcessResult::Accepted { outcomes, .. } => outcomes,
ProcessResult::Probed { .. } => unreachable!(),
ProcessResult::Rejected { .. } => panic!("expected accepted"),
};
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(
outcomes,
vec![Outcome::ParticipationAccepted { peer_id: 1 }]
);
assert_eq!(snap.pinging_peers().len(), 1);
}
#[test]
fn process_rejects_invalid_commands() {
let mut faction = test_faction();
assert!(matches!(
faction.process(Command::LocalParticipationCompleted),
ProcessResult::Rejected { .. }
));
assert!(matches!(
faction.process(Command::DeadlineExpired),
ProcessResult::Rejected { .. }
));
assert!(matches!(
faction.process(Command::LocalParticipationCompleted),
ProcessResult::Rejected { .. }
));
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(snap.peer_state(), PeerState::Fresh);
assert_eq!(snap.pinging_peers().len(), 0);
assert_eq!(snap.collecting_peers().len(), 0);
assert_eq!(snap.conclusion(), None);
assert!(!snap.is_concluded());
assert!(!snap.is_pinging_completed());
}
#[test]
fn process_participation_non_member_from_initial() {
let mut faction = test_faction();
let outcomes = match faction.process(Command::ParticipationObserved { peer_id: 99 }) {
ProcessResult::Accepted { outcomes, .. } => outcomes,
ProcessResult::Probed { .. } => unreachable!(),
ProcessResult::Rejected { .. } => panic!("expected accepted"),
};
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(outcomes, vec![Outcome::NonMemberIgnored { peer_id: 99 }]);
assert_eq!(snap.pinging_peers().len(), 0);
assert_eq!(snap.peer_state(), PeerState::Pinging);
}
#[test]
fn process_ready_non_member_from_initial() {
let mut faction = test_faction();
let outcomes = match faction.process(Command::ReadyObserved { peer_id: 99 }) {
ProcessResult::Accepted { outcomes, .. } => outcomes,
ProcessResult::Probed { .. } => unreachable!(),
ProcessResult::Rejected { .. } => panic!("expected accepted"),
};
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(outcomes, vec![Outcome::NonMemberIgnored { peer_id: 99 }]);
assert_eq!(snap.collecting_peers().len(), 0);
}
#[test]
fn new_returns_initial_state() {
let initial = Initial::new();
let config = Config::new(0, vec![0], QuorumPolicy::new(1));
let (outcomes, new_state) =
initial.step(Command::ParticipationObserved { peer_id: 0 }, &config);
assert_eq!(
outcomes,
vec![Outcome::ParticipationAccepted { peer_id: 0 }]
);
assert!(
new_state
.as_ref()
.cluster_view(&ClusterView::new(
PeerState::Pinging,
false,
vec![0],
vec![],
1
))
.pinging_peers()
.len()
> 0
);
}
#[test]
fn process_probe_returns_fresh_state() {
let mut faction = test_faction();
let snap = match faction.process(Command::Probe) {
ProcessResult::Probed { cluster_view, .. } => cluster_view,
_ => unreachable!(),
};
assert_eq!(snap.peer_state(), PeerState::Fresh);
assert_eq!(snap.conclusion(), None);
assert!(!snap.is_pinging_completed());
assert!(!snap.is_concluded());
assert_eq!(snap.pinging_peers().len(), 0);
assert_eq!(snap.collecting_peers().len(), 0);
assert_eq!(snap.required_count(), 4);
}
#[test]
fn cluster_view_inherits_correctly() {
let prev = ClusterView::new(PeerState::Collecting, true, vec![99], vec![99], 4);
let result = Initial.cluster_view(&prev);
assert_eq!(result.peer_state(), PeerState::Fresh);
assert_eq!(result.pinging_peers().len(), 0);
assert_eq!(result.collecting_peers().len(), 0);
assert_eq!(result.conclusion(), None);
assert!(!result.is_pinging_completed());
assert!(!result.is_concluded());
}