fix(m9/T28b): forward --trigger-kind on alarm-update/script-add/script-update; fix default-selector test

Three dead-code bugs: --trigger-kind was registered but never read or forwarded on the
alarm-update, script-add, and script-update paths. Introduced TriggerConfigJson.InjectAnalysisKind
helper that rewrites any raw --trigger-config JSON blob, writing "analysisKind":"Strict" when
the flag is strict (case-insensitive) and stripping the key for any other value. Wired the
helper into all three handlers alongside the existing alarm-add path (which already used
AlarmTriggerConfigJson.Build). Added 6 unit tests for the new helper in TemplateTriggerKindTests.

Also fixed a false-positive bUnit test (AlarmTriggerEditor_Expression_NoAnalysisKindInConfig_
SelectorDefaultsAdvisory) that passed because "Advisory" appeared anywhere in the HTML; now
asserts select.GetAttribute("value") == "Advisory". Added the missing equivalent test for
ScriptTriggerEditor (ScriptTriggerEditor_Expression_NoAnalysisKindInConfig_SelectorDefaultsAdvisory).
This commit is contained in:
Joseph Doherty
2026-06-18 10:54:42 -04:00
parent ea69178dd9
commit 0bd5e0986f
4 changed files with 171 additions and 15 deletions
@@ -13,18 +13,78 @@ namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands;
/// </summary>
public class TemplateTriggerKindTests
{
private static readonly Option<string> Url = new("--url") { Recursive = true };
private static readonly Option<string> Username = new("--username") { Recursive = true };
private static readonly Option<string> Password = new("--password") { Recursive = true };
private static readonly Option<string> Format = CliOptions.CreateFormatOption();
// ── TriggerConfigJson.InjectAnalysisKind — the shared helper used by
// alarm-update / script-add / script-update ──────────────────────────────
private static Command AlarmGroup()
=> TemplateCommands.Build(Url, Format, Username, Password)
.Subcommands.Single(c => c.Name == "alarm");
// Inject strict into an Expression config that has no prior analysisKind
[Fact]
public void InjectAnalysisKind_Strict_AddsKey()
{
var json = "{\"expression\":\"x > 0\"}";
var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");
private static Command ScriptGroup()
=> TemplateCommands.Build(Url, Format, Username, Password)
.Subcommands.Single(c => c.Name == "script");
Assert.NotNull(result);
using var doc = JsonDocument.Parse(result!);
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
}
// Inject advisory (null) into config that had Strict → key removed
[Fact]
public void InjectAnalysisKind_Advisory_RemovesKey()
{
var json = "{\"expression\":\"x > 0\",\"analysisKind\":\"Strict\"}";
var result = TriggerConfigJson.InjectAnalysisKind(json, null);
Assert.NotNull(result);
using var doc = JsonDocument.Parse(result!);
Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
}
// Inject strict into config that already had Strict → still Strict (idempotent)
[Fact]
public void InjectAnalysisKind_Strict_Idempotent()
{
var json = "{\"expression\":\"x > 0\",\"analysisKind\":\"Strict\"}";
var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");
Assert.NotNull(result);
using var doc = JsonDocument.Parse(result!);
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
}
// Null config → returns null (no config to inject into)
[Fact]
public void InjectAnalysisKind_NullConfig_ReturnsNull()
{
var result = TriggerConfigJson.InjectAnalysisKind(null, "strict");
Assert.Null(result);
}
// Case-insensitive: "STRICT" → canonical "Strict"
[Fact]
public void InjectAnalysisKind_Strict_CaseInsensitive()
{
var json = "{\"expression\":\"x > 0\"}";
var result = TriggerConfigJson.InjectAnalysisKind(json, "STRICT");
Assert.NotNull(result);
using var doc = JsonDocument.Parse(result!);
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
}
// Other keys preserved when injecting
[Fact]
public void InjectAnalysisKind_OtherKeysPreserved()
{
var json = "{\"expression\":\"x > 0\",\"mode\":\"OnTrue\"}";
var result = TriggerConfigJson.InjectAnalysisKind(json, "strict");
Assert.NotNull(result);
using var doc = JsonDocument.Parse(result!);
Assert.Equal("x > 0", doc.RootElement.GetProperty("expression").GetString());
Assert.Equal("OnTrue", doc.RootElement.GetProperty("mode").GetString());
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
}
// ── Option surface: alarm add + update have --trigger-kind ───────────────
@@ -154,4 +214,17 @@ public class TemplateTriggerKindTests
using var doc = JsonDocument.Parse(json!);
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
}
private static readonly Option<string> Url = new("--url") { Recursive = true };
private static readonly Option<string> Username = new("--username") { Recursive = true };
private static readonly Option<string> Password = new("--password") { Recursive = true };
private static readonly Option<string> Format = CliOptions.CreateFormatOption();
private static Command AlarmGroup()
=> TemplateCommands.Build(Url, Format, Username, Password)
.Subcommands.Single(c => c.Name == "alarm");
private static Command ScriptGroup()
=> TemplateCommands.Build(Url, Format, Username, Password)
.Subcommands.Single(c => c.Name == "script");
}
@@ -240,8 +240,21 @@ public class AlarmTriggerEditorAnalysisKindTests : BunitContext
.Add(p => p.Value, @"{""expression"":""x > 0""}"));
var select = cut.Find("#alarm-trigger-kind");
// The selected option must be Advisory (the default)
Assert.Contains("Advisory", select.InnerHtml);
// The bound value must be "Advisory" — not just present in the HTML
Assert.Equal("Advisory", select.GetAttribute("value"));
}
// Selector defaults to Advisory when config has no analysisKind (ScriptTriggerEditor)
[Fact]
public void ScriptTriggerEditor_Expression_NoAnalysisKindInConfig_SelectorDefaultsAdvisory()
{
var cut = Render<ScriptTriggerEditor>(ps => ps
.Add(p => p.TriggerType, "Expression")
.Add(p => p.TriggerConfig, @"{""expression"":""x > 0"",""mode"":""OnTrue""}"));
var select = cut.Find("#script-trigger-kind");
// The bound value must be "Advisory" — not just present in the HTML
Assert.Equal("Advisory", select.GetAttribute("value"));
}
// Choosing Strict writes analysisKind:"Strict" into emitted config