test: preserve server SDK 2.0 value security and history contracts

This commit is contained in:
Joseph Doherty
2026-11-10 10:37:52 -05:00
parent ac5e9d84f0
commit 9cddc9306b
45 changed files with 1215 additions and 641 deletions
@@ -62,11 +62,11 @@ public sealed class NodeManagerKeyUsabilityGateTests : IDisposable
var nodeId = Writable(nm, "Plant/Modbus/dev1/Setpoint");
var identity = KeyIdentity(OpcUaDataPlaneRoles.WriteOperate);
Write(nm, identity, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good);
Write(nm, identity, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good.Code);
clock.Advance(TimeSpan.FromMinutes(20));
Write(nm, identity, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied);
Write(nm, identity, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied.Code);
gateway.Writes.Count.ShouldBe(1);
state.Current.GenerationId.ShouldBe(1); // no deployment moved anything — only the clock did
}
@@ -90,13 +90,13 @@ public sealed class NodeManagerKeyUsabilityGateTests : IDisposable
var nodeId = Writable(nm, "Plant/Modbus/dev1/Setpoint");
var identity = KeyIdentity(OpcUaDataPlaneRoles.WriteOperate);
Write(nm, identity, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good);
Write(nm, identity, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good.Code);
var disabled = Key(expiresUtc: null);
disabled.Enabled = false;
state.Apply(Cluster, "rev-2", [disabled], [], NoScopes);
Write(nm, identity, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied);
Write(nm, identity, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied.Code);
gateway.Writes.Count.ShouldBe(1);
}
finally { await host.DisposeAsync(); }
@@ -124,9 +124,9 @@ public sealed class NodeManagerKeyUsabilityGateTests : IDisposable
new AnonymousIdentityToken(), [OpcUaDataPlaneRoles.WriteOperate],
OpcUaPrincipals.Anonymous, apiKeyId: null);
Write(nm, anonymous, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good);
Write(nm, anonymous, nodeId, 1f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good.Code);
clock.Advance(TimeSpan.FromDays(400));
Write(nm, anonymous, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good);
Write(nm, anonymous, nodeId, 2f)[0].StatusCode.Code.ShouldBe(StatusCodes.Good.Code);
gateway.Writes.Count.ShouldBe(2);
}
@@ -158,13 +158,13 @@ public sealed class NodeManagerKeyUsabilityGateTests : IDisposable
UserIdentity = KeyIdentity(OpcUaDataPlaneRoles.AlarmAck),
};
condition.OnAcknowledge!(ctx, condition, Guid.NewGuid().ToByteArray(), new LocalizedText("1"))
condition.OnAcknowledge!(ctx, condition, ByteString.From(Guid.NewGuid().ToByteArray()), new LocalizedText("1"))
.ShouldBe(ServiceResult.Good);
clock.Advance(TimeSpan.FromMinutes(20));
condition.OnAcknowledge!(ctx, condition, Guid.NewGuid().ToByteArray(), new LocalizedText("2"))
.StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied);
condition.OnAcknowledge!(ctx, condition, ByteString.From(Guid.NewGuid().ToByteArray()), new LocalizedText("2"))
.StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied.Code);
routed.Count.ShouldBe(1);
}
finally { await host.DisposeAsync(); }
@@ -217,14 +217,14 @@ public sealed class NodeManagerKeyUsabilityGateTests : IDisposable
OtOpcUaNodeManager nm, IUserIdentity identity, NodeId nodeId, object value)
{
var context = new OperationContext(
new RequestHeader(), secureChannelContext: null, RequestType.Write, identity);
new RequestHeader(), secureChannelContext: null, RequestType.Write, requestLifetime: RequestLifetime.None, identity);
var writes = new List<WriteValue>
{
new()
{
NodeId = nodeId,
AttributeId = Attributes.Value,
Value = new DataValue(new Variant(value)) { StatusCode = StatusCodes.Good },
Value = new DataValue(SdkValueBoundary.FromClr(value), StatusCodes.Good),
},
};
var errors = Enumerable.Repeat<ServiceResult>(null!, writes.Count).ToList();