using System.Linq; using ClaudeDo.Data.Git; using Xunit; namespace ClaudeDo.Data.Tests; public class ConflictMarkerParserTests { [Fact] public void NoMarkers_YieldsSingleStableSegment_AndRoundTrips() { const string text = "just\nsome\nplain\nlines\n"; var segments = ConflictMarkerParser.Parse(text); var only = Assert.Single(segments); Assert.False(only.IsConflict); Assert.Equal(text, ConflictMarkerParser.Compose(segments, c => c.Ours)); Assert.False(ConflictMarkerParser.HasConflicts(text)); } [Fact] public void SimpleConflict_SplitsIntoStable_Conflict_Stable() { const string text = "line1\n<<<<<<< HEAD\nours line\n=======\ntheirs line\n>>>>>>> branch\nline2\n"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal(3, segments.Count); Assert.Equal("line1\n", segments[0].Text); Assert.True(segments[1].IsConflict); Assert.Equal("ours line\n", segments[1].Ours); Assert.Equal("theirs line\n", segments[1].Theirs); Assert.Null(segments[1].Base); Assert.Equal("line2\n", segments[2].Text); Assert.True(ConflictMarkerParser.HasConflicts(text)); } [Fact] public void AcceptingOurs_Or_Theirs_ProducesTheResolvedFile() { const string text = "line1\n<<<<<<< HEAD\nours line\n=======\ntheirs line\n>>>>>>> branch\nline2\n"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal("line1\nours line\nline2\n", ConflictMarkerParser.Compose(segments, c => c.Ours)); Assert.Equal("line1\ntheirs line\nline2\n", ConflictMarkerParser.Compose(segments, c => c.Theirs)); // "Accept both" = ours followed by theirs. Assert.Equal("line1\nours line\ntheirs line\nline2\n", ConflictMarkerParser.Compose(segments, c => c.Ours + c.Theirs)); } [Fact] public void Diff3Style_CapturesTheMergeBase() { const string text = "a\n<<<<<<< HEAD\nX\n||||||| base\nB\n=======\nY\n>>>>>>> branch\nz\n"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal(3, segments.Count); Assert.Equal("X\n", segments[1].Ours); Assert.Equal("B\n", segments[1].Base); Assert.Equal("Y\n", segments[1].Theirs); } [Fact] public void MultipleConflicts_AreEachCaptured() { const string text = "<<<<<<< HEAD\nA\n=======\nB\n>>>>>>> br\nmid\n<<<<<<< HEAD\nC\n=======\nD\n>>>>>>> br\n"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal(3, segments.Count); Assert.True(segments[0].IsConflict); Assert.Equal("A\n", segments[0].Ours); Assert.Equal("mid\n", segments[1].Text); Assert.True(segments[2].IsConflict); Assert.Equal("D\n", segments[2].Theirs); } [Fact] public void CrlfLineEndings_ArePreserved() { const string text = "a\r\n<<<<<<< HEAD\r\nX\r\n=======\r\nY\r\n>>>>>>> br\r\nb\r\n"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal("a\r\nX\r\nb\r\n", ConflictMarkerParser.Compose(segments, c => c.Ours)); } [Fact] public void ConflictAtEndOfFile_WithoutTrailingNewline_IsParsed() { const string text = "a\n<<<<<<< HEAD\nX\n=======\nY\n>>>>>>> br"; var segments = ConflictMarkerParser.Parse(text); Assert.Equal(2, segments.Count); Assert.Equal("a\n", segments[0].Text); Assert.True(segments[1].IsConflict); Assert.Equal("X\n", segments[1].Ours); Assert.Equal("Y\n", segments[1].Theirs); } [Fact] public void SevenEqualsInOrdinaryText_IsNotTreatedAsAConflict() { const string text = "title\n=======\nbody\n"; var segments = ConflictMarkerParser.Parse(text); var only = Assert.Single(segments); Assert.False(only.IsConflict); Assert.Equal(text, ConflictMarkerParser.Compose(segments, c => c.Ours)); } }