magi-code 0.77.1

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

#[test]
fn projects_core_transcript_roles_and_stable_ids() {
    let mut state = MissionControlState::default();
    state.apply_output_event(&OutputEvent::UserPrompt {
        text: "prompt".into(),
    });
    state.apply_output_event(&OutputEvent::ThinkingSummaryComplete {
        text: "summary".into(),
    });
    state.apply_output_event(&OutputEvent::AssistantComplete {
        text: "answer".into(),
    });
    state.record_error_transcript("boom");

    let cards = project_cards(&state);
    assert_eq!(
        cards.iter().map(|card| card.role).collect::<Vec<_>>(),
        vec![
            TranscriptCardRole::UserPrompt,
            TranscriptCardRole::Reasoning,
            TranscriptCardRole::Assistant,
            TranscriptCardRole::Error,
        ]
    );
    assert_eq!(cards[0].id.entry_index, 0);
    assert_eq!(cards[2].id.entry_index, 2);
    assert_eq!(cards[0].title, "");
    assert_eq!(cards[1].title, "Reasoning");
    assert_eq!(cards[2].title, "Agent");
}

#[test]
fn user_prompt_card_has_one_padding_row_above_its_text() {
    let mut state = MissionControlState::default();
    state.apply_output_event(&OutputEvent::UserPrompt {
        text: "prompt".into(),
    });

    let width = 48;
    let lines = visual_lines(&state, width);
    let rendered = lines
        .iter()
        .map(|line| line_text(&line.line))
        .collect::<Vec<_>>();

    assert!(rendered.len() >= 3);
    assert!(rendered[0].trim().is_empty());
    assert!(rendered[1].contains("User: prompt"));
    assert!(rendered[2].trim().is_empty());
}

#[test]
fn groups_adjacent_reasoning_rows_with_boundaries_and_identity_preserved() {
    let mut state = MissionControlState::default();
    state.transcript.extend([
        "thinking: A".to_string(),
        "thinking: B".to_string(),
        "assistant: answer".to_string(),
        "thinking: C".to_string(),
        "thinking: C".to_string(),
    ]);

    let cards = project_cards(&state);
    assert_eq!(
        cards.iter().map(|card| card.role).collect::<Vec<_>>(),
        vec![
            TranscriptCardRole::Reasoning,
            TranscriptCardRole::Assistant,
            TranscriptCardRole::Reasoning,
        ]
    );
    assert_eq!(cards[0].id.entry_index, 0);
    assert_eq!(cards[0].body, "A\n\nB");
    assert_eq!(cards[2].body, "C\n\nC");

    for width in [CARD_LAYOUT_MIN_WIDTH - 1, CARD_LAYOUT_MIN_WIDTH] {
        let rendered = visual_lines(&state, width)
            .iter()
            .map(|line| line_text(&line.line))
            .collect::<Vec<_>>()
            .join("\n");
        assert_eq!(rendered.matches("Reasoning").count(), 2, "width {width}");
        assert!(rendered.contains("A"), "{rendered}");
        assert!(rendered.contains("B"), "{rendered}");
    }
}

#[test]
fn grouped_tail_scrollbar_estimate_scales_by_consumed_source_rows() {
    let mut state = MissionControlState::default();
    for index in 0..400 {
        state
            .transcript
            .push_back(format!("session: ordinary entry {index}"));
    }
    for index in 0..100 {
        state
            .transcript
            .push_back(format!("thinking: reasoning member {index}"));
    }

    let width = 80;
    let exact_rows = visual_lines(&state, width)
        .iter()
        .map(|line| visual_line_rows(line, width))
        .sum::<usize>();
    state.invalidate_transcript_cache();
    let visible = visible_scrollback_visual_lines(&state, width, 6, 0);

    assert!(visible.scrollbar.content_length > 6);
    assert!(
        visible.scrollbar.content_length <= exact_rows.saturating_mul(2),
        "estimate {} should stay proportional to exact rows {exact_rows}",
        visible.scrollbar.content_length
    );
}

#[test]
fn scrollbar_measurement_becomes_exact_at_start_of_scrollback() {
    let mut state = MissionControlState::default();
    for index in 0..400 {
        state
            .transcript
            .push_back(format!("session: compact entry {index}"));
    }
    for index in 0..100 {
        state.transcript.push_back(format!(
            "assistant: wrapped entry {index} {}",
            "wrapped ".repeat(80)
        ));
    }

    let width = 80;
    let viewport = 6;
    let exact_rows = visual_lines(&state, width)
        .iter()
        .map(|line| visual_line_rows(line, width))
        .sum::<usize>();
    let exact_overflow = exact_rows.saturating_sub(viewport);
    state.invalidate_transcript_cache();

    let tail = visible_scrollback_visual_lines(&state, width, viewport as u16, 0);
    assert!(
        tail.scrollbar.content_length > exact_overflow.saturating_add(1),
        "tail-heavy wrapping should begin with an overestimate"
    );

    let middle =
        visible_scrollback_visual_lines(&state, width, viewport as u16, exact_overflow / 2);
    assert_ne!(
        middle.scrollbar.content_length,
        tail.scrollbar.content_length
    );
    assert!(middle.scrollbar.position > 0);

    let top = visible_scrollback_visual_lines(&state, width, viewport as u16, exact_overflow);
    assert_eq!(top.scrollbar.position, 0);
    assert_eq!(
        top.scrollbar.content_length,
        exact_overflow.saturating_add(1)
    );
    let returned_to_tail = visible_scrollback_visual_lines(&state, width, viewport as u16, 0);
    assert_eq!(returned_to_tail.scrollbar.position, exact_overflow);
    assert_eq!(
        returned_to_tail.scrollbar.content_length,
        exact_overflow.saturating_add(1)
    );
}

#[test]
fn automatic_prompt_uses_legacy_diagnostic_projection_and_full_line_copy() {
    let mut state = MissionControlState::default();
    state.apply_output_event(&OutputEvent::AutomaticUserPrompt {
        text: "continue after compaction".to_string(),
    });

    let card = &project_cards(&state)[0];
    assert_eq!(card.role, TranscriptCardRole::Diagnostic);
    assert_eq!(card.title, "Legacy diagnostic");
    assert_eq!(card.body, "you [automatic]: continue after compaction");

    let copied = visual_text(&state);
    assert!(
        copied.contains("you [automatic]: continue after compaction"),
        "{copied}"
    );
    assert!(
        !copied.contains("User: continue after compaction"),
        "{copied}"
    );
}