use super::*;
#[test]
fn subagent_feed_background_stays_inside_shared_border() {
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
let mut state = MissionControlState {
subagent_card_rows: 3,
..Default::default()
};
append_subagent_card_batch(&mut state, "feed-gutters", &[ActivityStatus::Running]);
add_subagent_card_tool_activity(
&mut state,
&ActivityId::new("feed-gutters/g1"),
"feed-gutters/bash",
"bash",
"bash git diff --stat with a long description",
);
let theme = MissionControlTheme::default();
for width in [24, 80] {
let lines = visual_lines(&state, width);
let feed_start = lines
.iter()
.position(|line| line.copy_text.contains("bash •"))
.expect("populated feed row");
for (index, line) in lines[feed_start..feed_start + 3].iter().enumerate() {
assert_eq!(line.line.width(), usize::from(width));
assert_eq!(
line.copy_text.trim_matches([' ', '│']).is_empty(),
index > 0
);
let area = Rect::new(0, 0, width, 1);
let mut buffer = Buffer::empty(area);
line.line.clone().render(area, &mut buffer);
for x in 0..width {
let expected = if x < 4 || x >= width - 2 {
theme.surface_panel()
} else {
theme.surface_background()
};
assert_eq!(
buffer[(x, 0)].bg,
expected,
"width={width} row={index} x={x}"
);
}
assert_eq!(buffer[(3, 0)].symbol(), "│");
assert_eq!(buffer[(width - 2, 0)].symbol(), "│");
}
assert!(
lines[feed_start - 1]
.copy_text
.trim_start()
.starts_with('╭')
);
assert!(
lines[feed_start + 3]
.copy_text
.trim_start()
.starts_with('╰')
);
for line in [&lines[feed_start - 1], &lines[feed_start + 3]] {
assert!(
line.line
.spans
.iter()
.all(|span| span.style.bg == Some(theme.surface_panel()))
);
}
}
}
#[test]
fn subagent_card_rows_deduplicate_tool_names_and_format_reasoning_counts() {
let mut state = MissionControlState::default();
append_subagent_card_batch(&mut state, "row-format", &[ActivityStatus::Running]);
let task = ActivityId::new("row-format/g1");
add_subagent_card_tool_activity(
&mut state,
&task,
"row-format/bash",
"bash",
"bash git diff --stat",
);
state.apply_activity_event(ActivityEvent::Started {
id: ActivityId::new("row-format/reasoning-one"),
parent_id: Some(task.clone()),
kind: ActivityKind::Assistant,
status: ActivityStatus::Success,
metadata: ActivityMetadata::reasoning(&["checked inputs".to_string()]),
});
state.apply_activity_event(ActivityEvent::Started {
id: ActivityId::new("row-format/reasoning-two"),
parent_id: Some(task),
kind: ActivityKind::Assistant,
status: ActivityStatus::Success,
metadata: ActivityMetadata::reasoning(&[
"checked inputs".to_string(),
"planned fix".to_string(),
]),
});
let tail = &project_cards(&state)[0].children[0].live_tail;
assert!(
tail.iter().any(|line| line == "bash • git diff --stat • ✓"),
"{tail:?}"
);
assert!(
!tail.iter().any(|line| line.contains("bash • bash")),
"{tail:?}"
);
assert!(tail.iter().any(|line| line == "reasoning • checked inputs"));
assert!(
tail.iter()
.any(|line| line == "reasoning summaries ×2 • planned fix")
);
assert!(
!tail
.iter()
.any(|line| line.contains("×1") || line.contains("1. checked"))
);
}
#[test]
fn subagent_card_live_tail_uses_five_latest_lines_from_one_streaming_agent() {
let mut state = MissionControlState {
subagent_card_rows: 5,
..Default::default()
};
append_subagent_card_batch(&mut state, "stream-tail", &[ActivityStatus::Running]);
let task = ActivityId::new("stream-tail/g1");
let assistant = ActivityId::new("stream-tail/g1/assistant");
state.apply_activity_event(ActivityEvent::Started {
id: assistant.clone(),
parent_id: Some(task),
kind: ActivityKind::Assistant,
status: ActivityStatus::Running,
metadata: ActivityMetadata::new("agent"),
});
state.apply_activity_event(ActivityEvent::Delta {
id: assistant.clone(),
preview: "one\ntwo\nthree\nfour\nfive\n".to_string(),
});
assert_eq!(
project_cards(&state)[0].children[0].live_tail,
["one", "two", "three", "four", "five"].map(|line| format!("agent • {line}"))
);
state.apply_activity_event(ActivityEvent::Delta {
id: assistant.clone(),
preview: "six\n".to_string(),
});
let tail = &project_cards(&state)[0].children[0].live_tail;
assert_eq!(
tail,
&[
"agent • two",
"agent • three",
"agent • four",
"agent • five",
"agent • six",
]
);
state.apply_activity_event(ActivityEvent::Delta {
id: assistant,
preview: "six\n".to_string(),
});
let tail = &project_cards(&state)[0].children[0].live_tail;
assert_eq!(tail[3], "agent • six");
assert_eq!(tail[4], "agent • six");
}
#[test]
fn subagent_card_live_tail_uses_plain_category_labels_without_duplicates() {
let mut state = MissionControlState::default();
append_subagent_card_batch(&mut state, "plain-categories", &[ActivityStatus::Running]);
let task = ActivityId::new("plain-categories/g1");
for (id, kind, label) in [
(
"diagnostic",
ActivityKind::Diagnostic,
"diagnostic • checking state",
),
("hook", ActivityKind::Hook, "hook"),
(
"provider",
ActivityKind::ProviderContextInjection,
"provider context injection • loaded instructions",
),
(
"batch",
ActivityKind::SubagentBatch,
"subagent batch • 2 tasks",
),
(
"compact",
ActivityKind::Compaction,
"compaction • reclaim context",
),
] {
state.apply_activity_event(ActivityEvent::Started {
id: ActivityId::new(format!("plain-categories/g1/{id}")),
parent_id: Some(task.clone()),
kind,
status: ActivityStatus::Success,
metadata: ActivityMetadata::new(label),
});
}
let tail = &project_cards(&state)[0].children[0].live_tail;
assert_eq!(
tail,
&[
"diagnostic • checking state",
"hook",
"provider context injection • loaded instructions",
"subagent batch • 2 tasks",
"compaction • reclaim context",
]
);
}
#[test]
fn reasoning_tail_uses_ingested_summary_and_refreshes_on_final_event() {
let mut state = MissionControlState::default();
append_subagent_card_batch(&mut state, "typed-reasoning", &[ActivityStatus::Running]);
let id = ActivityId::new("typed-reasoning/g1/reasoning");
state.apply_activity_event(ActivityEvent::Started {
id: id.clone(),
parent_id: Some(ActivityId::new("typed-reasoning/g1")),
kind: ActivityKind::Assistant,
status: ActivityStatus::Running,
metadata: ActivityMetadata::reasoning(&["checked inputs".into(), "planned fix".into()]),
});
let node = state.nodes.get_mut(&id).unwrap();
node.metadata = ActivityMetadata::new("agent");
assert_eq!(
project_cards(&state)[0].children[0].live_tail,
["reasoning summaries ×2 • planned fix"]
);
state.apply_activity_event(ActivityEvent::FinalPreview {
id: id.clone(),
preview: String::new(),
metadata: Some(ActivityMetadata::reasoning(&[
"checked inputs".into(),
"planned fix".into(),
"verified fix".into(),
])),
status: Some(ActivityStatus::Success),
});
assert_eq!(
project_cards(&state)[0].children[0].live_tail,
["reasoning summaries ×3 • verified fix"]
);
state.apply_activity_event(ActivityEvent::FinalPreview {
id,
preview: "finished".into(),
metadata: Some(ActivityMetadata::new("agent")),
status: Some(ActivityStatus::Success),
});
assert_eq!(
project_cards(&state)[0].children[0].live_tail,
["agent • finished"]
);
}