magi-code 0.77.1

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

#[test]
fn ast_grep_card_projects_params_counts_preview_and_truncation() {
    let state = ast_grep_card_state(
        "ast-grep-full",
        ActivityStatus::Success,
        serde_json::json!({
            "pattern":"$F($$$)", "path":"src", "language":"rust",
            "rewrite":"$F($$$)", "limit":7
        }),
        serde_json::json!({"matches_returned":1,"truncated":true,"stdout_truncated":true}),
        "one\ntwo\nthree\nfour\nfive",
    );
    let card = &project_cards(&state)[0];
    assert_eq!(card.role, TranscriptCardRole::AstGrep);
    assert_eq!(card.title, "AST Grep • 1 match returned");
    assert_eq!(
        card.body,
        "\"$F($$$)\"\nin src\nlanguage rust\nrewrite \"$F($$$)\"\nlimit 7\none\ntwo\nthree\nfour\n… preview limited to 4 lines\n… output truncated"
    );

    for (count, title) in [
        (0, "AST Grep • 0 matches"),
        (1, "AST Grep • 1 match"),
        (2, "AST Grep • 2 matches"),
    ] {
        let state = ast_grep_card_state(
            &format!("ast-grep-count-{count}"),
            ActivityStatus::Success,
            serde_json::json!({"pattern":"x"}),
            serde_json::json!({"matches_returned":count}),
            "",
        );
        assert_eq!(project_cards(&state)[0].title, title);
    }
    let missing = ast_grep_card_state(
        "ast-grep-missing-count",
        ActivityStatus::Success,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({}),
        "",
    );
    assert_eq!(project_cards(&missing)[0].title, "AST Grep");

    let generic_truncation = ast_grep_card_state(
        "ast-grep-generic-truncation",
        ActivityStatus::Success,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({"matches_returned":2,"truncated":true}),
        "one\ntwo",
    );
    let card = &project_cards(&generic_truncation)[0];
    assert_eq!(card.title, "AST Grep • 2 matches returned");
    assert!(card.body.ends_with("… search incomplete"));

    let output_only_truncation = ast_grep_card_state(
        "ast-grep-output-only-truncation",
        ActivityStatus::Success,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({"matches_returned":1,"stdout_truncated":true}),
        "one",
    );
    assert!(
        project_cards(&output_only_truncation)[0]
            .body
            .ends_with("… output truncated")
    );
}

#[test]
fn ast_grep_card_uses_path_and_language_metadata_and_preserves_failure_detail() {
    let state = ast_grep_card_state(
        "ast-grep-meta",
        ActivityStatus::Running,
        serde_json::json!({"pattern":"node"}),
        serde_json::json!({"path":"resolved/src","language":"typescript"}),
        "",
    );
    assert_eq!(
        project_cards(&state)[0].body,
        "\"node\"\nin resolved/src\nlanguage typescript"
    );

    let failed = ast_grep_card_state(
        "ast-grep-install",
        ActivityStatus::Failed,
        serde_json::json!({"pattern":"node","path":"src"}),
        serde_json::json!({}),
        "ast-grep binary not found; install ast-grep with cargo install ast-grep",
    );
    assert_eq!(
        project_cards(&failed)[0].body,
        "\"node\"\nin src\nast-grep binary not found; install ast-grep with cargo install ast-grep"
    );

    let cleanup = ast_grep_card_state(
        "ast-grep-cleanup",
        ActivityStatus::Failed,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({"cleanup_warning":"cleanup warning\nsecondary"}),
        "real error",
    );
    assert!(project_cards(&cleanup)[0].body.ends_with("cleanup warning"));
}

#[test]
fn ast_grep_card_timeout_cancellation_and_failure_status_are_exact() {
    let timeout = ast_grep_card_state(
        "ast-grep-timeout",
        ActivityStatus::Failed,
        serde_json::json!({"pattern":"x","path":"."}),
        serde_json::json!({"timed_out":true}),
        "partial",
    );
    assert_eq!(project_cards(&timeout)[0].title, "AST Grep • timed out");
    assert!(
        project_cards(&timeout)[0]
            .body
            .ends_with("search timed out")
    );
    let canceled = ast_grep_card_state(
        "ast-grep-canceled",
        ActivityStatus::Canceled,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({}),
        "partial",
    );
    assert_eq!(
        project_cards(&canceled)[0].body,
        "\"x\"\nin .\nCanceled before completion"
    );
    let runtime_canceled = ast_grep_card_state(
        "ast-grep-runtime-canceled",
        ActivityStatus::Failed,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({}),
        "prompt canceled",
    );
    assert_eq!(
        project_cards(&runtime_canceled)[0].status,
        TranscriptCardStatus::Canceled
    );
    let failed = ast_grep_card_state(
        "ast-grep-negative-canceled",
        ActivityStatus::Failed,
        serde_json::json!({"pattern":"x"}),
        serde_json::json!({}),
        "real failure\nprompt canceled",
    );
    assert_eq!(
        project_cards(&failed)[0].status,
        TranscriptCardStatus::Failed
    );
    assert_eq!(project_cards(&failed)[0].body, "\"x\"\nin .\nreal failure");
}

#[test]
fn ast_grep_card_sanitizes_unicode_and_keeps_dedicated_copy_at_tiny_widths() {
    let secret = "ast-grep-secret";
    let long = "".repeat(PREVIEW_LINE_MAX_DISPLAY_WIDTH);
    let state = ast_grep_card_state(
        "ast-grep-safe",
        ActivityStatus::Success,
        serde_json::json!({
            "pattern":format!("authorization: Bearer {secret}\u{1b}[31m\nforged"),
            "path":format!("src/authorization: Bearer {secret}\u{1b}]0;hidden\u{7}"), "language":long,
            "rewrite":format!("token={secret}"), "limit":4
        }),
        serde_json::json!({"matches_returned":1}),
        &format!("{long}\nauthorization: Bearer {secret}\u{1b}[2J"),
    );
    let card = &project_cards(&state)[0];
    assert!(!card.body.contains(secret));
    assert!(!card.body.contains('\u{1b}'));
    assert!(
        card.body
            .lines()
            .all(|line| UnicodeWidthStr::width(line) <= PREVIEW_LINE_MAX_DISPLAY_WIDTH)
    );
    let visual = state.transcript_visual_text();
    let tiny_state = ast_grep_card_state(
        "ast-grep-tiny",
        ActivityStatus::Success,
        serde_json::json!({"pattern":"x","path":"src","language":"rust","rewrite":"y","limit":4}),
        serde_json::json!({"matches_returned":1}),
        "safe output",
    );
    assert!(!visual.contains("Tool:"));
    for width in [1, 2, 3, 12, CARD_LAYOUT_MIN_WIDTH, 80] {
        let lines = visual_lines(&tiny_state, width);
        assert!(lines.iter().all(
            |line| UnicodeWidthStr::width(line_text(&line.line).as_str()) == usize::from(width)
        ));
        let copied = visual_text_for_width(&tiny_state, width);
        assert!(copied.contains("AST Grep"));
        assert!(!copied.contains(secret));
    }
    let empty = ast_grep_card_state(
        "ast-grep-empty",
        ActivityStatus::Running,
        serde_json::json!({}),
        serde_json::json!({}),
        "",
    );
    assert_eq!(project_cards(&empty)[0].body, "in .");
}