use crate::brain::tools::suggest_options::MAX_OPTIONS;
use crate::channels::whatsapp::WhatsAppState;
use crate::channels::whatsapp::interactive::{
MAX_BUTTONS, MAX_POLL_OPTIONS, SuggestionSurface, suggestion_card_fits, suggestion_poll_fits,
suggestion_surface,
};
use uuid::Uuid;
#[test]
fn the_card_keeps_what_it_can_take() {
for n in 1..=MAX_BUTTONS {
assert_eq!(suggestion_surface(n, true), SuggestionSurface::Card, "{n}");
}
}
#[test]
fn sets_past_the_button_cap_get_a_poll() {
for n in (MAX_BUTTONS + 1)..=MAX_OPTIONS {
assert_eq!(
suggestion_surface(n, true),
SuggestionSurface::Poll,
"{n} options is past the {MAX_BUTTONS}-button cap and must render as a poll"
);
}
}
#[test]
fn a_single_option_is_not_a_poll() {
assert!(!suggestion_poll_fits(1));
assert!(suggestion_card_fits(1), "one option still fits a card");
}
#[test]
fn an_oversized_set_falls_back_to_text() {
assert_eq!(
suggestion_surface(MAX_POLL_OPTIONS + 1, true),
SuggestionSurface::Text
);
}
#[test]
fn an_opted_out_owner_still_gets_the_numbered_list() {
for n in 1..=MAX_OPTIONS {
assert_eq!(suggestion_surface(n, false), SuggestionSurface::Text, "{n}");
}
}
#[tokio::test]
async fn a_vote_label_selects_the_suggestion() {
let state = WhatsAppState::new();
let sid = Uuid::new_v4();
state
.set_pending_followups(
sid,
vec![
"Run the tests".to_string(),
"Show me the diff".to_string(),
"Ship it".to_string(),
"Roll back".to_string(),
],
)
.await;
let picked = state.take_followup_by_label(sid, "Show me the diff").await;
assert_eq!(picked.as_deref(), Some("Show me the diff"));
assert!(
state.take_followup_by_label(sid, "Ship it").await.is_none(),
"a selection consumes the whole set"
);
}
#[tokio::test]
async fn vote_matching_survives_the_round_trip_through_the_poll_proto() {
let state = WhatsAppState::new();
let sid = Uuid::new_v4();
state
.set_pending_followups(sid, vec!["Deploy to prod".to_string()])
.await;
assert_eq!(
state
.take_followup_by_label(sid, " deploy to PROD ")
.await
.as_deref(),
Some("Deploy to prod"),
"matching is trimmed and case-insensitive"
);
}
#[tokio::test]
async fn a_label_that_is_not_on_offer_selects_nothing() {
let state = WhatsAppState::new();
let sid = Uuid::new_v4();
state
.set_pending_followups(sid, vec!["Run the tests".to_string()])
.await;
assert!(
state
.take_followup_by_label(sid, "Delete production")
.await
.is_none()
);
assert!(state.take_followup_by_label(sid, "").await.is_none());
assert_eq!(
state.take_followup_by_label(sid, "Run the tests").await,
Some("Run the tests".to_string())
);
}
#[test]
fn the_vote_carries_its_labels_rather_than_re_parsing_the_prose() {
const POLL: &str = include_str!("../channels/whatsapp/poll.rs");
assert!(
POLL.contains("pub(crate) struct Vote {") && POLL.contains("pub chosen: Vec<String>,"),
"the decoded vote must expose its labels (#1616)"
);
}