feat(adminui): CreateAliasTagAsync/UpdateAliasTagAsync + Galaxy-gateway guard
This commit is contained in:
@@ -207,6 +207,211 @@ public sealed class UnsTreeServiceAliasTagTests
|
||||
alias.IsAlias.ShouldBeTrue();
|
||||
alias.Source.ShouldBeNull();
|
||||
}
|
||||
|
||||
// ---- Write-side: CreateAliasTagAsync / UpdateAliasTagAsync (T5) ----
|
||||
|
||||
private const string SecondClusterId = "ALT";
|
||||
private const string SecondGatewayDriverId = "DRV-GALAXY-ALT";
|
||||
|
||||
/// <summary>
|
||||
/// Seeds a second cluster (no UNS hierarchy needed) carrying its own GalaxyMxGateway driver, so a
|
||||
/// cross-cluster guard test can reference a gateway that is NOT in the equipment's cluster.
|
||||
/// </summary>
|
||||
private static void SeedSecondClusterGateway(string dbName)
|
||||
{
|
||||
using var db = UnsTreeTestDb.CreateNamed(dbName);
|
||||
|
||||
db.ServerClusters.Add(new ServerCluster
|
||||
{
|
||||
ClusterId = SecondClusterId,
|
||||
Name = "Alt",
|
||||
Enterprise = "zb",
|
||||
Site = "krakow",
|
||||
RedundancyMode = RedundancyMode.None,
|
||||
CreatedBy = "test",
|
||||
});
|
||||
db.Namespaces.Add(new Namespace
|
||||
{
|
||||
NamespaceId = "NS-SP-ALT",
|
||||
ClusterId = SecondClusterId,
|
||||
Kind = NamespaceKind.SystemPlatform,
|
||||
NamespaceUri = "urn:zb:sp:alt",
|
||||
});
|
||||
db.DriverInstances.Add(new DriverInstance
|
||||
{
|
||||
DriverInstanceId = SecondGatewayDriverId,
|
||||
ClusterId = SecondClusterId,
|
||||
NamespaceId = "NS-SP-ALT",
|
||||
Name = "alt galaxy gateway",
|
||||
DriverType = "GalaxyMxGateway",
|
||||
DriverConfig = "{}",
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
private static AliasTagInput Input(
|
||||
string tagId = "TAG-NEW-ALIAS",
|
||||
string name = "speed-alias",
|
||||
string driverInstanceId = GatewayDriverId,
|
||||
string dataType = "Float",
|
||||
TagAccessLevel accessLevel = TagAccessLevel.Read,
|
||||
string fullName = "TestMachine_020.Speed") =>
|
||||
new(tagId, name, driverInstanceId, dataType, accessLevel, fullName);
|
||||
|
||||
/// <summary>
|
||||
/// A valid create persists a Galaxy-gateway-bound, equipment-scoped alias Tag: <c>FolderPath</c> is
|
||||
/// null, the supplied AccessLevel is honoured, and the TagConfig is the <c>{"FullName":…}</c> envelope.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task CreateAliasTag_persists_galaxy_alias_row()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.CreateAliasTagAsync(
|
||||
EquipmentId, Input(accessLevel: TagAccessLevel.ReadWrite, fullName: "TestMachine_020.Speed"));
|
||||
|
||||
result.Ok.ShouldBeTrue();
|
||||
result.Error.ShouldBeNull();
|
||||
|
||||
using var db = UnsTreeTestDb.CreateNamed(dbName);
|
||||
var tag = db.Tags.Single(t => t.TagId == "TAG-NEW-ALIAS");
|
||||
tag.DriverInstanceId.ShouldBe(GatewayDriverId);
|
||||
tag.EquipmentId.ShouldBe(EquipmentId);
|
||||
tag.FolderPath.ShouldBeNull();
|
||||
tag.AccessLevel.ShouldBe(TagAccessLevel.ReadWrite);
|
||||
tag.Name.ShouldBe("speed-alias");
|
||||
tag.DataType.ShouldBe("Float");
|
||||
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(tag.TagConfig);
|
||||
doc.RootElement.GetProperty("FullName").GetString().ShouldBe("TestMachine_020.Speed");
|
||||
tag.TagConfig.ShouldContain("FullName");
|
||||
tag.TagConfig.ShouldContain("TestMachine_020.Speed");
|
||||
}
|
||||
|
||||
/// <summary>An empty/whitespace Galaxy reference is rejected before anything is written.</summary>
|
||||
[Fact]
|
||||
public async Task CreateAliasTag_rejects_empty_reference()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.CreateAliasTagAsync(EquipmentId, Input(fullName: " "));
|
||||
|
||||
result.Ok.ShouldBeFalse();
|
||||
result.Error!.ShouldContain("reference");
|
||||
|
||||
using var db = UnsTreeTestDb.CreateNamed(dbName);
|
||||
db.Tags.Any(t => t.TagId == "TAG-NEW-ALIAS").ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>Binding the alias to a non-Galaxy driver (the cluster's Modbus driver) is rejected.</summary>
|
||||
[Fact]
|
||||
public async Task CreateAliasTag_rejects_non_galaxy_driver()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.CreateAliasTagAsync(
|
||||
EquipmentId, Input(driverInstanceId: ModbusDriverId));
|
||||
|
||||
result.Ok.ShouldBeFalse();
|
||||
result.Error!.ShouldContain("Galaxy gateway");
|
||||
}
|
||||
|
||||
/// <summary>A Galaxy gateway in a different cluster than the equipment is rejected (cluster mismatch).</summary>
|
||||
[Fact]
|
||||
public async Task CreateAliasTag_rejects_cross_cluster_gateway()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
SeedSecondClusterGateway(dbName);
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.CreateAliasTagAsync(
|
||||
EquipmentId, Input(driverInstanceId: SecondGatewayDriverId));
|
||||
|
||||
result.Ok.ShouldBeFalse();
|
||||
result.Error!.ShouldContain("cluster");
|
||||
}
|
||||
|
||||
/// <summary>A name already used by another tag on the equipment is rejected.</summary>
|
||||
[Fact]
|
||||
public async Task CreateAliasTag_rejects_duplicate_name()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
|
||||
using (var db = UnsTreeTestDb.CreateNamed(dbName))
|
||||
{
|
||||
db.Tags.Add(new Tag
|
||||
{
|
||||
TagId = "TAG-EXISTING",
|
||||
DriverInstanceId = ModbusDriverId,
|
||||
EquipmentId = EquipmentId,
|
||||
Name = "speed-alias",
|
||||
DataType = "Int32",
|
||||
AccessLevel = TagAccessLevel.Read,
|
||||
TagConfig = "{}",
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.CreateAliasTagAsync(EquipmentId, Input(name: "speed-alias"));
|
||||
|
||||
result.Ok.ShouldBeFalse();
|
||||
result.Error!.ShouldContain("speed-alias");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update changes the alias's name, data type, access level, and Galaxy reference, returning Ok and
|
||||
/// persisting the new values and the refreshed <c>{"FullName":…}</c> TagConfig.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task UpdateAliasTag_changes_fields_and_reference()
|
||||
{
|
||||
var dbName = SeedCluster();
|
||||
byte[] rowVersion;
|
||||
|
||||
using (var db = UnsTreeTestDb.CreateNamed(dbName))
|
||||
{
|
||||
db.Tags.Add(new Tag
|
||||
{
|
||||
TagId = "TAG-UPD-ALIAS",
|
||||
DriverInstanceId = GatewayDriverId,
|
||||
EquipmentId = EquipmentId,
|
||||
Name = "old-name",
|
||||
DataType = "Float",
|
||||
AccessLevel = TagAccessLevel.Read,
|
||||
TagConfig = "{\"FullName\":\"TestMachine_020.Speed\"}",
|
||||
});
|
||||
db.SaveChanges();
|
||||
rowVersion = db.Tags.Single(t => t.TagId == "TAG-UPD-ALIAS").RowVersion;
|
||||
}
|
||||
|
||||
var service = new UnsTreeService(UnsTreeTestDb.Factory(dbName));
|
||||
|
||||
var result = await service.UpdateAliasTagAsync(
|
||||
"TAG-UPD-ALIAS",
|
||||
Input(tagId: "TAG-UPD-ALIAS", name: "new-name", dataType: "Double",
|
||||
accessLevel: TagAccessLevel.ReadWrite, fullName: "TestMachine_020.Setpoint"),
|
||||
rowVersion);
|
||||
|
||||
result.Ok.ShouldBeTrue();
|
||||
result.Error.ShouldBeNull();
|
||||
|
||||
using var verify = UnsTreeTestDb.CreateNamed(dbName);
|
||||
var tag = verify.Tags.Single(t => t.TagId == "TAG-UPD-ALIAS");
|
||||
tag.Name.ShouldBe("new-name");
|
||||
tag.DataType.ShouldBe("Double");
|
||||
tag.AccessLevel.ShouldBe(TagAccessLevel.ReadWrite);
|
||||
tag.EquipmentId.ShouldBe(EquipmentId);
|
||||
tag.FolderPath.ShouldBeNull();
|
||||
|
||||
using var doc = System.Text.Json.JsonDocument.Parse(tag.TagConfig);
|
||||
doc.RootElement.GetProperty("FullName").GetString().ShouldBe("TestMachine_020.Setpoint");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Small Shouldly-style helper for "exactly one match" assertions used by these tests.</summary>
|
||||
|
||||
Reference in New Issue
Block a user