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,16 +62,16 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
// EventType — the concrete condition type, so a client reading the FIELD (not just an OfType
// where-clause) can resolve the type.
condition.EventType.ShouldNotBeNull();
condition.EventType.Value.ShouldBe(ObjectTypeIds.OffNormalAlarmType);
condition.EventType!.Value.ShouldBe(ObjectTypeIds.OffNormalAlarmType);
// SourceNode — the condition's own NodeId (it IS the source: no separate value variable exists
// for an alarm-bearing raw tag). Equal to ConditionId.
condition.SourceNode.ShouldNotBeNull();
condition.SourceNode.Value.ShouldBe(new NodeId(RawAlarmPath, rawNs));
condition.SourceNode!.Value.ShouldBe(new NodeId(RawAlarmPath, rawNs));
// SourceName — the RawPath: unique across devices, matching ConditionId/SourceNode.
condition.SourceName.ShouldNotBeNull();
condition.SourceName.Value.ShouldBe(RawAlarmPath);
condition.SourceName!.Value.ShouldBe(RawAlarmPath);
// The leaf name is NOT lost — it stays on ConditionName.
condition.ConditionName!.Value.ShouldBe("HR200");
@@ -169,11 +169,11 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
condition.ShouldNotBeNull();
condition.ConditionClassId.ShouldNotBeNull();
condition.ConditionClassId.Value.ShouldBe(ObjectTypeIds.BaseConditionClassType);
condition.ConditionClassId!.Value.ShouldBe(ObjectTypeIds.BaseConditionClassType);
condition.ConditionClassName.ShouldNotBeNull();
condition.ConditionClassName.Value.ShouldNotBeNull();
condition.ConditionClassName.Value.Text.ShouldBe("BaseConditionClass");
condition.ConditionClassName!.Value.IsNull.ShouldBeFalse();
condition.ConditionClassName!.Value.Text.ShouldBe("BaseConditionClass");
}
/// <summary>#475 — a SCRIPTED condition (UNS realm) carries the SAME classification fields: native and scripted
@@ -213,8 +213,8 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
var condition = nm.TryGetAlarmCondition(RawAlarmPath, AddressSpaceRealm.Raw);
condition.ShouldNotBeNull();
condition.Quality.ShouldNotBeNull();
condition.Quality.Value.ShouldBe((StatusCode)StatusCodes.BadWaitingForInitialData);
StatusCode.IsGood(condition.Quality.Value).ShouldBeFalse();
condition.Quality!.Value.ShouldBe((StatusCode)StatusCodes.BadWaitingForInitialData);
StatusCode.IsGood(condition.Quality!.Value).ShouldBeFalse();
}
/// <summary>#477 — a SCRIPTED condition is script-computed and always "live" in v1, so it materialises Good.
@@ -233,7 +233,7 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
var condition = nm.TryGetAlarmCondition("scripted-q", AddressSpaceRealm.Uns);
condition.ShouldNotBeNull();
condition.Quality.ShouldNotBeNull();
StatusCode.IsGood(condition.Quality.Value).ShouldBeTrue();
StatusCode.IsGood(condition.Quality!.Value).ShouldBeTrue();
}
/// <summary>#477 — WriteAlarmCondition projects the snapshot's Quality onto the live condition node, so a
@@ -258,12 +258,12 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
var condition = nm.TryGetAlarmCondition(RawAlarmPath, AddressSpaceRealm.Raw);
condition.ShouldNotBeNull();
StatusCode.IsBad(condition.Quality.Value).ShouldBeTrue();
StatusCode.IsBad(condition.Quality!.Value).ShouldBeTrue();
// Recover: a Good snapshot restores Good quality.
var goodSnapshot = badSnapshot with { StatusCode = OpcUaStatus.Good };
nm.WriteAlarmCondition(RawAlarmPath, goodSnapshot, DateTime.UtcNow, AddressSpaceRealm.Raw);
StatusCode.IsGood(condition.Quality.Value).ShouldBeTrue();
StatusCode.IsGood(condition.Quality!.Value).ShouldBeTrue();
}
/// <summary>#477 Layer 2 — the dedicated connectivity-quality path sets ONLY the condition's Quality
@@ -283,7 +283,7 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
var condition = nm.TryGetAlarmCondition(RawAlarmPath, AddressSpaceRealm.Raw);
condition.ShouldNotBeNull();
var severityBefore = condition.Severity!.Value;
var messageBefore = condition.Message!.Value?.Text;
var messageBefore = condition.Message!.Value.Text;
// Device comes online → Good.
nm.WriteAlarmQuality(RawAlarmPath, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
@@ -291,9 +291,9 @@ public sealed class NodeManagerAlarmSourceFieldsTests : IDisposable
// Device goes comms-lost → Bad, but severity / message / active-ack state are untouched.
nm.WriteAlarmQuality(RawAlarmPath, OpcUaStatus.Bad, DateTime.UtcNow, AddressSpaceRealm.Raw);
StatusCode.IsBad(condition.Quality.Value).ShouldBeTrue();
condition.Severity.Value.ShouldBe(severityBefore);
condition.Message.Value?.Text.ShouldBe(messageBefore);
StatusCode.IsBad(condition.Quality!.Value).ShouldBeTrue();
condition.Severity!.Value.ShouldBe(severityBefore);
condition.Message!.Value.Text.ShouldBe(messageBefore);
condition.ActiveState!.Id!.Value.ShouldBeFalse(); // unchanged: annotation only
}