Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.Host.Tests/Configuration/ScriptingOptionsBindingTests.cs
T
Joseph Doherty 4d8de20050 test(scripting): pin the Scripting section bind behind one helper
Review m-1. The bind moves from Program.cs into
Host.Configuration.ScriptingOptionsBinding.Bind, which binds the
section and clamps it. Program.cs calls it, and
ScriptingOptionsBindingTests drives it with in-memory configuration.
Every documented key takes a non-default value, so a misspelled
property or key goes red instead of silently falling back to the
defaults. The tests also cover a clamped bind (1e300 / 1024 / 0, three
adjustments), an absent section, and the sibling LogFilePath /
LogTopicMinLevel keys leaving the bind untouched.

Refs #731 #734
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
2026-10-22 15:14:02 -04:00

65 lines
2.7 KiB
C#

using Microsoft.Extensions.Configuration;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
using ZB.MOM.WW.OtOpcUa.Host.Configuration;
namespace ZB.MOM.WW.OtOpcUa.Host.Tests.Configuration;
/// <summary>
/// Review m-1: the <c>Scripting</c> section binds by the documented key names and is clamped at bind. Each
/// configured value differs from the compiled default, so a misspelled key (which the binder would silently
/// ignore, falling back to the default) goes red.
/// </summary>
public sealed class ScriptingOptionsBindingTests
{
private static IConfiguration Config(Dictionary<string, string?> values) =>
new ConfigurationBuilder().AddInMemoryCollection(values).Build();
[Fact]
public void Every_documented_key_binds_to_its_property()
{
var bound = ScriptingOptionsBinding.Bind(Config(new()
{
["Scripting:MaxAllocBytesPerRun"] = "8388608",
["Scripting:QuarantineAfterConsecutiveTimeouts"] = "7",
["Scripting:StuckOrphanQuarantineFactor"] = "4.5",
// Sibling keys owned by the script-log pipeline must not disturb the bind.
["Scripting:LogFilePath"] = "logs/x-.log",
["Scripting:LogTopicMinLevel"] = "Warning",
}), out var adjustments);
adjustments.ShouldBeEmpty();
bound.MaxAllocBytesPerRun.ShouldBe(8L * 1024 * 1024);
bound.QuarantineAfterConsecutiveTimeouts.ShouldBe(7);
bound.StuckOrphanQuarantineFactor.ShouldBe(4.5);
}
[Fact]
public void Out_of_range_values_are_clamped_at_bind_and_reported()
{
var bound = ScriptingOptionsBinding.Bind(Config(new()
{
["Scripting:StuckOrphanQuarantineFactor"] = "1e300",
["Scripting:MaxAllocBytesPerRun"] = "1024",
["Scripting:QuarantineAfterConsecutiveTimeouts"] = "0",
}), out var adjustments);
bound.StuckOrphanQuarantineFactor.ShouldBe(ScriptingOptions.MaxStuckOrphanQuarantineFactor);
bound.MaxAllocBytesPerRun.ShouldBe(ScriptingOptions.MinMaxAllocBytesPerRun);
bound.QuarantineAfterConsecutiveTimeouts.ShouldBe(1);
adjustments.Count.ShouldBe(3);
}
[Fact]
public void An_absent_section_yields_the_compiled_defaults()
{
var bound = ScriptingOptionsBinding.Bind(Config(new()), out var adjustments);
adjustments.ShouldBeEmpty();
bound.MaxAllocBytesPerRun.ShouldBe(ScriptingOptions.DefaultMaxAllocBytesPerRun);
bound.QuarantineAfterConsecutiveTimeouts.ShouldBe(ScriptingOptions.DefaultQuarantineAfterConsecutiveTimeouts);
bound.StuckOrphanQuarantineFactor.ShouldBe(ScriptingOptions.DefaultStuckOrphanQuarantineFactor);
}
}