using Microsoft.EntityFrameworkCore; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Admin.Services; using ZB.MOM.WW.OtOpcUa.Configuration; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Configuration.Enums; namespace ZB.MOM.WW.OtOpcUa.Admin.Tests; [Trait("Category", "Unit")] public sealed class ClusterNodeServiceTests { [Fact] public void IsStale_NullLastSeen_Returns_True() { var node = NewNode("A", RedundancyRole.Primary, lastSeenAt: null); ClusterNodeService.IsStale(node).ShouldBeTrue(); } [Fact] public void IsStale_RecentLastSeen_Returns_False() { var node = NewNode("A", RedundancyRole.Primary, lastSeenAt: DateTime.UtcNow.AddSeconds(-5)); ClusterNodeService.IsStale(node).ShouldBeFalse(); } [Fact] public void IsStale_Old_LastSeen_Returns_True() { var node = NewNode("A", RedundancyRole.Primary, lastSeenAt: DateTime.UtcNow - ClusterNodeService.StaleThreshold - TimeSpan.FromSeconds(1)); ClusterNodeService.IsStale(node).ShouldBeTrue(); } [Fact] public async Task ListByClusterAsync_OrdersByServiceLevelBase_Descending_Then_NodeId() { using var ctx = NewContext(); ctx.ClusterNodes.AddRange( NewNode("B-low", RedundancyRole.Secondary, serviceLevelBase: 150, clusterId: "c1"), NewNode("A-high", RedundancyRole.Primary, serviceLevelBase: 200, clusterId: "c1"), NewNode("other-cluster", RedundancyRole.Primary, serviceLevelBase: 200, clusterId: "c2")); await ctx.SaveChangesAsync(); var svc = new ClusterNodeService(ctx); var rows = await svc.ListByClusterAsync("c1", CancellationToken.None); rows.Count.ShouldBe(2); rows[0].NodeId.ShouldBe("A-high"); // higher ServiceLevelBase first rows[1].NodeId.ShouldBe("B-low"); } private static ClusterNode NewNode( string nodeId, RedundancyRole role, DateTime? lastSeenAt = null, int serviceLevelBase = 200, string clusterId = "c1") => new() { NodeId = nodeId, ClusterId = clusterId, RedundancyRole = role, Host = $"{nodeId}.example", ApplicationUri = $"urn:{nodeId}", ServiceLevelBase = (byte)serviceLevelBase, LastSeenAt = lastSeenAt, CreatedBy = "test", }; private static OtOpcUaConfigDbContext NewContext() { var opts = new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; return new OtOpcUaConfigDbContext(opts); } }