fix(abcip,adminui): ParseEnum device wording + libplctag probe assertion (#574); AB forms preserve unknown keys + MaxConcurrentReads (#576); probe off for new AbCip driver (#577); docs T4:0.DN (#584)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -172,4 +172,77 @@ public sealed class AbCipDriverPageFormSerializationTests
|
||||
form.Transport = null; // the "Default (...)" dropdown option
|
||||
AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices)).ShouldNotContain("transport", Case.Insensitive);
|
||||
}
|
||||
|
||||
// ---- #576: preserve-unknown + MaxConcurrentReads ----
|
||||
|
||||
private const string FullConfig = """
|
||||
{"transport":"Native","timeout":"00:00:05","maxConcurrentReads":3,"enableControllerBrowse":true,
|
||||
"enableDeclarationOnlyUdtGrouping":true,"enableAlarmProjection":true,"alarmPollInterval":"00:00:02",
|
||||
"probeTimeoutSeconds":12,
|
||||
"probe":{"enabled":true,"interval":"00:00:10","timeout":"00:00:03","probeTagPath":"Program:Main.Hb","futureProbeKnob":7},
|
||||
"devices":[{"hostAddress":"ab://10.0.0.1/1,0","plcFamily":"ControlLogix","deviceName":"PLC-1","futureDeviceKey":"keep"}],
|
||||
"rawTags":[{"rawPath":"a/b"}],
|
||||
"futureOption":{"nested":[1,2,3]},"anotherUnknown":"x"}
|
||||
""";
|
||||
|
||||
[Fact]
|
||||
public void Editing_one_field_preserves_every_other_option_and_unknown_keys()
|
||||
{
|
||||
var loaded = AbCipDriverForm.TryDeserialize(FullConfig)!;
|
||||
var form = AbCipDriverForm.FormModel.FromOptions(loaded);
|
||||
form.TimeoutSeconds = 9; // the one edit
|
||||
|
||||
var root = System.Text.Json.Nodes.JsonNode.Parse(
|
||||
AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices), FullConfig))!.AsObject();
|
||||
|
||||
root["timeout"]!.GetValue<string>().ShouldBe("00:00:09");
|
||||
root["maxConcurrentReads"]!.GetValue<int>().ShouldBe(3); // was reset to 8 before #576
|
||||
root["transport"]!.GetValue<string>().ShouldBe("Native");
|
||||
root["enableControllerBrowse"]!.GetValue<bool>().ShouldBeTrue();
|
||||
root["enableDeclarationOnlyUdtGrouping"]!.GetValue<bool>().ShouldBeTrue();
|
||||
root["enableAlarmProjection"]!.GetValue<bool>().ShouldBeTrue();
|
||||
root["alarmPollInterval"]!.GetValue<string>().ShouldBe("00:00:02");
|
||||
root["probeTimeoutSeconds"]!.GetValue<int>().ShouldBe(12);
|
||||
root["probe"]!["enabled"]!.GetValue<bool>().ShouldBeTrue();
|
||||
root["probe"]!["interval"]!.GetValue<string>().ShouldBe("00:00:10");
|
||||
root["probe"]!["timeout"]!.GetValue<string>().ShouldBe("00:00:03");
|
||||
root["probe"]!["probeTagPath"]!.GetValue<string>().ShouldBe("Program:Main.Hb");
|
||||
// unknown keys at every level survive
|
||||
root["probe"]!["futureProbeKnob"]!.GetValue<int>().ShouldBe(7);
|
||||
root["devices"]![0]!["futureDeviceKey"]!.GetValue<string>().ShouldBe("keep");
|
||||
root["rawTags"]![0]!["rawPath"]!.GetValue<string>().ShouldBe("a/b");
|
||||
root["futureOption"]!["nested"]!.AsArray().Count.ShouldBe(3);
|
||||
root["anotherUnknown"]!.GetValue<string>().ShouldBe("x");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Choosing_Default_transport_and_clearing_the_probe_path_removes_those_keys_from_the_overlay()
|
||||
{
|
||||
var loaded = AbCipDriverForm.TryDeserialize(FullConfig)!;
|
||||
var form = AbCipDriverForm.FormModel.FromOptions(loaded);
|
||||
form.Transport = null;
|
||||
form.ProbeTagPath = "";
|
||||
|
||||
var root = System.Text.Json.Nodes.JsonNode.Parse(
|
||||
AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices), FullConfig))!.AsObject();
|
||||
|
||||
root.ContainsKey("transport").ShouldBeFalse();
|
||||
root["probe"]!.AsObject().ContainsKey("probeTagPath").ShouldBeFalse();
|
||||
root["futureOption"].ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_stored_PascalCase_key_is_replaced_not_duplicated()
|
||||
{
|
||||
var original = """{"Transport":"Native","TimeoutSeconds":1,"futureOption":true}""";
|
||||
var loaded = AbCipDriverForm.TryDeserialize(original)!;
|
||||
var form = AbCipDriverForm.FormModel.FromOptions(loaded);
|
||||
form.Transport = AbCipTransport.Libplctag;
|
||||
|
||||
var json = AbCipDriverForm.Serialize(form.ToOptions(loaded.Devices), original);
|
||||
System.Text.RegularExpressions.Regex.Matches(json, "transport", System.Text.RegularExpressions.RegexOptions.IgnoreCase)
|
||||
.Count.ShouldBe(1);
|
||||
json.ShouldContain("\"transport\":\"Libplctag\"");
|
||||
json.ShouldContain("futureOption");
|
||||
}
|
||||
}
|
||||
|
||||
+47
@@ -187,4 +187,51 @@ public sealed class AbLegacyDriverPageFormSerializationTests
|
||||
razor.ShouldContain("<option value=\"@AbLegacyTransport.Libplctag\">libplctag</option>");
|
||||
razor.ShouldNotContain("Default (Native");
|
||||
}
|
||||
|
||||
// ---- #576: preserve-unknown ----
|
||||
|
||||
private const string FullConfig = """
|
||||
{"transport":"Native","timeout":"00:00:05","probeTimeoutSeconds":12,
|
||||
"probe":{"enabled":true,"interval":"00:00:10","timeout":"00:00:03","probeAddress":"S:1","futureProbeKnob":7},
|
||||
"devices":[{"hostAddress":"ab://10.0.0.1/1,0","plcFamily":"Slc500","futureDeviceKey":"keep"}],
|
||||
"rawTags":[{"rawPath":"a/b"}],
|
||||
"futureOption":{"nested":[1,2,3]}}
|
||||
""";
|
||||
|
||||
[Fact]
|
||||
public void Editing_one_field_preserves_every_other_option_and_unknown_keys()
|
||||
{
|
||||
var loaded = AbLegacyDriverForm.TryDeserialize(FullConfig)!;
|
||||
var form = AbLegacyDriverForm.FormModel.FromOptions(loaded);
|
||||
form.TimeoutSeconds = 9;
|
||||
|
||||
var root = System.Text.Json.Nodes.JsonNode.Parse(
|
||||
AbLegacyDriverForm.Serialize(form.ToOptions(loaded.Devices), FullConfig))!.AsObject();
|
||||
|
||||
root["timeout"]!.GetValue<string>().ShouldBe("00:00:09");
|
||||
root["transport"]!.GetValue<string>().ShouldBe("Native");
|
||||
root["probeTimeoutSeconds"]!.GetValue<int>().ShouldBe(12);
|
||||
root["probe"]!["enabled"]!.GetValue<bool>().ShouldBeTrue();
|
||||
root["probe"]!["interval"]!.GetValue<string>().ShouldBe("00:00:10");
|
||||
root["probe"]!["timeout"]!.GetValue<string>().ShouldBe("00:00:03");
|
||||
root["probe"]!["probeAddress"]!.GetValue<string>().ShouldBe("S:1");
|
||||
root["probe"]!["futureProbeKnob"]!.GetValue<int>().ShouldBe(7);
|
||||
root["devices"]![0]!["futureDeviceKey"]!.GetValue<string>().ShouldBe("keep");
|
||||
root["rawTags"]![0]!["rawPath"]!.GetValue<string>().ShouldBe("a/b");
|
||||
root["futureOption"]!["nested"]!.AsArray().Count.ShouldBe(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Choosing_Default_transport_removes_the_key_but_keeps_unknown_ones()
|
||||
{
|
||||
var loaded = AbLegacyDriverForm.TryDeserialize(FullConfig)!;
|
||||
var form = AbLegacyDriverForm.FormModel.FromOptions(loaded);
|
||||
form.Transport = null;
|
||||
|
||||
var root = System.Text.Json.Nodes.JsonNode.Parse(
|
||||
AbLegacyDriverForm.Serialize(form.ToOptions(loaded.Devices), FullConfig))!.AsObject();
|
||||
|
||||
root.ContainsKey("transport").ShouldBeFalse();
|
||||
root["futureOption"].ShouldNotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user