use crate::brain::agent::QueuedUserMessage;
use crate::channels::telegram::TelegramState;
use std::sync::Arc;
use uuid::Uuid;
#[test]
fn reactions_drain_fifo_per_session() {
let state = Arc::new(TelegramState::new());
let s1 = Uuid::new_v4();
let s2 = Uuid::new_v4();
state.enqueue_reaction(s1, QueuedUserMessage::plain("first".to_string()));
state.enqueue_reaction(s1, QueuedUserMessage::plain("second".to_string()));
state.enqueue_reaction(s2, QueuedUserMessage::plain("other".to_string()));
let drained = |v: Option<QueuedUserMessage>| v.map(|m| m.context_text);
assert_eq!(drained(state.drain_reaction(s1)).as_deref(), Some("first"));
assert_eq!(drained(state.drain_reaction(s1)).as_deref(), Some("second"));
assert!(state.drain_reaction(s1).is_none());
assert_eq!(drained(state.drain_reaction(s2)).as_deref(), Some("other"));
assert!(state.drain_reaction(s2).is_none());
}
#[test]
fn active_turn_guard_marks_and_clears_on_drop() {
let state = Arc::new(TelegramState::new());
let sid = Uuid::new_v4();
assert!(!state.is_turn_active(sid));
{
let _guard = state.mark_turn_active(sid);
assert!(state.is_turn_active(sid));
}
assert!(!state.is_turn_active(sid));
}
#[test]
fn active_turns_are_independent_per_session() {
let state = Arc::new(TelegramState::new());
let a = Uuid::new_v4();
let b = Uuid::new_v4();
let guard_a = state.mark_turn_active(a);
assert!(state.is_turn_active(a));
assert!(!state.is_turn_active(b));
drop(guard_a);
assert!(!state.is_turn_active(a));
}
#[tokio::test]
async fn queue_callback_drains_the_same_state() {
let state = Arc::new(TelegramState::new());
let sid = Uuid::new_v4();
state.enqueue_reaction(sid, QueuedUserMessage::plain("queued".to_string()));
let cb = state.reaction_queue_callback();
assert_eq!(
cb(sid).await.map(|m| m.context_text).as_deref(),
Some("queued")
);
assert!(cb(sid).await.is_none());
}
#[test]
fn try_begin_turn_is_atomic_check_and_set() {
let state = Arc::new(TelegramState::new());
let s = Uuid::new_v4();
let guard = state.try_begin_turn(s).expect("first claim succeeds");
assert!(state.is_turn_active(s));
assert!(
state.try_begin_turn(s).is_none(),
"concurrent claim must be refused while a turn is active"
);
drop(guard);
assert!(!state.is_turn_active(s));
assert!(state.try_begin_turn(s).is_some(), "reclaim after turn ends");
}
#[test]
fn try_begin_turn_is_per_session() {
let state = Arc::new(TelegramState::new());
let a = Uuid::new_v4();
let b = Uuid::new_v4();
let _ga = state.try_begin_turn(a).expect("a claims");
assert!(state.try_begin_turn(b).is_some(), "b is independent of a");
assert!(state.try_begin_turn(a).is_none(), "a still blocked");
}
#[test]
fn reaction_claim_blocks_concurrent_reaction_and_enqueues() {
let state = Arc::new(TelegramState::new());
let sid = Uuid::new_v4();
let reaction_turn_guard = state
.try_begin_turn(sid)
.expect("idle session: reaction claims the turn");
assert!(state.is_turn_active(sid), "reaction turn is now visible");
assert!(
state.try_begin_turn(sid).is_none(),
"concurrent reaction must not fork a second turn"
);
state.enqueue_reaction(sid, QueuedUserMessage::plain("mid-turn react".to_string()));
drop(reaction_turn_guard);
assert!(!state.is_turn_active(sid));
assert_eq!(
state.drain_reaction(sid).map(|m| m.context_text).as_deref(),
Some("mid-turn react"),
);
}