use uuid::Uuid;
use crate::cli::resume_delivery::{
ResumePresence, ResumeSendOutcome, chunks_for, resume_notice_text,
};
use crate::tui::events::TuiEvent;
#[test]
fn restart_resume_receipts_painted_and_cleared_for_remote_rows() {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<TuiEvent>();
let id = Uuid::new_v4();
let guard = ResumePresence::start(&tx, id, "discord").expect("remote rows carry presence");
let started = rx.try_recv().expect("started event on creation");
assert!(
matches!(started, TuiEvent::ChannelProcessingStarted(g) if g == id),
"expected ChannelProcessingStarted({id}), got {started:?}"
);
drop(guard);
let finished = rx.try_recv().expect("finished event on Drop");
assert!(
matches!(finished, TuiEvent::ChannelProcessingFinished(g) if g == id),
"expected ChannelProcessingFinished({id}), got {finished:?}"
);
assert!(rx.try_recv().is_err(), "exactly one started/finished pair");
}
#[test]
fn restart_resume_receipts_absent_for_tui_rows() {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<TuiEvent>();
let guard = ResumePresence::start(&tx, Uuid::new_v4(), "tui");
assert!(guard.is_none(), "tui rows must not double-presence");
assert!(rx.try_recv().is_err(), "no events for tui rows");
}
#[test]
fn restart_resume_receipts_notice_names_the_session() {
let id = Uuid::new_v4();
let text = resume_notice_text(id);
assert!(
text.starts_with("🔁"),
"notice must be recognizable: {text}"
);
let short = id.simple().to_string()[..8].to_string();
assert!(
text.contains(&short),
"notice must carry the session id: {text}"
);
}
#[test]
fn restart_resume_receipts_only_reached_surfaces_count() {
assert!(ResumeSendOutcome::Sent.counts_as_delivered());
assert!(ResumeSendOutcome::SurfacedTui.counts_as_delivered());
for gone in [
ResumeSendOutcome::SendFailed,
ResumeSendOutcome::TransportGone,
ResumeSendOutcome::NoAddress,
ResumeSendOutcome::BadAddress,
ResumeSendOutcome::Unsupported,
] {
assert!(!gone.counts_as_delivered(), "{gone:?} reached no surface");
}
}
#[cfg(feature = "discord")]
#[test]
fn restart_resume_receipts_long_discord_answers_split() {
let text = "a".repeat(5000);
let chunks = chunks_for("discord", &text);
assert!(chunks.len() > 1, "5000 chars must not ride one message");
for (i, chunk) in chunks.iter().enumerate() {
assert!(
chunk.len() <= 2000,
"chunk {i} is {} bytes, over the cap",
chunk.len()
);
}
assert_eq!(chunks.concat(), text, "the seams must not lose a byte");
}
#[cfg(feature = "discord")]
#[test]
fn restart_resume_receipts_short_answers_stay_whole() {
let chunks = chunks_for("discord", "hello");
assert_eq!(chunks, vec!["hello".to_string()]);
}
#[test]
fn restart_resume_receipts_other_channels_keep_single_path() {
let text = "x".repeat(5000);
assert_eq!(chunks_for("whatsapp", &text).len(), 1);
assert_eq!(chunks_for("slack", &text).len(), 1);
}