using Microsoft.EntityFrameworkCore; using ZB.MOM.WW.ScadaBridge.Commons.Entities.SecuredWrites; using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase; using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories; using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Migrations; using Xunit; namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests; /// /// Integration tests for (M7 OPC UA / MxGateway /// UX, Task T14b). Uses the same as the migration /// tests so the EF reads/writes execute against the real PendingSecuredWrites /// schema produced by the migration. Each test mints a fresh per-test /// SiteId/Status suffix so tests neither collide nor require teardown. /// Tests pair with Skip.IfNot(...) so the /// runner reports them as Skipped (not Passed) when MSSQL is unreachable. /// public class SecuredWriteRepositoryTests : IClassFixture { private readonly MsSqlMigrationFixture _fixture; public SecuredWriteRepositoryTests(MsSqlMigrationFixture fixture) { _fixture = fixture; } [SkippableFact] public async Task Lifecycle_AddGetUpdateQuery_RoundTrips() { Skip.IfNot(_fixture.Available, _fixture.SkipReason); var siteId = NewSiteId(); // Add a Pending row. await using var context = CreateContext(); var repo = new SecuredWriteRepository(context); var pending = NewRow(siteId, status: "Pending"); var id = await repo.AddAsync(pending); Assert.True(id > 0, "AddAsync should return the store-generated identity."); // Get returns it. await using (var readContext = CreateContext()) { var loaded = await new SecuredWriteRepository(readContext).GetAsync(id); Assert.NotNull(loaded); Assert.Equal(siteId, loaded!.SiteId); Assert.Equal("Pending", loaded.Status); Assert.Equal("conn-1", loaded.ConnectionName); Assert.Equal("Plant.Tank.Setpoint", loaded.TagPath); Assert.Equal("op.alice", loaded.OperatorUser); Assert.Null(loaded.VerifierUser); Assert.Null(loaded.DecidedAtUtc); } // Update to Approved. await using (var updateContext = CreateContext()) { var updateRepo = new SecuredWriteRepository(updateContext); var toApprove = await updateRepo.GetAsync(id); Assert.NotNull(toApprove); toApprove!.Status = "Approved"; toApprove.VerifierUser = "ver.bob"; toApprove.VerifierComment = "looks good"; toApprove.DecidedAtUtc = DateTime.UtcNow; await updateRepo.UpdateAsync(toApprove); } // Get reflects the update. await using (var verifyContext = CreateContext()) { var reloaded = await new SecuredWriteRepository(verifyContext).GetAsync(id); Assert.NotNull(reloaded); Assert.Equal("Approved", reloaded!.Status); Assert.Equal("ver.bob", reloaded.VerifierUser); Assert.Equal("looks good", reloaded.VerifierComment); Assert.NotNull(reloaded.DecidedAtUtc); } // Query by status returns it. await using (var queryContext = CreateContext()) { var queryRepo = new SecuredWriteRepository(queryContext); var byStatus = await queryRepo.QueryAsync(status: "Approved", siteId: siteId, skip: 0, take: 50); Assert.Contains(byStatus, p => p.Id == id); Assert.All(byStatus, p => Assert.Equal("Approved", p.Status)); // A non-matching status filter excludes the row. var pendingPage = await queryRepo.QueryAsync(status: "Pending", siteId: siteId, skip: 0, take: 50); Assert.DoesNotContain(pendingPage, p => p.Id == id); } } [SkippableFact] public async Task QueryAsync_NullFilters_MatchEveryStatusForSite() { Skip.IfNot(_fixture.Available, _fixture.SkipReason); var siteId = NewSiteId(); await using var context = CreateContext(); var repo = new SecuredWriteRepository(context); var pendingId = await repo.AddAsync(NewRow(siteId, status: "Pending")); var executedId = await repo.AddAsync(NewRow(siteId, status: "Executed")); var all = await repo.QueryAsync(status: null, siteId: siteId, skip: 0, take: 50); Assert.Contains(all, p => p.Id == pendingId); Assert.Contains(all, p => p.Id == executedId); } // --- helpers ------------------------------------------------------------ private ScadaBridgeDbContext CreateContext() { var options = new DbContextOptionsBuilder() .UseSqlServer(_fixture.ConnectionString) .Options; return new ScadaBridgeDbContext(options); } private static string NewSiteId() => "site-t14b-" + Guid.NewGuid().ToString("N").Substring(0, 8); private static PendingSecuredWrite NewRow(string siteId, string status) => new() { SiteId = siteId, ConnectionName = "conn-1", TagPath = "Plant.Tank.Setpoint", ValueJson = "42.5", ValueType = "Double", Status = status, OperatorUser = "op.alice", OperatorComment = "raise setpoint", SubmittedAtUtc = DateTime.UtcNow, }; }