using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
///
/// Verifies the bulk path: valid rows insert, rows
/// whose MachineCode already exists (in the DB or earlier in the same batch) are skipped, and rows
/// referencing an unknown UNS line are reported as errors. (v3: equipment no longer binds a driver, so
/// the old DriverInstanceId column and its decision-#122 cluster guard — and their tests — are gone.)
///
[Trait("Category", "Unit")]
public sealed class UnsTreeServiceImportTests
{
private static (UnsTreeService Service, string DbName) Fresh()
{
var dbName = $"uns-import-{Guid.NewGuid():N}";
return (new UnsTreeService(UnsTreeTestDb.Factory(dbName)), dbName);
}
/// Seeds an area → line (id LINE-1) under .
private static void SeedLine(string dbName, string cluster)
{
using var db = UnsTreeTestDb.CreateNamed(dbName);
db.UnsAreas.Add(new UnsArea { UnsAreaId = "AREA-1", ClusterId = cluster, Name = "a" });
db.UnsLines.Add(new UnsLine { UnsLineId = "LINE-1", UnsAreaId = "AREA-1", Name = "l" });
db.SaveChanges();
}
private static EquipmentInput Input(string name, string machineCode, string unsLineId) =>
new(name, machineCode, unsLineId,
ZTag: null, SAPID: null, Manufacturer: null, Model: null, SerialNumber: null,
HardwareRevision: null, SoftwareRevision: null, YearOfConstruction: null,
AssetLocation: null, ManufacturerUri: null, DeviceManualUri: null, Enabled: true);
/// Valid rows insert with system-generated EQ- ids and are counted in Inserted.
[Fact]
public async Task Import_inserts_valid_rows()
{
var (service, dbName) = Fresh();
SeedLine(dbName, "MAIN");
var result = await service.ImportEquipmentAsync(
[
Input("machine-1", "mc_1", "LINE-1"),
Input("machine-2", "mc_2", "LINE-1"),
]);
result.Inserted.ShouldBe(2);
result.Skipped.ShouldBe(0);
result.Errors.ShouldBeEmpty();
using var db = UnsTreeTestDb.CreateNamed(dbName);
db.Equipment.Count().ShouldBe(2);
var eq = db.Equipment.Single(e => e.MachineCode == "mc_1");
eq.EquipmentId.ShouldStartWith("EQ-");
eq.EquipmentId.Length.ShouldBe(15); // "EQ-" + 12 hex chars
eq.Name.ShouldBe("machine-1");
eq.UnsLineId.ShouldBe("LINE-1");
eq.Enabled.ShouldBeTrue();
}
///
/// A row whose MachineCode already exists in the DB is skipped (not an error), and a second row
/// later in the batch that reuses an earlier row's MachineCode is also skipped.
///
[Fact]
public async Task Import_skips_duplicate_machinecode()
{
var (service, dbName) = Fresh();
SeedLine(dbName, "MAIN");
// Pre-existing equipment with MachineCode "mc_existing".
await service.CreateEquipmentAsync(Input("machine-x", "mc_existing", "LINE-1"));
var result = await service.ImportEquipmentAsync(
[
Input("machine-1", "mc_existing", "LINE-1"), // dup of DB row → skip
Input("machine-2", "mc_new", "LINE-1"), // inserts
Input("machine-3", "mc_new", "LINE-1"), // dup of in-batch row → skip
]);
result.Inserted.ShouldBe(1);
result.Skipped.ShouldBe(2);
result.Errors.ShouldBeEmpty();
using var db = UnsTreeTestDb.CreateNamed(dbName);
db.Equipment.Count(e => e.MachineCode == "mc_new").ShouldBe(1);
db.Equipment.Count(e => e.MachineCode == "mc_existing").ShouldBe(1);
}
/// A row whose UnsLineId does not exist is reported as an error and not inserted.
[Fact]
public async Task Import_reports_unknown_line()
{
var (service, dbName) = Fresh();
SeedLine(dbName, "MAIN");
var result = await service.ImportEquipmentAsync(
[
Input("machine-1", "mc_1", "LINE-BOGUS"),
Input("machine-2", "mc_2", "LINE-1"),
]);
result.Inserted.ShouldBe(1);
result.Skipped.ShouldBe(0);
result.Errors.Count.ShouldBe(1);
result.Errors[0].ShouldContain("mc_1");
result.Errors[0].ShouldContain("LINE-BOGUS");
using var db = UnsTreeTestDb.CreateNamed(dbName);
db.Equipment.Any(e => e.MachineCode == "mc_1").ShouldBeFalse();
db.Equipment.Any(e => e.MachineCode == "mc_2").ShouldBeTrue();
}
}