fix(site-runtime,dcl): low-severity cleanups — observed adapter dispose (S9), alarm-filter overwrite warning (S10), bounded-channel comment (C2), invariant-culture condition fallback (C6)

This commit is contained in:
Joseph Doherty
2026-07-09 02:09:06 -04:00
parent 30886c9426
commit c9ac075e39
5 changed files with 84 additions and 8 deletions
@@ -81,6 +81,29 @@ public class DataConnectionActorAlarmTests : TestKit
ExpectMsg<SubscribeAlarmsResponse>(m => !m.Success && m.ErrorMessage != null);
}
[Fact]
public void SecondAlarmSubscriber_WithDifferentFilter_LogsOverwriteWarning()
{
// The adapter alarm feed carries a single shared condition filter per source;
// a second subscriber with a different filter overwrites it (last-writer-wins).
// That silent overwrite must be surfaced as a warning so a co-subscriber
// mismatch is diagnosable (S10).
var (adapter, _) = BuildAlarmAdapter();
var actor = Sys.ActorOf(Props.Create(() => new DataConnectionActor(
"conn", adapter, _options, _health, _factory, "OpcUa")));
actor.Tell(new SubscribeAlarmsRequest(
"c1", "instA", "conn", "Tank01", "AnalogLimit.Hi", DateTimeOffset.UtcNow));
ExpectMsg<SubscribeAlarmsResponse>(m => m.Success);
EventFilter.Warning(contains: "condition filter").ExpectOne(() =>
{
actor.Tell(new SubscribeAlarmsRequest(
"c2", "instB", "conn", "Tank01", "AnalogLimit.Lo", DateTimeOffset.UtcNow));
ExpectMsg<SubscribeAlarmsResponse>(m => m.Success);
});
}
// ── M2.4 (#8): conditionFilter is now applied client-side in the actor ──
[Fact]
@@ -1,3 +1,4 @@
using System.Globalization;
using Akka.Actor;
using Akka.TestKit;
using Akka.TestKit.Xunit2;
@@ -300,6 +301,36 @@ public class ScriptActorTests : TestKit, IDisposable
private AttributeValueChanged Change(string attribute, object? value) =>
new("TestInstance", attribute, attribute, value, "Good", DateTimeOffset.UtcNow);
[Fact]
public void ConditionalTrigger_NonNumericFallback_IsCultureInvariant()
{
// EvaluateCondition runs on a dispatcher thread in the live path, whose
// CurrentCulture a test cannot deterministically set — so the culture-
// invariance of the string fallback is verified by calling the pure helper
// directly under a forced de-DE culture.
var original = CultureInfo.CurrentCulture;
try
{
CultureInfo.CurrentCulture = new CultureInfo("de-DE"); // 1234.5 -> "1.234,5"
// "1.234,5" is the de-DE rendering of 1234.5: it FAILS the invariant
// numeric parse (decimal point then group separator) and falls through to
// the string comparison. The old fallback compared against the culture-
// sensitive Threshold.ToString() ("1.234,5" under de-DE) and matched (fired);
// the invariant fallback compares against "1234.5" and must NOT match.
var config = new ConditionalTriggerConfig("Temp", "==", 1234.5, TriggerMode.OnTrue);
Assert.False(ScriptActor.EvaluateCondition(config, "1.234,5"));
// Sanity: a value that genuinely equals the invariant rendering still matches.
Assert.True(ScriptActor.EvaluateCondition(
new ConditionalTriggerConfig("Temp", "==", 1234.5, TriggerMode.OnTrue), "1234.5"));
}
finally
{
CultureInfo.CurrentCulture = original;
}
}
private Script<object?> CompileTriggerExpression(string expression) =>
_compilationService.CompileTriggerExpression("trigger-expr", expression).CompiledScript!;