using Microsoft.Extensions.DependencyInjection; using NSubstitute; using ZB.MOM.WW.ScadaBridge.AuditLog.Central; using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories; using SiteEntity = ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites.Site; namespace ZB.MOM.WW.ScadaBridge.AuditLog.Tests.Central; /// /// Unit tests for the production — the central /// reconciliation-singleton collaborator that projects the config-DB /// rows into the targets the /// polls. /// /// /// The enumerator opens a fresh DI scope per /// call (mirroring the per-tick scope pattern in the reconciliation actor) /// because is a SCOPED EF Core service. The tests /// register a substituted repository as a scoped service so the enumerator's /// CreateAsyncScope resolves it and the projection / blank-address /// filtering can be exercised without an MSSQL container. /// public class SiteEnumeratorTests { private static SiteEntity SiteWith(string identifier, string? grpcNodeA, string? grpcNodeB = null) { var site = new SiteEntity($"Display {identifier}", identifier) { GrpcNodeAAddress = grpcNodeA, GrpcNodeBAddress = grpcNodeB, }; return site; } private static IServiceProvider BuildProvider(ISiteRepository repository) { var services = new ServiceCollection(); // Scoped to match the production lifetime (EF Core); the enumerator // must open a scope to resolve it. services.AddScoped(_ => repository); return services.BuildServiceProvider(); } [Fact] public async Task EnumerateAsync_ProjectsSitesWithNodeAAddress_AndSkipsBlankOnes() { var repository = Substitute.For(); repository.GetAllSitesAsync(Arg.Any()).Returns(new List { SiteWith("site-a", "http://site-a:8083"), SiteWith("site-b", grpcNodeA: " "), // blank NodeA -> skipped }); var enumerator = new SiteEnumerator(BuildProvider(repository)); var result = await enumerator.EnumerateAsync(); var entry = Assert.Single(result); Assert.Equal("site-a", entry.SiteId); Assert.Equal("http://site-a:8083", entry.GrpcEndpoint); } [Fact] public async Task EnumerateAsync_SkipsNullNodeAAddress() { var repository = Substitute.For(); repository.GetAllSitesAsync(Arg.Any()).Returns(new List { SiteWith("site-null", grpcNodeA: null), }); var enumerator = new SiteEnumerator(BuildProvider(repository)); var result = await enumerator.EnumerateAsync(); Assert.Empty(result); } [Fact] public async Task EnumerateAsync_ReturnsEmpty_WhenNoSites() { var repository = Substitute.For(); repository.GetAllSitesAsync(Arg.Any()).Returns(new List()); var enumerator = new SiteEnumerator(BuildProvider(repository)); var result = await enumerator.EnumerateAsync(); Assert.Empty(result); } }