magi-code 0.77.1

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

#[test]
fn grep_card_projects_running_success_zero_and_alias() {
    let running = grep_card_state(
        "grep-running",
        "ffgrep",
        ActivityStatus::Running,
        serde_json::json!({"patterns":["ToolActivityDetail"],"path":"src/"}),
        serde_json::json!({}),
        "",
    );
    let card = &project_cards(&running)[0];
    assert_eq!(card.role, TranscriptCardRole::Grep);
    assert_eq!(card.title, "Grep");
    assert_eq!(card.body, "\"ToolActivityDetail\"\nin src/");

    let success = grep_card_state(
        "grep-success",
        "grep",
        ActivityStatus::Success,
        serde_json::json!({"patterns":["ToolActivityDetail"],"path":"src/"}),
        serde_json::json!({"matches_returned":2,"truncated":false}),
        "src/a.rs:1:ToolActivityDetail\nsrc/b.rs:2:ToolActivityDetail",
    );
    let card = &project_cards(&success)[0];
    assert_eq!(card.title, "Grep • 2 matches");
    assert_eq!(
        card.body,
        "\"ToolActivityDetail\"\nin src/\nsrc/a.rs:1:ToolActivityDetail\nsrc/b.rs:2:ToolActivityDetail"
    );
    let visual = success.transcript_visual_text();
    assert!(!visual.contains("Tool:"), "{visual}");
    assert!(!visual.contains("STATUS:"), "{visual}");
    assert!(!visual.contains("PATTERN:"), "{visual}");
    assert!(!visual.contains("OUTPUT:"), "{visual}");

    let zero = grep_card_state(
        "grep-zero",
        "grep",
        ActivityStatus::Success,
        serde_json::json!({"patterns":["missing"]}),
        serde_json::json!({"matches_returned":0,"truncated":false}),
        "",
    );
    let card = &project_cards(&zero)[0];
    assert_eq!(card.title, "Grep • 0 matches");
    assert_eq!(card.body, "\"missing\"\nin .");
}

#[test]
fn grep_card_displays_multiple_canonical_patterns() {
    let state = grep_card_state(
        "grep-patterns",
        "grep",
        ActivityStatus::Running,
        serde_json::json!({"patterns":["alpha", "beta"],"path":"src"}),
        serde_json::json!({}),
        "",
    );
    let card = &project_cards(&state)[0];
    assert_eq!(card.body, "[\"alpha\", \"beta\"]\nin src");
}

#[test]
fn grep_card_projects_failures_timeout_and_truncation_precisely() {
    let failed = grep_card_state(
        "grep-failed",
        "grep",
        ActivityStatus::Failed,
        serde_json::json!({"patterns":["["],"path":"src"}),
        serde_json::json!({}),
        "regex parse error: unclosed character class\nbacktrace omitted",
    );
    let card = &project_cards(&failed)[0];
    assert_eq!(card.title, "Grep");
    assert_eq!(
        card.body,
        "\"[\"\nin src\nregex parse error: unclosed character class"
    );

    let timeout = grep_card_state(
        "grep-timeout",
        "grep",
        ActivityStatus::Failed,
        serde_json::json!({"patterns":["needle"],"path":"."}),
        serde_json::json!({"timed_out":true}),
        "partial output",
    );
    let card = &project_cards(&timeout)[0];
    assert_eq!(card.title, "Grep • timed out");
    assert_eq!(card.body, "\"needle\"\nin .\nsearch timed out");

    let canceled = grep_card_state(
        "grep-canceled",
        "grep",
        ActivityStatus::Canceled,
        serde_json::json!({"patterns":["needle"],"path":"src"}),
        serde_json::json!({}),
        "partial output",
    );
    let card = &project_cards(&canceled)[0];
    assert_eq!(card.title, "Grep • canceled");
    assert_eq!(card.body, "\"needle\"\nin src\ncanceled");

    for (reason, message) in [
        ("matches", "… more matches may exist"),
        ("file_bytes", "… search incomplete: file size limit"),
        (
            "read_errors",
            "… search incomplete: unreadable files skipped",
        ),
        (
            "walk_errors",
            "… search incomplete: workspace walk errors or omissions",
        ),
    ] {
        let truncated = grep_card_state(
            "grep-truncated",
            "grep",
            ActivityStatus::Success,
            serde_json::json!({"patterns":["needle"],"path":"src"}),
            serde_json::json!({"matches_returned":4,"truncated":true,"limit_reason":reason}),
            "a:1:needle\nb:1:needle\nc:1:needle\nd:1:needle\ne:1:needle",
        );
        let card = &project_cards(&truncated)[0];
        assert_eq!(card.title, "Grep • 4 matches returned");
        assert!(
            card.body.contains("… preview limited to 4 lines"),
            "{}",
            card.body
        );
        assert!(card.body.ends_with(message), "{}", card.body);
        assert!(!card.body.contains("e:1:needle"), "{}", card.body);
    }
}

