131 lines
4.8 KiB
C#
131 lines
4.8 KiB
C#
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;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Admin.Tests;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class UnsServiceMoveTests
|
|
{
|
|
[Fact]
|
|
public async Task LoadSnapshotAsync_ReturnsAllAreasAndLines_WithEquipmentCounts()
|
|
{
|
|
using var ctx = NewContext();
|
|
Seed(ctx, draftId: 1, areas: new[] { "area-1", "area-2" },
|
|
lines: new[] { ("line-a", "area-1"), ("line-b", "area-1"), ("line-c", "area-2") },
|
|
equipmentLines: new[] { "line-a", "line-a", "line-b" });
|
|
var svc = new UnsService(ctx);
|
|
|
|
var snap = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
|
|
snap.Areas.Count.ShouldBe(2);
|
|
snap.Lines.Count.ShouldBe(3);
|
|
snap.FindLine("line-a")!.EquipmentCount.ShouldBe(2);
|
|
snap.FindLine("line-b")!.EquipmentCount.ShouldBe(1);
|
|
snap.FindLine("line-c")!.EquipmentCount.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LoadSnapshotAsync_RevisionToken_IsStable_BetweenTwoReads()
|
|
{
|
|
using var ctx = NewContext();
|
|
Seed(ctx, draftId: 1, areas: new[] { "area-1" }, lines: new[] { ("line-a", "area-1") });
|
|
var svc = new UnsService(ctx);
|
|
|
|
var first = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
var second = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
|
|
second.RevisionToken.Matches(first.RevisionToken).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task LoadSnapshotAsync_RevisionToken_Changes_When_LineAdded()
|
|
{
|
|
using var ctx = NewContext();
|
|
Seed(ctx, draftId: 1, areas: new[] { "area-1" }, lines: new[] { ("line-a", "area-1") });
|
|
var svc = new UnsService(ctx);
|
|
|
|
var before = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
await svc.AddLineAsync(1, "area-1", "new-line", null, CancellationToken.None);
|
|
var after = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
|
|
after.RevisionToken.Matches(before.RevisionToken).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MoveLineAsync_WithMatchingToken_Reparents_Line()
|
|
{
|
|
using var ctx = NewContext();
|
|
Seed(ctx, draftId: 1, areas: new[] { "area-1", "area-2" },
|
|
lines: new[] { ("line-a", "area-1") });
|
|
var svc = new UnsService(ctx);
|
|
|
|
var snap = await svc.LoadSnapshotAsync(1, CancellationToken.None);
|
|
await svc.MoveLineAsync(1, snap.RevisionToken, "line-a", "area-2", CancellationToken.None);
|
|
|
|
var moved = await ctx.UnsLines.AsNoTracking().FirstAsync(l => l.UnsLineId == "line-a");
|
|
moved.UnsAreaId.ShouldBe("area-2");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task MoveLineAsync_WithStaleToken_Throws_DraftRevisionConflict()
|
|
{
|
|
using var ctx = NewContext();
|
|
Seed(ctx, draftId: 1, areas: new[] { "area-1", "area-2" },
|
|
lines: new[] { ("line-a", "area-1") });
|
|
var svc = new UnsService(ctx);
|
|
|
|
// Simulate a peer operator's concurrent edit between our preview + commit.
|
|
var stale = new DraftRevisionToken("0000000000000000");
|
|
|
|
await Should.ThrowAsync<DraftRevisionConflictException>(() =>
|
|
svc.MoveLineAsync(1, stale, "line-a", "area-2", CancellationToken.None));
|
|
|
|
var row = await ctx.UnsLines.AsNoTracking().FirstAsync(l => l.UnsLineId == "line-a");
|
|
row.UnsAreaId.ShouldBe("area-1");
|
|
}
|
|
|
|
private static void Seed(OtOpcUaConfigDbContext ctx, long draftId,
|
|
IEnumerable<string> areas,
|
|
IEnumerable<(string line, string area)> lines,
|
|
IEnumerable<string>? equipmentLines = null)
|
|
{
|
|
foreach (var a in areas)
|
|
{
|
|
ctx.UnsAreas.Add(new UnsArea
|
|
{
|
|
GenerationId = draftId, UnsAreaId = a, ClusterId = "c1", Name = a,
|
|
});
|
|
}
|
|
foreach (var (line, area) in lines)
|
|
{
|
|
ctx.UnsLines.Add(new UnsLine
|
|
{
|
|
GenerationId = draftId, UnsLineId = line, UnsAreaId = area, Name = line,
|
|
});
|
|
}
|
|
foreach (var lineId in equipmentLines ?? [])
|
|
{
|
|
ctx.Equipment.Add(new Equipment
|
|
{
|
|
EquipmentRowId = Guid.NewGuid(), GenerationId = draftId,
|
|
EquipmentId = $"EQ-{Guid.NewGuid():N}"[..15],
|
|
EquipmentUuid = Guid.NewGuid(), DriverInstanceId = "drv",
|
|
UnsLineId = lineId, Name = "x", MachineCode = "m",
|
|
});
|
|
}
|
|
ctx.SaveChanges();
|
|
}
|
|
|
|
private static OtOpcUaConfigDbContext NewContext()
|
|
{
|
|
var opts = new DbContextOptionsBuilder<OtOpcUaConfigDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options;
|
|
return new OtOpcUaConfigDbContext(opts);
|
|
}
|
|
}
|