using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.Calculation.Tests; /// /// Unit tests for — the Calculation pseudo-driver's Test-Connect /// probe (parse-only, since there is no backend to reach). /// public sealed class CalculationDriverProbeTests { private static readonly CalculationDriverProbe Sut = new(); [Fact] public async Task Empty_config_json_fails() { var result = await Sut.ProbeAsync("", TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeFalse(); result.Message.ShouldNotBeNullOrWhiteSpace(); } [Fact] public async Task Malformed_json_fails() { var result = await Sut.ProbeAsync("not json", TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeFalse(); result.Message.ShouldNotBeNullOrWhiteSpace(); } [Fact] public async Task A_config_with_no_rawTags_array_is_still_valid() { var result = await Sut.ProbeAsync("{}", TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeTrue(result.Message); } [Fact] public async Task A_well_formed_rawTags_config_is_valid() { const string json = """{"rawTags":[{"rawPath":"calc/e/x","tagConfig":"{}","writeIdempotent":false}]}"""; var result = await Sut.ProbeAsync(json, TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeTrue(result.Message); } /// /// The probe must not be STRICTER than the real spawn/reinit path: both /// CalculationDriverFactoryExtensions.JsonOptions and CalculationDriver.ReinitJsonOptions /// set AllowTrailingCommas + ReadCommentHandling.Skip. Before this pin the probe's own /// options omitted both, so a config a real deploy would accept could fail Test-Connect as "invalid". /// [Fact] public async Task Tolerates_trailing_commas_like_the_real_spawn_and_reinit_paths() { const string json = """{"rawTags":[{"rawPath":"calc/e/x","tagConfig":"{}","writeIdempotent":false},],}"""; var result = await Sut.ProbeAsync(json, TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeTrue(result.Message); } /// Same parity claim for comments, which the spawn/reinit path also tolerates. [Fact] public async Task Tolerates_comments_like_the_real_spawn_and_reinit_paths() { const string json = """ { // a hand-edited config comment "rawTags": [] } """; var result = await Sut.ProbeAsync(json, TimeSpan.FromSeconds(1), TestContext.Current.CancellationToken); result.Ok.ShouldBeTrue(result.Message); } }