#[test]
fn grep_card_quotes_pattern_and_reports_independent_truncation_limits() {
    let quoted = grep_card_state(
        "grep-quoted",
        "grep",
        ActivityStatus::Running,
        serde_json::json!({"patterns":["quote\" and slash\\"],"path":"."}),
        serde_json::json!({}),
        "",
    );
    let card = &project_cards(&quoted)[0];
    assert_eq!(card.body, "\"quote\\\" and slash\\\\\"\nin .");

    let truncated = grep_card_state(
        "grep-multiple-limits",
        "grep",
        ActivityStatus::Success,
        serde_json::json!({"patterns":["needle"],"path":"src"}),
        serde_json::json!({
            "matches_returned":4,
            "truncated":true,
            "truncation_reasons":["matches", "file_bytes", "read_errors", "walk_errors"],
            "limit_reason":"matches"
        }),
        "a:1:needle",
    );
    let card = &project_cards(&truncated)[0];
    assert_eq!(card.title, "Grep • 4 matches returned");
    assert!(
        card.body.ends_with(
            "… more matches may exist\n… search incomplete: file size limit\n… search incomplete: unreadable files skipped\n… search incomplete: workspace walk errors or omissions"
        )
    );
}

#[test]
fn grep_card_sanitizes_pattern_scope_preview_and_handles_tiny_widths() {
    let secret = ["pattern", "secret"].join("-");
    let bearer = ["output", "secret"].join("-");
    let pattern = format!("{}{}\x1b[31m\nforged", "token=", secret);
    let output = format!("src/a.rs:1:authorization: {}{}\x1b[2J", "Bearer ", bearer);
    let state = grep_card_state(
        "grep-safe",
        "grep",
        ActivityStatus::Success,
        serde_json::json!({
            "patterns":[pattern],
            "path":"src\tsecret\u{1b}]0;hidden\u{7}"
        }),
        serde_json::json!({"matches_returned":1,"truncated":false}),
        &output,
    );
    let card = &project_cards(&state)[0];
    assert_eq!(card.title, "Grep • 1 match");
    assert!(card.body.contains("<redacted>"), "{}", card.body);
    assert!(card.body.contains("in src\\tsecret"), "{}", card.body);
    assert!(!card.body.contains(&secret), "{}", card.body);
    assert!(!card.body.contains(&bearer), "{}", card.body);
    assert!(!card.body.contains("hidden"), "{}", card.body);
    assert!(!card.body.contains('\u{1b}'), "{}", card.body);
    for width in [1, 2, 3, 12, CARD_LAYOUT_MIN_WIDTH, 80] {
        let lines = visual_lines(&state, width);
        assert!(!lines.is_empty());
        assert!(lines.iter().all(|line| {
            UnicodeWidthStr::width(line_text(&line.line).as_str()) == usize::from(width)
        }));
        let copied = visual_text_for_width(&state, width);
        assert!(copied.contains("Grep"), "width {width}: {copied:?}");
        assert!(!copied.contains("hidden"), "width {width}: {copied:?}");
    }
}