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:
Joseph Doherty
2026-10-07 22:53:04 -04:00
parent 04b6563286
commit e588443ced
9 changed files with 251 additions and 15 deletions
@@ -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();
}
}