feat(configmanager): add DiffService with tests
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
using DiffPlex;
|
||||||
|
using DiffPlex.DiffBuilder;
|
||||||
|
using DiffPlex.DiffBuilder.Model;
|
||||||
|
|
||||||
|
namespace JdeScoping.ConfigManager.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for generating diffs between text content.
|
||||||
|
/// </summary>
|
||||||
|
public class DiffService : IDiffService
|
||||||
|
{
|
||||||
|
public DiffResult GenerateDiff(string original, string modified)
|
||||||
|
{
|
||||||
|
var diffBuilder = new InlineDiffBuilder(new Differ());
|
||||||
|
var diff = diffBuilder.BuildDiffModel(original, modified);
|
||||||
|
|
||||||
|
var lines = new List<DiffLine>();
|
||||||
|
int oldLineNum = 1;
|
||||||
|
int newLineNum = 1;
|
||||||
|
int insertions = 0;
|
||||||
|
int deletions = 0;
|
||||||
|
|
||||||
|
foreach (var line in diff.Lines)
|
||||||
|
{
|
||||||
|
var diffLine = new DiffLine
|
||||||
|
{
|
||||||
|
Text = line.Text,
|
||||||
|
Type = line.Type switch
|
||||||
|
{
|
||||||
|
ChangeType.Inserted => DiffLineType.Added,
|
||||||
|
ChangeType.Deleted => DiffLineType.Removed,
|
||||||
|
_ => DiffLineType.Unchanged
|
||||||
|
},
|
||||||
|
OldLineNumber = line.Type == ChangeType.Inserted ? null : oldLineNum,
|
||||||
|
NewLineNumber = line.Type == ChangeType.Deleted ? null : newLineNum
|
||||||
|
};
|
||||||
|
|
||||||
|
lines.Add(diffLine);
|
||||||
|
|
||||||
|
switch (line.Type)
|
||||||
|
{
|
||||||
|
case ChangeType.Inserted:
|
||||||
|
newLineNum++;
|
||||||
|
insertions++;
|
||||||
|
break;
|
||||||
|
case ChangeType.Deleted:
|
||||||
|
oldLineNum++;
|
||||||
|
deletions++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
oldLineNum++;
|
||||||
|
newLineNum++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DiffResult
|
||||||
|
{
|
||||||
|
HasChanges = insertions > 0 || deletions > 0,
|
||||||
|
Lines = lines,
|
||||||
|
Insertions = insertions,
|
||||||
|
Deletions = deletions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace JdeScoping.ConfigManager.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a line in a diff output.
|
||||||
|
/// </summary>
|
||||||
|
public class DiffLine
|
||||||
|
{
|
||||||
|
public required int? OldLineNumber { get; init; }
|
||||||
|
public required int? NewLineNumber { get; init; }
|
||||||
|
public required string Text { get; init; }
|
||||||
|
public required DiffLineType Type { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DiffLineType
|
||||||
|
{
|
||||||
|
Unchanged,
|
||||||
|
Added,
|
||||||
|
Removed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of a diff operation.
|
||||||
|
/// </summary>
|
||||||
|
public class DiffResult
|
||||||
|
{
|
||||||
|
public bool HasChanges { get; init; }
|
||||||
|
public List<DiffLine> Lines { get; init; } = [];
|
||||||
|
public int Insertions { get; init; }
|
||||||
|
public int Deletions { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for generating diffs between text content.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDiffService
|
||||||
|
{
|
||||||
|
DiffResult GenerateDiff(string original, string modified);
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
using JdeScoping.ConfigManager.Services;
|
||||||
|
|
||||||
|
namespace JdeScoping.ConfigManager.Tests.Services;
|
||||||
|
|
||||||
|
public class DiffServiceTests
|
||||||
|
{
|
||||||
|
private readonly DiffService _sut;
|
||||||
|
|
||||||
|
public DiffServiceTests()
|
||||||
|
{
|
||||||
|
_sut = new DiffService();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithNoChanges_ReturnsEmptyDiff()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2\nline3";
|
||||||
|
var modified = "line1\nline2\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithChanges_ReturnsDiffLines()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2\nline3";
|
||||||
|
var modified = "line1\nmodified\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Lines.ShouldNotBeEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithAddedLine_ReportsInsertion()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2";
|
||||||
|
var modified = "line1\nline2\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Insertions.ShouldBe(1);
|
||||||
|
result.Deletions.ShouldBe(0);
|
||||||
|
result.Lines.ShouldContain(l => l.Type == DiffLineType.Added && l.Text == "line3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithRemovedLine_ReportsDeletion()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2\nline3";
|
||||||
|
var modified = "line1\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Deletions.ShouldBe(1);
|
||||||
|
result.Lines.ShouldContain(l => l.Type == DiffLineType.Removed && l.Text == "line2");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithModifiedLine_ReportsAdditionAndDeletion()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\noriginal\nline3";
|
||||||
|
var modified = "line1\nchanged\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Insertions.ShouldBeGreaterThan(0);
|
||||||
|
result.Deletions.ShouldBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithEmptyOriginal_ReportsAllAsInsertions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "";
|
||||||
|
var modified = "line1\nline2";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Insertions.ShouldBeGreaterThan(0);
|
||||||
|
result.Deletions.ShouldBe(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_WithEmptyModified_ReportsAllAsDeletions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2";
|
||||||
|
var modified = "";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.HasChanges.ShouldBeTrue();
|
||||||
|
result.Insertions.ShouldBe(0);
|
||||||
|
result.Deletions.ShouldBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_LineNumbers_AreCorrectForUnchangedLines()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2\nline3";
|
||||||
|
var modified = "line1\nline2\nline3";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var firstLine = result.Lines.First();
|
||||||
|
firstLine.OldLineNumber.ShouldBe(1);
|
||||||
|
firstLine.NewLineNumber.ShouldBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_AddedLine_HasNullOldLineNumber()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1";
|
||||||
|
var modified = "line1\nline2";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var addedLine = result.Lines.FirstOrDefault(l => l.Type == DiffLineType.Added);
|
||||||
|
addedLine.ShouldNotBeNull();
|
||||||
|
addedLine.OldLineNumber.ShouldBeNull();
|
||||||
|
addedLine.NewLineNumber.ShouldNotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateDiff_RemovedLine_HasNullNewLineNumber()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var original = "line1\nline2";
|
||||||
|
var modified = "line1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.GenerateDiff(original, modified);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var removedLine = result.Lines.FirstOrDefault(l => l.Type == DiffLineType.Removed);
|
||||||
|
removedLine.ShouldNotBeNull();
|
||||||
|
removedLine.OldLineNumber.ShouldNotBeNull();
|
||||||
|
removedLine.NewLineNumber.ShouldBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user