use std::sync::Arc;
use crate::brain::agent::service::AgentService;
use crate::brain::agent::service::clear::{
CLEAR_HINT, CLEAR_MARKER_PREFIX, ClearReceipt, clear_marker,
};
use crate::channels::commands::ChannelCommand;
use crate::db::Database;
use crate::services::{MessageService, ServiceContext, SessionService};
use crate::tests::agent_service_mocks::MockProvider;
const LOADER_PREFIX: &str = "[CONTEXT COMPACTION";
#[test]
fn the_marker_is_what_the_loader_and_the_tui_key_on() {
assert!(CLEAR_MARKER_PREFIX.starts_with(LOADER_PREFIX));
let marker = clear_marker(Some("Router audit"));
assert!(marker.starts_with(CLEAR_MARKER_PREFIX));
const TUI: &str = include_str!("../tui/app/messaging.rs");
assert!(TUI.contains("if msg.content.starts_with(\"[CONTEXT COMPACTION\")"));
}
#[test]
fn the_body_names_the_session_and_the_search_tool() {
let marker = clear_marker(Some(" Router audit "));
let body = marker.split_once("\n\n").expect("banner then body").1;
assert!(body.contains("session_search"));
assert!(body.contains("'Router audit'"));
assert!(body.contains("'tail'") && body.contains("'search'"));
assert!(
!body.contains("[CONTEXT COMPACTION"),
"the banner is not part of the body"
);
}
#[test]
fn an_untitled_session_still_gets_a_usable_nudge() {
let marker = clear_marker(None);
assert!(marker.contains("'untitled'"));
assert!(marker.contains("session_search"));
assert_eq!(clear_marker(Some(" ")), clear_marker(None));
}
#[test]
fn the_banner_is_stripped_and_the_body_kept_when_loaded_as_context() {
let mut content = clear_marker(Some("Router audit"));
AgentService::strip_compaction_banner(&mut content);
assert!(!content.starts_with("["));
assert!(content.starts_with("The user cleared this session's context"));
assert!(content.contains("session_search"));
}
#[test]
fn the_receipt_line_reports_title_and_an_aborted_summariser() {
let plain = ClearReceipt {
session_title: Some("Router audit".to_string()),
aborted_background_compaction: false,
}
.user_line();
assert!(plain.contains("(title 'Router audit')"));
assert!(plain.contains("No summariser call was made."));
assert!(!plain.contains("cancelled"));
let aborted = ClearReceipt {
session_title: None,
aborted_background_compaction: true,
}
.user_line();
assert!(!aborted.contains("title"));
assert!(aborted.contains("background compaction that was running has been cancelled"));
}
#[tokio::test]
async fn clearing_appends_the_marker_and_the_loader_cuts_there() {
let db = Database::connect_in_memory().await.unwrap();
db.run_migrations().await.unwrap();
let context = ServiceContext::new(db.pool().clone());
let agent = AgentService::new_for_test(Arc::new(MockProvider), context.clone()).await;
let sessions = SessionService::new(context.clone());
let messages = MessageService::new(context);
let session = sessions
.create_session(Some("Router audit".to_string()))
.await
.unwrap();
messages
.create_message(session.id, "user".to_string(), "first question".to_string())
.await
.unwrap();
messages
.create_message(
session.id,
"assistant".to_string(),
"first answer".to_string(),
)
.await
.unwrap();
let receipt = agent
.clear_context(session.id)
.await
.expect("clear succeeds");
assert_eq!(receipt.session_title.as_deref(), Some("Router audit"));
assert!(!receipt.aborted_background_compaction);
let rows = messages
.list_messages_for_session(session.id)
.await
.unwrap();
assert_eq!(rows.len(), 3, "history is kept, one marker row appended");
assert_eq!(rows[0].content, "first question");
let marker = &rows[2];
assert_eq!(marker.role, "user");
assert!(marker.content.starts_with(CLEAR_MARKER_PREFIX));
assert!(marker.content.contains("'Router audit'"));
let loaded = AgentService::messages_from_last_compaction(rows);
assert_eq!(loaded.len(), 1, "the next turn loads only the marker");
assert!(loaded[0].content.starts_with(CLEAR_MARKER_PREFIX));
agent.clear_context(session.id).await.expect("second clear");
let rows = messages
.list_messages_for_session(session.id)
.await
.unwrap();
assert_eq!(rows.len(), 4);
}
#[test]
fn clear_aborts_a_background_summariser_before_writing_the_marker() {
const SRC: &str = include_str!("../brain/agent/service/clear.rs");
let abort = SRC
.find("take_pending_compaction(session_id)")
.expect("pending taken");
let write = SRC.find("create_message(").expect("marker written");
assert!(
abort < write,
"the in-flight summary is aborted before the cut"
);
assert!(SRC[abort..write].contains("pending.abort()"));
}
#[test]
fn channels_parse_clear_owner_only() {
const SRC: &str = include_str!("../channels/commands.rs");
let arm = SRC.find("\"/clear\" => {").expect("/clear parsed");
assert!(SRC[arm..arm + 220].contains("Owner-only command"));
assert!(SRC[arm..arm + 220].contains("ChannelCommand::ClearContext"));
assert!(
SRC.contains("(\n \"/clear\","),
"listed in /help"
);
let _ = ChannelCommand::ClearContext;
}
#[test]
fn every_surface_handles_clear_without_an_agent_turn() {
const TELEGRAM: &str = include_str!("../channels/telegram/commands_tg.rs");
const DISCORD: &str = include_str!("../channels/discord/handler.rs");
const SLACK: &str = include_str!("../channels/slack/handler.rs");
const WHATSAPP: &str = include_str!("../channels/whatsapp/handler.rs");
const TUI: &str = include_str!("../tui/app/messaging.rs");
const MENU: &str = include_str!("../channels/telegram/agent.rs");
for (name, src) in [
("telegram", TELEGRAM),
("discord", DISCORD),
("slack", SLACK),
("whatsapp", WHATSAPP),
] {
let arm = src
.find("ChannelCommand::ClearContext => {")
.unwrap_or_else(|| panic!("{name} handles ClearContext"));
assert!(
src[arm..arm + 400].contains("clear_context(session_id)"),
"{name} clears through the agent service"
);
}
let tui = TUI.find("\"/clear\" => {").expect("TUI command");
let window = &TUI[tui..tui + 1200];
assert!(
window.contains("is_processing"),
"refused while a turn runs"
);
assert!(window.contains("clear_context(session_id)"));
assert!(
window.contains("base_context_tokens()"),
"footer reset to baseline"
);
assert!(
MENU.contains("BotCommand::new(\"clear\""),
"in the Telegram menu"
);
}
#[test]
fn a_failed_manual_compaction_points_at_clear() {
const TOOL_LOOP: &str = include_str!("../brain/agent/service/tool_loop.rs");
let failure = TOOL_LOOP
.find("Manual compaction failed")
.expect("manual failure branch");
assert!(TOOL_LOOP[failure..failure + 900].contains("CLEAR_HINT"));
assert!(CLEAR_HINT.contains("/clear"));
const NOTICE: &str = include_str!("../brain/agent/service/compaction_notice.rs");
assert!(NOTICE.contains("self.verbose && matches!(step, CompactionStep::Failed { .. })"));
}