test: preserve server SDK 2.0 value security and history contracts
This commit is contained in:
+22
-24
@@ -41,19 +41,19 @@ public sealed class NodeManagerHistoryReadEventsProjectionTests : IDisposable
|
||||
|
||||
var history = Read(server, nm, notifier, Select("EventId", "EventType", "ConditionName", "SourceNode"), where: null);
|
||||
|
||||
var rows = history.Events.ToDictionary(e => System.Text.Encoding.UTF8.GetString((byte[])e.EventFields[0].Value));
|
||||
rows["e-limit"].EventFields[1].Value.ShouldBe(ObjectTypeIds.LimitAlarmType);
|
||||
rows["e-limit"].EventFields[2].Value.ShouldBe("HighTemp");
|
||||
rows["e-limit"].EventFields[3].Value.ShouldBe(condition, "the alarm id resolves to the materialised condition — the live events' SourceNode");
|
||||
var rows = history.Events.ToArray()!.ToDictionary(e => System.Text.Encoding.UTF8.GetString((byte[])SdkValueBoundary.ToClr(e.EventFields[0])!));
|
||||
SdkValueBoundary.ToClr(rows["e-limit"].EventFields[1])!.ShouldBe(ObjectTypeIds.LimitAlarmType);
|
||||
SdkValueBoundary.ToClr(rows["e-limit"].EventFields[2])!.ShouldBe("HighTemp");
|
||||
SdkValueBoundary.ToClr(rows["e-limit"].EventFields[3])!.ShouldBe(condition, "the alarm id resolves to the materialised condition — the live events' SourceNode");
|
||||
|
||||
rows["e-offnormal"].EventFields[1].Value.ShouldBe(ObjectTypeIds.OffNormalAlarmType);
|
||||
rows["e-offnormal"].EventFields[3].Value.ShouldBeNull("no condition is materialised for that alarm id");
|
||||
SdkValueBoundary.ToClr(rows["e-offnormal"].EventFields[1])!.ShouldBe(ObjectTypeIds.OffNormalAlarmType);
|
||||
SdkValueBoundary.ToClr(rows["e-offnormal"].EventFields[3])!.ShouldBeNull("no condition is materialised for that alarm id");
|
||||
|
||||
// No stored type: still an alarm condition (every historical row is alarm history); an unrecognised or non-event
|
||||
// type name is not passed through as a non-event type.
|
||||
rows["e-untyped"].EventFields[1].Value.ShouldBe(ObjectTypeIds.AlarmConditionType);
|
||||
rows["e-untyped"].EventFields[2].Value.ShouldBeNull();
|
||||
rows["e-bogus"].EventFields[1].Value.ShouldBe(ObjectTypeIds.AlarmConditionType);
|
||||
SdkValueBoundary.ToClr(rows["e-untyped"].EventFields[1])!.ShouldBe(ObjectTypeIds.AlarmConditionType);
|
||||
SdkValueBoundary.ToClr(rows["e-untyped"].EventFields[2])!.ShouldBeNull();
|
||||
SdkValueBoundary.ToClr(rows["e-bogus"].EventFields[1])!.ShouldBe(ObjectTypeIds.AlarmConditionType);
|
||||
|
||||
await host.DisposeAsync();
|
||||
}
|
||||
@@ -79,9 +79,7 @@ public sealed class NodeManagerHistoryReadEventsProjectionTests : IDisposable
|
||||
{
|
||||
var (host, server, nm, notifier) = await BootAsync();
|
||||
var where = new ContentFilter();
|
||||
where.Push(FilterOperator.GreaterThan,
|
||||
new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("Severity")) { AttributeId = Attributes.Value },
|
||||
new LiteralOperand(new Variant((ushort)600)));
|
||||
where.Push(FilterOperator.GreaterThan, Variant.FromStructure(new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName("Severity")) { AttributeId = Attributes.Value }), Variant.FromStructure(new LiteralOperand(new Variant((ushort)600))));
|
||||
|
||||
Ids(Read(server, nm, notifier, Select("EventId"), where)).ShouldBe(["e-limit", "e-bogus"]);
|
||||
await host.DisposeAsync();
|
||||
@@ -100,13 +98,13 @@ public sealed class NodeManagerHistoryReadEventsProjectionTests : IDisposable
|
||||
switch (fault)
|
||||
{
|
||||
case "unknown operator":
|
||||
where.Push((FilterOperator)99, new LiteralOperand(new Variant(1)), new LiteralOperand(new Variant(1)));
|
||||
where.Push((FilterOperator)99, Variant.FromStructure(new LiteralOperand(new Variant(1))), Variant.FromStructure(new LiteralOperand(new Variant(1))));
|
||||
break;
|
||||
case "self-referencing element":
|
||||
where.Push(FilterOperator.Not, new ElementOperand(0));
|
||||
where.Push(FilterOperator.Not, Variant.FromStructure(new ElementOperand(0)));
|
||||
break;
|
||||
default:
|
||||
where.Push(FilterOperator.Equals, new LiteralOperand(new Variant(1)));
|
||||
where.Push(FilterOperator.Equals, Variant.FromStructure(new LiteralOperand(new Variant(1))));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -115,39 +113,39 @@ public sealed class NodeManagerHistoryReadEventsProjectionTests : IDisposable
|
||||
|
||||
// Positive control: the same read with a valid where-clause is served.
|
||||
var valid = new ContentFilter();
|
||||
valid.Push(FilterOperator.Equals, new LiteralOperand(new Variant(1)), new LiteralOperand(new Variant(1)));
|
||||
valid.Push(FilterOperator.Equals, Variant.FromStructure(new LiteralOperand(new Variant(1))), Variant.FromStructure(new LiteralOperand(new Variant(1))));
|
||||
StatusCode.IsGood(ReadStatus(server, nm, notifier, Select("EventId"), valid).Status).ShouldBeTrue();
|
||||
await host.DisposeAsync();
|
||||
}
|
||||
|
||||
private static string[] Ids(HistoryEvent h) =>
|
||||
h.Events.Select(e => System.Text.Encoding.UTF8.GetString((byte[])e.EventFields[0].Value)).ToArray();
|
||||
h.Events.ToArray()!.Select(e => System.Text.Encoding.UTF8.GetString((byte[])SdkValueBoundary.ToClr(e.EventFields[0])!)).ToArray();
|
||||
|
||||
private static ContentFilter OfType(NodeId type)
|
||||
{
|
||||
var f = new ContentFilter();
|
||||
f.Push(FilterOperator.OfType, new LiteralOperand(new Variant(type)));
|
||||
f.Push(FilterOperator.OfType, Variant.FromStructure(new LiteralOperand(new Variant(type))));
|
||||
return f;
|
||||
}
|
||||
|
||||
private static SimpleAttributeOperandCollection Select(params string[] leaves)
|
||||
private static List<SimpleAttributeOperand> Select(params string[] leaves)
|
||||
{
|
||||
var c = new SimpleAttributeOperandCollection();
|
||||
var c = new List<SimpleAttributeOperand>();
|
||||
foreach (var leaf in leaves)
|
||||
c.Add(new SimpleAttributeOperand(ObjectTypeIds.BaseEventType, new QualifiedName(leaf)) { AttributeId = Attributes.Value });
|
||||
return c;
|
||||
}
|
||||
|
||||
private static HistoryEvent Read(OtOpcUaSdkServer server, OtOpcUaNodeManager nm, NodeId notifier,
|
||||
SimpleAttributeOperandCollection select, ContentFilter? where)
|
||||
List<SimpleAttributeOperand> select, ContentFilter? where)
|
||||
{
|
||||
var (status, result) = ReadStatus(server, nm, notifier, select, where);
|
||||
StatusCode.IsGood(status).ShouldBeTrue($"HistoryRead failed: {status}");
|
||||
return (HistoryEvent)ExtensionObject.ToEncodeable(result.HistoryData);
|
||||
return (HistoryEvent)ExtensionObject.ToEncodeable(result.HistoryData)!;
|
||||
}
|
||||
|
||||
private static (StatusCode Status, SdkHistoryReadResult Result) ReadStatus(OtOpcUaSdkServer server, OtOpcUaNodeManager nm,
|
||||
NodeId notifier, SimpleAttributeOperandCollection select, ContentFilter? where)
|
||||
NodeId notifier, List<SimpleAttributeOperand> select, ContentFilter? where)
|
||||
{
|
||||
var details = new ReadEventDetails
|
||||
{
|
||||
@@ -156,7 +154,7 @@ public sealed class NodeManagerHistoryReadEventsProjectionTests : IDisposable
|
||||
NumValuesPerNode = 100,
|
||||
Filter = new EventFilter { SelectClauses = select, WhereClause = where ?? new ContentFilter() },
|
||||
};
|
||||
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, identity: null);
|
||||
var context = new OperationContext(new RequestHeader(), secureChannelContext: null, RequestType.HistoryRead, requestLifetime: RequestLifetime.None, identity: null);
|
||||
var results = new List<SdkHistoryReadResult> { null! };
|
||||
var errors = new List<ServiceResult> { null! };
|
||||
nm.HistoryRead(context, details, TimestampsToReturn.Both, releaseContinuationPoints: false,
|
||||
|
||||
Reference in New Issue
Block a user