using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Scripting; namespace ZB.MOM.WW.OtOpcUa.Driver.Calculation.Tests; /// /// The Calculation driver's run-guard options: the node-wide Scripting section is the base, and a key /// authored in the driver's own config overrides only that knob. Before the section was bound, a Calculation /// driver always ran on the compiled defaults whatever the node configured. /// [Trait("Category", "Unit")] public sealed class CalculationScriptingOptionsResolutionTests { private static readonly ScriptingOptions Node = new() { MaxAllocBytesPerRun = 8L * 1024 * 1024, QuarantineAfterConsecutiveTimeouts = 7, StuckOrphanQuarantineFactor = 4, }; [Fact] public void No_driver_keys_and_no_node_options_resolves_null_so_the_guard_uses_its_defaults() => CalculationDriverFactoryExtensions.ResolveScripting(null, null, null, out _).ShouldBeNull(); [Fact] public void No_driver_keys_inherits_every_node_knob() { var resolved = CalculationDriverFactoryExtensions.ResolveScripting(null, null, Node, out _).ShouldNotBeNull(); resolved.MaxAllocBytesPerRun.ShouldBe(Node.MaxAllocBytesPerRun); resolved.QuarantineAfterConsecutiveTimeouts.ShouldBe(7); resolved.StuckOrphanQuarantineFactor.ShouldBe(4); } [Fact] public void A_driver_key_overrides_only_its_own_knob_and_the_node_object_is_not_mutated() { var resolved = CalculationDriverFactoryExtensions.ResolveScripting(null, 2, Node, out var adjustments).ShouldNotBeNull(); resolved.QuarantineAfterConsecutiveTimeouts.ShouldBe(2); resolved.MaxAllocBytesPerRun.ShouldBe(Node.MaxAllocBytesPerRun, "the unauthored key keeps the node value"); resolved.StuckOrphanQuarantineFactor.ShouldBe(4, "the stuck factor has no driver key; it is node-wide"); resolved.ShouldNotBeSameAs(Node); Node.QuarantineAfterConsecutiveTimeouts.ShouldBe(7); adjustments.ShouldBeEmpty(); } /// Review m-2: an out-of-range value authored in the driver's own config is clamped AND reported, so /// the factory can log it rather than the guard changing it silently. [Fact] public void An_out_of_range_driver_key_is_clamped_and_reported() { var resolved = CalculationDriverFactoryExtensions.ResolveScripting(1024L, 0, Node, out var adjustments).ShouldNotBeNull(); resolved.MaxAllocBytesPerRun.ShouldBe(ScriptingOptions.MinMaxAllocBytesPerRun); resolved.QuarantineAfterConsecutiveTimeouts.ShouldBe(1); adjustments.Count.ShouldBe(2); } [Fact] public void A_driver_key_with_no_node_options_layers_on_the_compiled_defaults() { var resolved = CalculationDriverFactoryExtensions.ResolveScripting(0L, null, null, out _).ShouldNotBeNull(); resolved.MaxAllocBytesPerRun.ShouldBe(0); resolved.QuarantineAfterConsecutiveTimeouts.ShouldBe(ScriptingOptions.DefaultQuarantineAfterConsecutiveTimeouts); } }