asterism 0.5.5

Hyperbolic navigation for tree data
Documentation
use crate::formats::difftastic::parse_difftastic_json;

#[test]
fn test_parse_single_file_diff() {
    let json = r##"[{"chunks":[[{"lhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":" ","highlight":"normal"},{"start":2,"end":3,"content":"1","highlight":"normal"}]},"rhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":" ","highlight":"normal"},{"start":2,"end":3,"content":"I","highlight":"normal"}]}}]],"language":"Text","path":"test.md","status":"changed"}]"##;

    let sections = parse_difftastic_json(json).unwrap();

    assert!(!sections.is_empty(), "Should parse sections");

    // Should have 1 hunk section
    assert_eq!(sections.len(), 1, "Should have 1 hunk");
    assert_eq!(sections[0].level, 1, "Hunk should be level 1");
    assert!(sections[0].title.contains("Hunk"), "Should be a hunk");
    assert_eq!(sections[0].file_path, "test.md", "Should track file path");

    // Hunks are top-level, no parent
    assert_eq!(sections[0].parent_index, None, "Hunks have no parent");
}

#[test]
fn test_parse_multi_hunk_file() {
    // Your actual test.md example with multiple changes
    let json = r##"{"chunks":[[{"lhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":" ","highlight":"normal"},{"start":2,"end":3,"content":"1","highlight":"normal"}]},"rhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":" ","highlight":"normal"},{"start":2,"end":3,"content":"I","highlight":"normal"}]}},{"lhs":{"line_number":2,"changes":[{"start":0,"end":1,"content":"?","highlight":"normal"}]},"rhs":{"line_number":2,"changes":[{"start":0,"end":1,"content":"!","highlight":"normal"}]}}],[{"lhs":{"line_number":4,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":"#","highlight":"normal"}]},"rhs":{"line_number":4,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"},{"start":1,"end":2,"content":"#","highlight":"normal"}]}}]],"language":"Text","path":"test.md","status":"changed"}"##;

    let sections = parse_difftastic_json(json).unwrap();

    // Should have 2 hunks (2 chunk arrays)
    assert_eq!(sections.len(), 2, "Should have 2 hunks");

    assert_eq!(sections[0].level, 1, "First hunk is level 1");
    assert_eq!(sections[1].level, 1, "Second hunk is level 1");

    assert!(sections[0].title.contains("Hunk 1"));
    assert!(sections[1].title.contains("Hunk 2"));

    // Both hunks from same file
    assert_eq!(sections[0].file_path, "test.md");
    assert_eq!(sections[1].file_path, "test.md");
}

#[test]
fn test_parse_ndjson_multi_file() {
    // Your actual git diff output with foo.py and test.md
    let json = r##"{"language":"Python","path":"foo.py","status":"created"}
{"chunks":[[{"lhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"}]},"rhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"#","highlight":"normal"}]}}]],"language":"Text","path":"test.md","status":"changed"}"##;

    let sections = parse_difftastic_json(json).unwrap();

    assert!(!sections.is_empty(), "Should parse NDJSON format");

    // Should have: 1 placeholder for foo.py + 1 hunk for test.md = 2 sections
    assert_eq!(sections.len(), 2, "Should have 2 sections total");

    // Check foo.py placeholder (created, no chunks)
    assert!(sections[0].title.contains("created") || sections[0].title.contains("File"));
    assert_eq!(sections[0].file_path, "foo.py");

    // Check test.md hunk
    assert!(sections[1].title.contains("Hunk"));
    assert_eq!(sections[1].file_path, "test.md");
}

#[test]
fn test_hunk_structure() {
    let json = r#"[{"chunks":[[{"lhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"a","highlight":"normal"}]},"rhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"b","highlight":"normal"}]}}],[{"lhs":{"line_number":5,"changes":[{"start":0,"end":1,"content":"c","highlight":"normal"}]},"rhs":{"line_number":5,"changes":[{"start":0,"end":1,"content":"d","highlight":"normal"}]}}]],"language":"Text","path":"multi.md","status":"changed"}]"#;

    let sections = parse_difftastic_json(json).unwrap();

    // Should have 2 hunks (2 chunk arrays)
    assert_eq!(sections.len(), 2, "Should have 2 hunks");

    // Both are top-level sections with same file path
    assert_eq!(sections[0].level, 1);
    assert_eq!(sections[1].level, 1);
    assert_eq!(sections[0].file_path, "multi.md");
    assert_eq!(sections[1].file_path, "multi.md");

    // No parent-child relationships between hunks
    assert_eq!(sections[0].parent_index, None);
    assert_eq!(sections[1].parent_index, None);
    assert!(sections[0].children_indices.is_empty());
    assert!(sections[1].children_indices.is_empty());
}

#[test]
fn test_unchanged_file_skipped() {
    let json = r#"[{"language":"Text","path":"unchanged.md","status":"unchanged"}]"#;
    let sections = parse_difftastic_json(json).unwrap();
    assert!(sections.is_empty(), "Should skip unchanged files");
}

#[test]
fn test_created_file_no_chunks() {
    let json = r#"[{"language":"Python","path":"new.py","status":"created"}]"#;
    let sections = parse_difftastic_json(json).unwrap();

    // Should have 1 placeholder hunk for the created file
    assert_eq!(sections.len(), 1, "Should have placeholder hunk");
    assert!(sections[0].title.contains("created") || sections[0].title.contains("File"));
    assert_eq!(sections[0].file_path, "new.py");
}

#[test]
fn test_deleted_file_no_chunks() {
    let json = r#"[{"language":"Python","path":"old.py","status":"deleted"}]"#;
    let sections = parse_difftastic_json(json).unwrap();

    assert_eq!(sections.len(), 1, "Should have placeholder hunk");
    assert!(sections[0].title.contains("deleted") || sections[0].title.contains("File"));
    assert_eq!(sections[0].file_path, "old.py");
}

#[test]
fn test_file_path_tracking() {
    let json = r#"{"chunks":[[{"lhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"a","highlight":"normal"}]},"rhs":{"line_number":0,"changes":[{"start":0,"end":1,"content":"b","highlight":"normal"}]}}]],"language":"Rust","path":"src/main.rs","status":"changed"}"#;

    let sections = parse_difftastic_json(json).unwrap();

    assert_eq!(sections.len(), 1);
    assert_eq!(
        sections[0].file_path, "src/main.rs",
        "Should preserve full file path"
    );
}