magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

#[test]
fn card_headers_have_one_padding_consistent_status_marks_and_bullet_separators() {
    let theme = crate::tui::theme::MissionControlTheme::default();
    let states = [
        (
            bash_card_state(
                "header-running",
                "bash",
                ActivityStatus::Running,
                serde_json::json!({"command":"cargo check"}),
                serde_json::json!({}),
                "",
            ),
            "● Bash",
            crate::tui::theme::MissionControlTheme::default()
                .activity_status_color(ActivityStatus::Running),
            Modifier::BOLD,
        ),
        (
            bash_card_state(
                "header-success",
                "bash",
                ActivityStatus::Success,
                serde_json::json!({"command":"cargo check"}),
                serde_json::json!({"exit_code":0}),
                "ok",
            ),
            "✓ Bash • exit 0",
            theme.activity_status_color(ActivityStatus::Success),
            Modifier::empty(),
        ),
        (
            bash_card_state(
                "header-failed",
                "bash",
                ActivityStatus::Failed,
                serde_json::json!({"command":"cargo check"}),
                serde_json::json!({"exit_code":1}),
                "failed",
            ),
            "✗ Bash • exit 1",
            theme.activity_status_color(ActivityStatus::Failed),
            Modifier::BOLD,
        ),
        (
            bash_card_state(
                "header-canceled",
                "bash",
                ActivityStatus::Canceled,
                serde_json::json!({"command":"cargo check"}),
                serde_json::json!({}),
                "canceled",
            ),
            "⊘ Bash • canceled",
            crate::tui::theme::MissionControlTheme::default()
                .activity_status_color(ActivityStatus::Canceled),
            Modifier::DIM,
        ),
    ];

    for (state, expected, status_color, status_modifier) in states {
        let header = &visual_lines(&state, 80)[1];
        let rendered = line_text(&header.line);
        assert!(
            rendered.starts_with(format!(" {expected}").as_str()),
            "{rendered:?}"
        );
        assert!(!header.copy_text.contains(" · "), "{}", header.copy_text);
        assert_eq!(header.visual_copy_column_offset, 1);
        assert_eq!(UnicodeWidthStr::width(rendered.as_str()), 80);
        assert_eq!(header.line.spans[1].style.fg, Some(status_color));
        assert_eq!(header.line.spans[1].style.add_modifier, status_modifier);
        let title = header
            .line
            .spans
            .iter()
            .find(|span| span.content.contains("Bash"))
            .expect("Bash title span");
        assert_eq!(title.style.fg, Some(theme.text_accent()));
        assert!(title.style.add_modifier.contains(Modifier::BOLD));
        assert!(
            header
                .line
                .spans
                .iter()
                .all(|span| span.style.bg == Some(theme.surface_panel_alt()))
        );
    }

    let generic = linked_tool_state(
        "header-generic",
        "dynamic_tool",
        "dynamic_tool",
        ActivityStatus::Success,
        Vec::new(),
        "ok",
    );
    let generic_header = &visual_lines(&generic, 80)[1];
    assert!(line_text(&generic_header.line).starts_with(" ✓ Tool • dynamic_tool"));
    let generic_title = generic_header
        .line
        .spans
        .iter()
        .find(|span| span.content.contains("Tool"))
        .expect("generic tool title span");
    assert_eq!(generic_title.style.fg, Some(theme.text_accent()));
    assert!(generic_title.style.add_modifier.contains(Modifier::BOLD));

    let mut subagent_card = MissionControlState::default();
    append_subagent_card_batch(
        &mut subagent_card,
        "header-subagent_card",
        &[ActivityStatus::Success],
    );
    assert!(
        line_text(&visual_lines(&subagent_card, 80)[1].line)
            .starts_with(" Subagents • 1/1 settled")
    );
}

#[test]
fn card_headers_preserve_status_contract_at_tiny_widths() {
    let state = bash_card_state(
        "tiny-running-header",
        "bash",
        ActivityStatus::Running,
        serde_json::json!({"command":"cargo check"}),
        serde_json::json!({}),
        "",
    );
    for (width, expected) in [(1, " "), (2, "")] {
        let header = &visual_lines(&state, width)[1];
        assert_eq!(line_text(&header.line), expected);
        assert_eq!(
            UnicodeWidthStr::width(line_text(&header.line).as_str()),
            width as usize
        );
        assert_eq!(header.copy_text, "● Bash");
        assert_eq!(header.visual_copy_column_offset, 1);
    }
}

