test(vtags): planner detects Historize-only toggle as a change + doc nit (H5a review follow-up)

This commit is contained in:
Joseph Doherty
2026-06-15 10:21:31 -04:00
parent 9c5a091395
commit 83d3b9f7be
2 changed files with 37 additions and 2 deletions
@@ -115,8 +115,8 @@ public sealed record EquipmentTagAlarmInfo(string AlarmType, int Severity);
/// </summary>
/// <param name="Historize">When true, this VirtualTag's values are historized (carried from the
/// <c>VirtualTag.Historize</c> entity column). Threaded through the deploy-diff equality below so a
/// Historize-only toggle is detected as a change. Defaults to <c>false</c> — matching both the entity
/// default for an unset column and the artifact-decode default when the flag is absent/non-bool —
/// Historize-only toggle is detected as a change. Defaults to <c>false</c> — matching both the CLR
/// default of the <c>bool VirtualTag.Historize</c> column and the artifact-decode default when the flag is absent/non-bool —
/// which keeps existing positional+named ctor call sites compiling and preserves byte-parity.</param>
public sealed record EquipmentVirtualTagPlan(
string VirtualTagId,
@@ -139,6 +139,41 @@ public sealed class Phase7PlannerTests
plan.RemovedEquipmentVirtualTags.ShouldBeEmpty();
}
/// <summary>H5a — a VirtualTag with the same id but a toggled <c>Historize</c> flag (and otherwise
/// identical fields) must route to ChangedEquipmentVirtualTags. This pins that <c>Historize</c> is
/// part of <see cref="EquipmentVirtualTagPlan.Equals"/> so a Historize-only deploy is not a silent
/// no-op at the diff/IsEmpty gate.</summary>
[Fact]
public void Same_id_with_toggled_historize_routes_to_ChangedEquipmentVirtualTags()
{
var prev = new Phase7CompositionResult(
Array.Empty<EquipmentNode>(), Array.Empty<DriverInstancePlan>(), Array.Empty<ScriptedAlarmPlan>())
{
EquipmentVirtualTags = new[]
{
new EquipmentVirtualTagPlan("vt-1", "eq-1", FolderPath: "", Name: "Efficiency", DataType: "Float",
Expression: "a + b", DependencyRefs: new[] { "a", "b" }, Historize: false),
},
};
var next = new Phase7CompositionResult(
Array.Empty<EquipmentNode>(), Array.Empty<DriverInstancePlan>(), Array.Empty<ScriptedAlarmPlan>())
{
EquipmentVirtualTags = new[]
{
new EquipmentVirtualTagPlan("vt-1", "eq-1", FolderPath: "", Name: "Efficiency", DataType: "Float",
Expression: "a + b", DependencyRefs: new[] { "a", "b" }, Historize: true),
},
};
var plan = Phase7Planner.Compute(prev, next);
plan.IsEmpty.ShouldBeFalse();
plan.ChangedEquipmentVirtualTags.Single().Previous.Historize.ShouldBeFalse();
plan.ChangedEquipmentVirtualTags.Single().Current.Historize.ShouldBeTrue();
plan.AddedEquipmentVirtualTags.ShouldBeEmpty();
plan.RemovedEquipmentVirtualTags.ShouldBeEmpty();
}
/// <summary>Regression guard for structural equality on <see cref="EquipmentVirtualTagPlan.DependencyRefs"/>:
/// two snapshots containing the SAME VirtualTag built from SEPARATE list instances must diff to an empty plan
/// (IReadOnlyList equality is BY REFERENCE without the custom Equals override, so every VirtualTag with