5dabafecdc
Review m-2. A Calculation driver's own maxAllocBytesPerRun / quarantineAfterConsecutiveTimeouts used to be clamped silently by ScriptRunGuard. ResolveScripting now sanitizes the layered options itself and returns the adjustments, and the factory logs one Warning per clamped value, naming the driver. docs/ScriptEditor.md "Runtime safety" gains the key table: defaults, the 1 MB allocation floor, the 1000 caps, NaN and disable semantics, and the per-driver override. docs/Configuration.md has no Scripting section, so these keys have no other home. Refs #731 #734 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
70 lines
3.0 KiB
C#
70 lines
3.0 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Calculation.Tests;
|
|
|
|
/// <summary>
|
|
/// The Calculation driver's run-guard options: the node-wide <c>Scripting</c> 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.
|
|
/// </summary>
|
|
[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();
|
|
}
|
|
|
|
/// <summary>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.</summary>
|
|
[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);
|
|
}
|
|
}
|