#[test]
fn legacy_generic_tool_marks_normalize_to_canonical_headers() {
    for (legacy, expected_status, expected_title, expected_header) in [
        (
            "⟳ alpha • running",
            TranscriptCardStatus::Running,
            "alpha",
            "● Tool • alpha",
        ),
        (
            "○ beta • queued",
            TranscriptCardStatus::Running,
            "beta",
            "● Tool • beta",
        ),
        (
            "⊘ gamma • canceled",
            TranscriptCardStatus::Canceled,
            "gamma • canceled",
            "⊘ Tool • gamma • canceled",
        ),
    ] {
        let mut state = MissionControlState::default();
        state.transcript.push_back(format!("tool: {legacy}"));
        let card = &project_cards(&state)[0];
        assert_eq!(card.status, expected_status);
        assert_eq!(card.title, expected_title);
        assert!(
            line_text(&visual_lines(&state, 80)[1].line)
                .starts_with(format!(" {expected_header}").as_str()),
            "{legacy}"
        );
    }
}

#[test]
fn error_and_markless_semantic_headers_keep_their_exceptions() {
    let mut error = MissionControlState::default();
    error
        .transcript
        .push_back("error: failed safely".to_string());
    let error_header = &visual_lines(&error, 80)[1];
    assert!(line_text(&error_header.line).starts_with(" ✗ Error"));
    assert!(
        error_header
            .line
            .spans
            .iter()
            .all(|span| span.style.bg == Some(MissionControlTheme::default().surface_error()))
    );

    for entry in ["assistant: answer", "thinking: summary"] {
        let mut state = MissionControlState::default();
        state.transcript.push_back(entry.to_string());
        let header = &visual_lines(&state, 80)[1];
        assert!(!header.copy_text.starts_with(['', '', '', '']));
        assert!(matches!(header.copy_text.as_str(), "Agent" | "Reasoning"));
    }
}

#[test]
fn canceled_read_and_generic_tool_use_one_canonical_suffix() {
    let read = read_card_state(
        "canceled-read-header",
        ActivityStatus::Canceled,
        serde_json::json!({"paths":["src/lib.rs", "src/main.rs"]}),
        serde_json::json!({"files":2}),
        "prompt canceled",
    );
    let read_card = &project_cards(&read)[0];
    assert_eq!(read_card.title, "Read • 2 resources • canceled");
    assert_eq!(
        read_card.body,
        "src/lib.rs\nsrc/main.rs\nCanceled before completion"
    );
    assert!(
        line_text(&visual_lines(&read, 80)[1].line).starts_with(" ⊘ Read • 2 resources • canceled")
    );

    let tool = linked_tool_state(
        "canceled-generic-header",
        "dynamic_tool",
        "dynamic_tool",
        ActivityStatus::Canceled,
        Vec::new(),
        "canceled",
    );
    let tool_header = line_text(&visual_lines(&tool, 80)[1].line);
    assert!(tool_header.starts_with(" ⊘ Tool • dynamic_tool • canceled"));
    assert_eq!(tool_header.matches("canceled").count(), 1);
}

#[test]
fn non_user_headers_have_three_styled_rows_without_copy_or_hit_padding() {
    let mut states = Vec::new();
    for entry in [
        "assistant: answer",
        "thinking: summary",
        "error: failed safely",
        "tool: ✓ dynamic_tool • done",
        "system: notice",
    ] {
        let mut state = MissionControlState::default();
        state.transcript.push_back(entry.to_string());
        states.push(state);
    }
    states.push(bash_card_state(
        "three-row-bash",
        "bash",
        ActivityStatus::Running,
        serde_json::json!({"command":"cargo check"}),
        serde_json::json!({}),
        "",
    ));
    let mut subagents = MissionControlState::default();
    append_subagent_card_batch(
        &mut subagents,
        "three-row-subagents",
        &[ActivityStatus::Success],
    );
    states.push(subagents);

    for state in states {
        for width in [1, 2, 12, 80] {
            let lines = visual_lines(&state, width);
            let header = &lines[1];
            assert!(header.copyable);
            assert!(!header.copy_text.is_empty());
            let background = header.line.spans[0].style.bg;
            for row in [0, 2] {
                let padding = &lines[row];
                assert_eq!(line_text(&padding.line), " ".repeat(usize::from(width)));
                assert!(
                    padding
                        .line
                        .spans
                        .iter()
                        .all(|span| span.style.bg == background)
                );
                assert!(!padding.copyable);
                assert!(padding.copy_text.is_empty());
                assert!(padding.copy_byte_range.is_none());
                assert!(padding.hit_regions.is_empty());
                assert!(padding.status_indicators.is_empty());
                assert_eq!(visual_line_rows(padding, width), 1);
            }
        }
    }
}