feat(deploy): surface connection-level changes in the deployment diff (#10)

ComputeConnectionsDiff existed with tests but was never called and ConfigurationDiff
had no slot for it, so standalone connection endpoint/protocol/failover drift never
appeared in the deployment diff (only per-attribute binding drift did). Add a
ConnectionChanges slot, wire ComputeConnectionsDiff into ComputeDiff, and render the
connection section in the deployment diff UI.
This commit is contained in:
Joseph Doherty
2026-06-15 13:36:40 -04:00
parent 41d828e38e
commit e9a84ba220
5 changed files with 336 additions and 8 deletions
@@ -369,4 +369,105 @@ public class DiffServiceTests
Assert.Empty(diff);
}
// ── TemplateEngine-018: ComputeDiff wires ComputeConnectionsDiff into the
// public ConfigurationDiff.ConnectionChanges slot so standalone connection
// protocol/endpoint/failover drift surfaces in the deployment diff (#10). ──
[Fact]
public void ComputeDiff_ConnectionProtocolAndEndpointAndFailoverChange_PopulatesConnectionChanges()
{
// Protocol, endpoint config JSON, and failover retry count all differ
// on the same connection. Before this wiring, ComputeDiff dropped the
// entire connection dimension so this redeploy showed "no changes".
var oldConfig = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Connections = new Dictionary<string, ConnectionConfig>
{
["plc1"] = new ConnectionConfig
{
Protocol = "OpcUa",
ConfigurationJson = "{\"endpoint\":\"opc.tcp://host-a:4840\"}",
FailoverRetryCount = 3,
}
}
};
var newConfig = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Connections = new Dictionary<string, ConnectionConfig>
{
["plc1"] = new ConnectionConfig
{
Protocol = "Modbus",
ConfigurationJson = "{\"endpoint\":\"opc.tcp://host-b:4840\"}",
FailoverRetryCount = 5,
}
}
};
var diff = _sut.ComputeDiff(oldConfig, newConfig);
Assert.True(diff.HasChanges);
Assert.Single(diff.ConnectionChanges);
var entry = diff.ConnectionChanges[0];
Assert.Equal("plc1", entry.CanonicalName);
Assert.Equal(DiffChangeType.Changed, entry.ChangeType);
Assert.Equal("OpcUa", entry.OldValue!.Protocol);
Assert.Equal("Modbus", entry.NewValue!.Protocol);
Assert.Contains("host-a", entry.OldValue!.ConfigurationJson);
Assert.Contains("host-b", entry.NewValue!.ConfigurationJson);
Assert.Equal(3, entry.OldValue!.FailoverRetryCount);
Assert.Equal(5, entry.NewValue!.FailoverRetryCount);
}
[Fact]
public void ComputeDiff_OnlyConnectionDiffers_HasChangesIsTrue()
{
// Attributes, alarms, and scripts are identical; only a connection's
// endpoint changed. HasChanges must be true so the diff view does not
// claim "no differences" while a connection endpoint silently moved.
var attributes = new List<ResolvedAttribute>
{
new ResolvedAttribute { CanonicalName = "Temp", Value = "25", DataType = "Double" }
};
var oldConfig = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Attributes = attributes,
Connections = new Dictionary<string, ConnectionConfig>
{
["plc1"] = new ConnectionConfig
{
Protocol = "OpcUa",
ConfigurationJson = "{\"endpoint\":\"opc.tcp://host-a:4840\"}",
FailoverRetryCount = 3,
}
}
};
var newConfig = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Attributes = attributes,
Connections = new Dictionary<string, ConnectionConfig>
{
["plc1"] = new ConnectionConfig
{
Protocol = "OpcUa",
ConfigurationJson = "{\"endpoint\":\"opc.tcp://host-b:4840\"}",
FailoverRetryCount = 3,
}
}
};
var diff = _sut.ComputeDiff(oldConfig, newConfig);
Assert.True(diff.HasChanges);
Assert.Empty(diff.AttributeChanges);
Assert.Empty(diff.AlarmChanges);
Assert.Empty(diff.ScriptChanges);
Assert.Single(diff.ConnectionChanges);
Assert.Equal(DiffChangeType.Changed, diff.ConnectionChanges[0].ChangeType);
}
}