diff --git headers pack two paths on one space-delimited line, which broke for unquoted paths containing spaces and for git's C-style octal-quoted paths (non-ASCII filenames). Add quote-aware header splitting plus a git unquote helper, and prefer the unambiguous "--- a/"/"+++ b/" lines to correct the file's identity when present.
178 lines
5.6 KiB
C#
178 lines
5.6 KiB
C#
using System.Linq;
|
|
using ClaudeDo.Ui.ViewModels.Modals;
|
|
|
|
namespace ClaudeDo.Ui.Tests.ViewModels;
|
|
|
|
public class UnifiedDiffParserTests
|
|
{
|
|
[Fact]
|
|
public void Modified_file_counts_additions_and_deletions()
|
|
{
|
|
const string raw =
|
|
"diff --git a/src/Foo.cs b/src/Foo.cs\n" +
|
|
"index 111..222 100644\n" +
|
|
"--- a/src/Foo.cs\n" +
|
|
"+++ b/src/Foo.cs\n" +
|
|
"@@ -1,3 +1,3 @@\n" +
|
|
" ctx\n" +
|
|
"-old\n" +
|
|
"+new\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal("src/Foo.cs", file.Path);
|
|
Assert.Equal(DiffFileStatus.Modified, file.Status);
|
|
Assert.Equal("M", file.StatusCode);
|
|
Assert.Equal(1, file.Additions);
|
|
Assert.Equal(1, file.Deletions);
|
|
Assert.True(file.HasLines);
|
|
Assert.False(file.IsBinary);
|
|
}
|
|
|
|
[Fact]
|
|
public void New_file_is_marked_added()
|
|
{
|
|
const string raw =
|
|
"diff --git a/New.cs b/New.cs\n" +
|
|
"new file mode 100644\n" +
|
|
"index 000..abc\n" +
|
|
"--- /dev/null\n" +
|
|
"+++ b/New.cs\n" +
|
|
"@@ -0,0 +1,1 @@\n" +
|
|
"+hello\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal(DiffFileStatus.Added, file.Status);
|
|
Assert.Equal("A", file.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Deleted_file_is_marked_deleted()
|
|
{
|
|
const string raw =
|
|
"diff --git a/Gone.cs b/Gone.cs\n" +
|
|
"deleted file mode 100644\n" +
|
|
"index abc..000\n" +
|
|
"--- a/Gone.cs\n" +
|
|
"+++ /dev/null\n" +
|
|
"@@ -1,1 +0,0 @@\n" +
|
|
"-bye\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal(DiffFileStatus.Deleted, file.Status);
|
|
Assert.Equal("D", file.StatusCode);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rename_captures_old_and_new_path()
|
|
{
|
|
const string raw =
|
|
"diff --git a/Old.cs b/New.cs\n" +
|
|
"similarity index 100%\n" +
|
|
"rename from Old.cs\n" +
|
|
"rename to New.cs\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal(DiffFileStatus.Renamed, file.Status);
|
|
Assert.Equal("R", file.StatusCode);
|
|
Assert.Equal("Old.cs", file.OldPath);
|
|
Assert.Equal("New.cs", file.Path);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binary_file_is_flagged_with_no_lines()
|
|
{
|
|
const string raw =
|
|
"diff --git a/img.png b/img.png\n" +
|
|
"new file mode 100644\n" +
|
|
"index 000..abc\n" +
|
|
"Binary files /dev/null and b/img.png differ\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.True(file.IsBinary);
|
|
Assert.False(file.HasLines);
|
|
Assert.False(file.IsEmptyContent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Empty_new_file_reports_empty_content()
|
|
{
|
|
const string raw =
|
|
"diff --git a/Empty.txt b/Empty.txt\n" +
|
|
"new file mode 100644\n" +
|
|
"index 000..000\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal(DiffFileStatus.Added, file.Status);
|
|
Assert.False(file.HasLines);
|
|
Assert.True(file.IsEmptyContent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Path_with_spaces_is_parsed_in_full()
|
|
{
|
|
const string raw =
|
|
"diff --git a/pfad mit space.txt b/pfad mit space.txt\n" +
|
|
"index 111..222 100644\n" +
|
|
"--- a/pfad mit space.txt\t\n" +
|
|
"+++ b/pfad mit space.txt\t\n" +
|
|
"@@ -1,1 +1,1 @@\n" +
|
|
"-old\n" +
|
|
"+new\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal("pfad mit space.txt", file.Path);
|
|
Assert.Equal(DiffFileStatus.Modified, file.Status);
|
|
}
|
|
|
|
[Fact]
|
|
public void Rename_with_spaces_on_both_sides_captures_full_paths()
|
|
{
|
|
const string raw =
|
|
"diff --git a/old name with space.txt b/new name with space.txt\n" +
|
|
"similarity index 75%\n" +
|
|
"rename from old name with space.txt\n" +
|
|
"rename to new name with space.txt\n" +
|
|
"index 111..222 100644\n" +
|
|
"--- a/old name with space.txt\t\n" +
|
|
"+++ b/new name with space.txt\t\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal(DiffFileStatus.Renamed, file.Status);
|
|
Assert.Equal("old name with space.txt", file.OldPath);
|
|
Assert.Equal("new name with space.txt", file.Path);
|
|
}
|
|
|
|
[Fact]
|
|
public void Git_quoted_unicode_path_is_unquoted()
|
|
{
|
|
// git quotes paths containing non-ASCII bytes as C-style octal escapes,
|
|
// e.g. "pfad_ä.txt" -> "pfad_\303\244.txt" (UTF-8 bytes for 'ä').
|
|
const string raw =
|
|
"diff --git \"a/pfad_\\303\\244.txt\" \"b/pfad_\\303\\244.txt\"\n" +
|
|
"index 111..222 100644\n" +
|
|
"--- \"a/pfad_\\303\\244.txt\"\n" +
|
|
"+++ \"b/pfad_\\303\\244.txt\"\n" +
|
|
"@@ -1,1 +1,1 @@\n" +
|
|
"-old\n" +
|
|
"+new\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal("pfad_ä.txt", file.Path);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binary_file_with_spaces_is_parsed_in_full()
|
|
{
|
|
const string raw =
|
|
"diff --git a/bin file.png b/bin file.png\n" +
|
|
"new file mode 100644\n" +
|
|
"index 000..abc\n" +
|
|
"Binary files /dev/null and b/bin file.png differ\n";
|
|
|
|
var file = Assert.Single(UnifiedDiffParser.Parse(raw));
|
|
Assert.Equal("bin file.png", file.Path);
|
|
Assert.True(file.IsBinary);
|
|
Assert.False(file.HasLines);
|
|
}
|
|
}
|