Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.Server.Tests/EquipmentNamespaceContentLoaderTests.cs
Joseph Doherty a25593a9c6 chore: organize solution into module folders (Core/Server/Drivers/Client/Tooling)
Group all 69 projects into category subfolders under src/ and tests/ so the
Rider Solution Explorer mirrors the module structure. Folders: Core, Server,
Drivers (with a nested Driver CLIs subfolder), Client, Tooling.

- Move every project folder on disk with git mv (history preserved as renames).
- Recompute relative paths in 57 .csproj files: cross-category ProjectReferences,
  the lib/ HintPath+None refs in Driver.Historian.Wonderware, and the external
  mxaccessgw refs in Driver.Galaxy and its test project.
- Rebuild ZB.MOM.WW.OtOpcUa.slnx with nested solution folders.
- Re-prefix project paths in functional scripts (e2e, compliance, smoke SQL,
  integration, install).

Build green (0 errors); unit tests pass. Docs left for a separate pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 01:55:28 -04:00

173 lines
6.4 KiB
C#

using Microsoft.EntityFrameworkCore;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Server.OpcUa;
namespace ZB.MOM.WW.OtOpcUa.Server.Tests;
[Trait("Category", "Unit")]
public sealed class EquipmentNamespaceContentLoaderTests : IDisposable
{
private const string DriverId = "galaxy-prod";
private const string OtherDriverId = "galaxy-dev";
private const long Gen = 5;
private readonly OtOpcUaConfigDbContext _db;
private readonly EquipmentNamespaceContentLoader _loader;
public EquipmentNamespaceContentLoaderTests()
{
var options = new DbContextOptionsBuilder<OtOpcUaConfigDbContext>()
.UseInMemoryDatabase($"eq-content-loader-{Guid.NewGuid():N}")
.Options;
_db = new OtOpcUaConfigDbContext(options);
_loader = new EquipmentNamespaceContentLoader(_db);
}
public void Dispose() => _db.Dispose();
[Fact]
public async Task Returns_Null_When_Driver_Has_No_Equipment_At_Generation()
{
var result = await _loader.LoadAsync(DriverId, Gen, CancellationToken.None);
result.ShouldBeNull();
}
[Fact]
public async Task Loads_Areas_Lines_Equipment_Tags_For_Driver_At_Generation()
{
SeedBaseline();
var result = await _loader.LoadAsync(DriverId, Gen, CancellationToken.None);
result.ShouldNotBeNull();
result!.Areas.ShouldHaveSingleItem().UnsAreaId.ShouldBe("area-1");
result.Lines.ShouldHaveSingleItem().UnsLineId.ShouldBe("line-a");
result.Equipment.Count.ShouldBe(2);
result.Equipment.ShouldContain(e => e.EquipmentId == "eq-oven-3");
result.Equipment.ShouldContain(e => e.EquipmentId == "eq-press-7");
result.Tags.Count.ShouldBe(2);
result.Tags.ShouldContain(t => t.TagId == "tag-temp");
result.Tags.ShouldContain(t => t.TagId == "tag-press");
}
[Fact]
public async Task Skips_Other_Drivers_Equipment()
{
SeedBaseline();
// Equipment + Tag owned by a different driver at the same generation — must not leak.
_db.Equipment.Add(new Equipment
{
EquipmentRowId = Guid.NewGuid(), GenerationId = Gen,
EquipmentId = "eq-other", EquipmentUuid = Guid.NewGuid(),
DriverInstanceId = OtherDriverId, UnsLineId = "line-a", Name = "other-eq",
MachineCode = "MC-other",
});
_db.Tags.Add(new Tag
{
TagRowId = Guid.NewGuid(), GenerationId = Gen, TagId = "tag-other",
DriverInstanceId = OtherDriverId, EquipmentId = "eq-other",
Name = "OtherTag", DataType = "Int32",
AccessLevel = TagAccessLevel.ReadWrite, TagConfig = "plcaddr-other",
});
await _db.SaveChangesAsync();
var result = await _loader.LoadAsync(DriverId, Gen, CancellationToken.None);
result.ShouldNotBeNull();
result!.Equipment.ShouldNotContain(e => e.EquipmentId == "eq-other");
result.Tags.ShouldNotContain(t => t.TagId == "tag-other");
}
[Fact]
public async Task Skips_Other_Generations()
{
SeedBaseline();
// Same driver, different generation — must not leak in. Walker consumes one sealed
// generation per bootstrap per decision #148.
_db.Equipment.Add(new Equipment
{
EquipmentRowId = Guid.NewGuid(), GenerationId = 99,
EquipmentId = "eq-futuristic", EquipmentUuid = Guid.NewGuid(),
DriverInstanceId = DriverId, UnsLineId = "line-a", Name = "futuristic",
MachineCode = "MC-fut",
});
await _db.SaveChangesAsync();
var result = await _loader.LoadAsync(DriverId, Gen, CancellationToken.None);
result.ShouldNotBeNull();
result!.Equipment.ShouldNotContain(e => e.EquipmentId == "eq-futuristic");
}
[Fact]
public async Task Skips_Disabled_Equipment()
{
SeedBaseline();
_db.Equipment.Add(new Equipment
{
EquipmentRowId = Guid.NewGuid(), GenerationId = Gen,
EquipmentId = "eq-disabled", EquipmentUuid = Guid.NewGuid(),
DriverInstanceId = DriverId, UnsLineId = "line-a", Name = "disabled-eq",
MachineCode = "MC-dis", Enabled = false,
});
await _db.SaveChangesAsync();
var result = await _loader.LoadAsync(DriverId, Gen, CancellationToken.None);
result.ShouldNotBeNull();
result!.Equipment.ShouldNotContain(e => e.EquipmentId == "eq-disabled");
}
private void SeedBaseline()
{
_db.UnsAreas.Add(new UnsArea
{
UnsAreaRowId = Guid.NewGuid(), UnsAreaId = "area-1", ClusterId = "c-warsaw",
Name = "warsaw", GenerationId = Gen,
});
_db.UnsLines.Add(new UnsLine
{
UnsLineRowId = Guid.NewGuid(), UnsLineId = "line-a", UnsAreaId = "area-1",
Name = "line-a", GenerationId = Gen,
});
_db.Equipment.AddRange(
new Equipment
{
EquipmentRowId = Guid.NewGuid(), GenerationId = Gen,
EquipmentId = "eq-oven-3", EquipmentUuid = Guid.NewGuid(),
DriverInstanceId = DriverId, UnsLineId = "line-a", Name = "oven-3",
MachineCode = "MC-oven-3",
},
new Equipment
{
EquipmentRowId = Guid.NewGuid(), GenerationId = Gen,
EquipmentId = "eq-press-7", EquipmentUuid = Guid.NewGuid(),
DriverInstanceId = DriverId, UnsLineId = "line-a", Name = "press-7",
MachineCode = "MC-press-7",
});
_db.Tags.AddRange(
new Tag
{
TagRowId = Guid.NewGuid(), GenerationId = Gen, TagId = "tag-temp",
DriverInstanceId = DriverId, EquipmentId = "eq-oven-3",
Name = "Temperature", DataType = "Int32",
AccessLevel = TagAccessLevel.ReadWrite, TagConfig = "plcaddr-temperature",
},
new Tag
{
TagRowId = Guid.NewGuid(), GenerationId = Gen, TagId = "tag-press",
DriverInstanceId = DriverId, EquipmentId = "eq-press-7",
Name = "PressTemp", DataType = "Int32",
AccessLevel = TagAccessLevel.ReadWrite, TagConfig = "plcaddr-press-temp",
});
_db.SaveChanges();
}
}