Files
git.stella-ops.org/src/AirGap/__Tests/StellaOps.AirGap.Sync.Tests/HlcMergeServiceTests.MergeOrdering.cs
2026-02-04 19:59:20 +02:00

97 lines
3.8 KiB
C#

// <copyright file="HlcMergeServiceTests.MergeOrdering.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under BUSL-1.1.
// </copyright>
using FluentAssertions;
using StellaOps.AirGap.Sync.Models;
using Xunit;
namespace StellaOps.AirGap.Sync.Tests;
public sealed partial class HlcMergeServiceTests
{
[Fact]
public async Task MergeAsync_TwoNodes_MergesByHlcOrderAsync()
{
var nodeA = CreateNodeLog("node-a", new[]
{
CreateEntry("node-a", 100, 0, Guid.Parse("aaaaaaaa-0001-0000-0000-000000000000")),
CreateEntry("node-a", 102, 0, Guid.Parse("aaaaaaaa-0003-0000-0000-000000000000"))
});
var nodeB = CreateNodeLog("node-b", new[]
{
CreateEntry("node-b", 101, 0, Guid.Parse("bbbbbbbb-0002-0000-0000-000000000000")),
CreateEntry("node-b", 103, 0, Guid.Parse("bbbbbbbb-0004-0000-0000-000000000000"))
});
var result = await _sut.MergeAsync(new[] { nodeA, nodeB });
result.MergedEntries.Should().HaveCount(4);
result.MergedEntries[0].THlc.PhysicalTime.Should().Be(100);
result.MergedEntries[1].THlc.PhysicalTime.Should().Be(101);
result.MergedEntries[2].THlc.PhysicalTime.Should().Be(102);
result.MergedEntries[3].THlc.PhysicalTime.Should().Be(103);
result.SourceNodes.Should().HaveCount(2);
}
[Fact]
public async Task MergeAsync_SamePhysicalTime_OrdersByLogicalCounterAsync()
{
var nodeA = CreateNodeLog("node-a", new[]
{
CreateEntry("node-a", 100, 0, Guid.Parse("aaaaaaaa-0000-0000-0000-000000000001")),
CreateEntry("node-a", 100, 2, Guid.Parse("aaaaaaaa-0000-0000-0000-000000000003"))
});
var nodeB = CreateNodeLog("node-b", new[]
{
CreateEntry("node-b", 100, 1, Guid.Parse("bbbbbbbb-0000-0000-0000-000000000002")),
CreateEntry("node-b", 100, 3, Guid.Parse("bbbbbbbb-0000-0000-0000-000000000004"))
});
var result = await _sut.MergeAsync(new[] { nodeA, nodeB });
result.MergedEntries.Should().HaveCount(4);
result.MergedEntries[0].THlc.LogicalCounter.Should().Be(0);
result.MergedEntries[1].THlc.LogicalCounter.Should().Be(1);
result.MergedEntries[2].THlc.LogicalCounter.Should().Be(2);
result.MergedEntries[3].THlc.LogicalCounter.Should().Be(3);
}
[Fact]
public async Task MergeAsync_SameTimeAndCounter_OrdersByNodeIdAsync()
{
var nodeA = CreateNodeLog("alpha-node", new[]
{
CreateEntry("alpha-node", 100, 0, Guid.Parse("aaaaaaaa-0000-0000-0000-000000000001"))
});
var nodeB = CreateNodeLog("beta-node", new[]
{
CreateEntry("beta-node", 100, 0, Guid.Parse("bbbbbbbb-0000-0000-0000-000000000002"))
});
var result = await _sut.MergeAsync(new[] { nodeA, nodeB });
result.MergedEntries.Should().HaveCount(2);
result.MergedEntries[0].SourceNodeId.Should().Be("alpha-node");
result.MergedEntries[1].SourceNodeId.Should().Be("beta-node");
}
[Fact]
public async Task MergeAsync_RecomputesUnifiedChainAsync()
{
var nodeLog = CreateNodeLog("node-a", new[]
{
CreateEntry("node-a", 100, 0, Guid.Parse("11111111-1111-1111-1111-111111111111")),
CreateEntry("node-a", 200, 0, Guid.Parse("22222222-2222-2222-2222-222222222222"))
});
var result = await _sut.MergeAsync(new[] { nodeLog });
result.MergedEntries.Should().HaveCount(2);
result.MergedEntries[0].MergedLink.Should().NotBeNull();
result.MergedEntries[1].MergedLink.Should().NotBeNull();
result.MergedChainHead.Should().NotBeNull();
result.MergedEntries[0].MergedLink.Should().HaveCount(32);
result.MergedChainHead.Should().BeEquivalentTo(result.MergedEntries[1].MergedLink);
}
}