use jiff::Timestamp;
use serde::{Deserialize, Serialize};
use crate::agent::AgentName;
use crate::flight::ItineraryId;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Stall {
pub itinerary: ItineraryId,
pub agent: AgentName,
pub missing: Vec<AgentName>,
pub stranded: usize,
pub at: Timestamp,
}
impl Stall {
#[must_use]
pub fn new(
itinerary: ItineraryId,
agent: AgentName,
missing: Vec<AgentName>,
stranded: usize,
at: Timestamp,
) -> Self {
Self {
itinerary,
agent,
missing,
stranded,
at,
}
}
#[must_use]
pub fn summary(&self) -> String {
let missing = self
.missing
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ");
format!(
"`{}` never woke: nothing live could still deliver {missing}",
self.agent
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn stall(missing: &[&str]) -> Stall {
Stall::new(
ItineraryId::generate(),
AgentName::new("publisher"),
missing.iter().map(|n| AgentName::new(*n)).collect(),
1,
Timestamp::now(),
)
}
#[test]
fn a_stall_names_the_agent_that_never_woke() {
let summary = stall(&["reviewer"]).summary();
assert!(summary.contains("publisher"), "{summary}");
assert!(summary.contains("never woke"), "{summary}");
assert!(summary.contains("reviewer"), "{summary}");
}
#[test]
fn every_missing_upstream_is_named() {
let summary = stall(&["reviewer", "tester"]).summary();
assert!(summary.contains("reviewer"), "{summary}");
assert!(summary.contains("tester"), "{summary}");
}
#[test]
fn a_stall_survives_a_round_trip_through_json() {
let original = stall(&["reviewer"]);
let text = serde_json::to_string(&original).expect("serialises");
let read: Stall = serde_json::from_str(&text).expect("deserialises");
assert_eq!(read, original);
}
}