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