using Microsoft.Extensions.Logging.Abstractions; using ZB.MOM.WW.CBDDC.Core; using ZB.MOM.WW.CBDDC.Core.Network; using ZB.MOM.WW.CBDDC.Persistence.BLite; namespace ZB.MOM.WW.CBDDC.Sample.Console.Tests; public class PeerOplogConfirmationStoreTests : IDisposable { private readonly string _testDbPath; private readonly SampleDbContext _context; private readonly BLitePeerOplogConfirmationStore _store; /// /// Initializes a new instance of the class. /// public PeerOplogConfirmationStoreTests() { _testDbPath = Path.Combine(Path.GetTempPath(), $"test-peer-confirmation-{Guid.NewGuid()}.blite"); _context = new SampleDbContext(_testDbPath); _store = new BLitePeerOplogConfirmationStore( _context, NullLogger>.Instance); } /// /// Verifies that ensuring peer registration multiple times remains idempotent. /// [Fact] public async Task EnsurePeerRegisteredAsync_IsIdempotent() { await _store.EnsurePeerRegisteredAsync("peer-a", "10.0.0.10:5050", PeerType.StaticRemote); await _store.EnsurePeerRegisteredAsync("peer-a", "10.0.0.10:5050", PeerType.StaticRemote); var active = (await _store.GetActiveTrackedPeersAsync()).ToList(); var exported = (await _store.ExportAsync()).ToList(); active.Count.ShouldBe(1); active[0].ShouldBe("peer-a"); exported.Count(x => x.PeerNodeId == "peer-a" && x.SourceNodeId == "__peer_registration__").ShouldBe(1); } /// /// Verifies create, update, and read flows for peer oplog confirmations. /// [Fact] public async Task ConfirmationStore_CrudFlow_Works() { await _store.EnsurePeerRegisteredAsync("peer-a", "10.0.0.10:5050", PeerType.StaticRemote); await _store.UpdateConfirmationAsync("peer-a", "source-1", new HlcTimestamp(100, 1, "source-1"), "hash-1"); var firstRead = (await _store.GetConfirmationsForPeerAsync("peer-a")).ToList(); firstRead.Count.ShouldBe(1); firstRead[0].ConfirmedWall.ShouldBe(100); firstRead[0].ConfirmedLogic.ShouldBe(1); firstRead[0].ConfirmedHash.ShouldBe("hash-1"); await _store.UpdateConfirmationAsync("peer-a", "source-1", new HlcTimestamp(120, 2, "source-1"), "hash-2"); await _store.UpdateConfirmationAsync("peer-a", "source-2", new HlcTimestamp(130, 0, "source-2"), "hash-3"); var secondRead = (await _store.GetConfirmationsForPeerAsync("peer-a")).OrderBy(x => x.SourceNodeId).ToList(); var allConfirmations = (await _store.GetConfirmationsAsync()).ToList(); secondRead.Count.ShouldBe(2); secondRead[0].SourceNodeId.ShouldBe("source-1"); secondRead[0].ConfirmedWall.ShouldBe(120); secondRead[0].ConfirmedLogic.ShouldBe(2); secondRead[0].ConfirmedHash.ShouldBe("hash-2"); secondRead[1].SourceNodeId.ShouldBe("source-2"); secondRead[1].ConfirmedWall.ShouldBe(130); secondRead[1].ConfirmedLogic.ShouldBe(0); secondRead[1].ConfirmedHash.ShouldBe("hash-3"); allConfirmations.Count.ShouldBe(2); } /// /// Verifies that removing peer tracking deactivates tracking records for that peer. /// [Fact] public async Task RemovePeerTrackingAsync_DeactivatesPeerTracking() { await _store.EnsurePeerRegisteredAsync("peer-a", "10.0.0.10:5050", PeerType.StaticRemote); await _store.EnsurePeerRegisteredAsync("peer-b", "10.0.0.11:5050", PeerType.StaticRemote); await _store.UpdateConfirmationAsync("peer-a", "source-1", new HlcTimestamp(100, 0, "source-1"), "hash-a"); await _store.UpdateConfirmationAsync("peer-b", "source-1", new HlcTimestamp(100, 0, "source-1"), "hash-b"); await _store.RemovePeerTrackingAsync("peer-a"); var activePeers = (await _store.GetActiveTrackedPeersAsync()).ToList(); var exported = (await _store.ExportAsync()).ToList(); var peerARows = exported.Where(x => x.PeerNodeId == "peer-a").ToList(); activePeers.ShouldContain("peer-b"); activePeers.ShouldNotContain("peer-a"); peerARows.ShouldNotBeEmpty(); peerARows.All(x => !x.IsActive).ShouldBeTrue(); } /// public void Dispose() { _context?.Dispose(); if (File.Exists(_testDbPath)) { try { File.Delete(_testDbPath); } catch { } } } }