using System.Text.Json; using System.Text.Json.Serialization; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.Mqtt; namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt.Tests; public sealed class MqttDriverOptionsTests { private static readonly JsonSerializerOptions J = new() { PropertyNameCaseInsensitive = true, UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, Converters = { new JsonStringEnumConverter() }, }; [Fact] public void Deserialize_SparkplugConfig_ReadsModeAndSubObject() { const string json = """ { "host":"10.100.0.35","port":8883,"useTls":true,"mode":"SparkplugB", "sparkplug":{"groupId":"Plant1","hostId":"h1","requestRebirthOnGap":true} } """; var o = JsonSerializer.Deserialize(json, J)!; o.Mode.ShouldBe(MqttMode.SparkplugB); o.UseTls.ShouldBeTrue(); o.Sparkplug!.GroupId.ShouldBe("Plant1"); o.Plain.ShouldBeNull(); } [Fact] public void Serialize_WritesEnumsAsNames_NotOrdinals() { var s = JsonSerializer.Serialize(new MqttDriverOptions { Mode = MqttMode.Plain }, J); s.ShouldContain("\"Plain\""); s.ShouldNotContain("\"mode\":0"); } // Pins the plan's cross-cutting rule ("never ship anonymous/public-broker defaults") as an // executable assertion — nothing else in this suite fails if UseTls / AllowUntrustedServerCertificate // (or the §5.1 numeric defaults) get flipped by an unrelated edit. [Fact] public void Defaults_SecurityCriticalKnobs_MatchPlan() { var o = new MqttDriverOptions(); o.UseTls.ShouldBeTrue(); o.AllowUntrustedServerCertificate.ShouldBeFalse(); o.ConnectTimeoutSeconds.ShouldBe(15); o.ReconnectMinBackoffSeconds.ShouldBe(1); o.ReconnectMaxBackoffSeconds.ShouldBe(30); } // The production case: an operator-authored config blob that simply doesn't mention TLS must // never silently deserialize into an insecure instance. [Fact] public void Deserialize_PartialJsonOmittingTlsKnobs_KeepsSecureDefaults() { const string json = """{ "host":"10.100.0.35","port":8883 }"""; var o = JsonSerializer.Deserialize(json, J)!; o.UseTls.ShouldBeTrue(); o.AllowUntrustedServerCertificate.ShouldBeFalse(); } [Fact] public void ToString_DoesNotLeakPassword() { var o = new MqttDriverOptions { Password = "hunter2-supersecret" }; var s = o.ToString(); s.ShouldNotContain("hunter2-supersecret"); s.ShouldContain("***"); } }