513fa1aa16
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
246 lines
11 KiB
C#
246 lines
11 KiB
C#
using System.CommandLine;
|
|
using System.Text.Json;
|
|
using ZB.MOM.WW.ScadaBridge.CLI;
|
|
using ZB.MOM.WW.ScadaBridge.CLI.Commands;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands;
|
|
|
|
/// <summary>
|
|
/// <c>template alarm add/update</c> and <c>template script add/update</c>
|
|
/// must expose a <c>--trigger-kind</c> option (advisory|strict) that writes
|
|
/// <c>"analysisKind":"Strict"</c> into the trigger-config JSON for expression
|
|
/// triggers, matching the T28a backend contract exactly.
|
|
/// </summary>
|
|
public class TemplateTriggerKindTests
|
|
{
|
|
// ── TriggerConfigJson.InjectAnalysisKind — the shared helper used by
|
|
// alarm-update / script-add / script-update ──────────────────────────────
|
|
|
|
// Inject strict into an Expression config that has no prior analysisKind
|
|
/// <summary>Verifies that injecting the Strict analysis kind adds the analysis kind key to the trigger config.</summary>
|
|
[Fact]
|
|
public void InjectAnalysisKind_Strict_AddsKey()
|
|
{
|
|
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());
|
|
}
|
|
|
|
// Inject advisory (null) into config that had Strict → key removed
|
|
/// <summary>Verifies that injecting the Advisory analysis kind removes the analysis kind key from the trigger config.</summary>
|
|
[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)
|
|
/// <summary>Verifies that injecting the Strict analysis kind twice yields the same config as injecting it once.</summary>
|
|
[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)
|
|
/// <summary>Verifies that injecting an analysis kind into a null config returns null.</summary>
|
|
[Fact]
|
|
public void InjectAnalysisKind_NullConfig_ReturnsNull()
|
|
{
|
|
var result = TriggerConfigJson.InjectAnalysisKind(null, "strict");
|
|
Assert.Null(result);
|
|
}
|
|
|
|
// Case-insensitive: "STRICT" → canonical "Strict"
|
|
/// <summary>Verifies that the Strict analysis kind is recognized regardless of letter casing.</summary>
|
|
[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
|
|
/// <summary>Verifies that injecting an analysis kind leaves the other trigger config keys unchanged.</summary>
|
|
[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 ───────────────
|
|
|
|
/// <summary>Verifies that the alarm add command exposes a trigger-kind option.</summary>
|
|
[Fact]
|
|
public void AlarmAdd_HasTriggerKindOption()
|
|
{
|
|
var add = AlarmGroup().Subcommands.Single(c => c.Name == "add");
|
|
Assert.Contains("--trigger-kind", add.Options.Select(o => o.Name));
|
|
}
|
|
|
|
/// <summary>Verifies that the alarm update command exposes a trigger-kind option.</summary>
|
|
[Fact]
|
|
public void AlarmUpdate_HasTriggerKindOption()
|
|
{
|
|
var update = AlarmGroup().Subcommands.Single(c => c.Name == "update");
|
|
Assert.Contains("--trigger-kind", update.Options.Select(o => o.Name));
|
|
}
|
|
|
|
// ── Option surface: script add + update have --trigger-kind ─────────────
|
|
|
|
/// <summary>Verifies that the script add command exposes a trigger-kind option.</summary>
|
|
[Fact]
|
|
public void ScriptAdd_HasTriggerKindOption()
|
|
{
|
|
var add = ScriptGroup().Subcommands.Single(c => c.Name == "add");
|
|
Assert.Contains("--trigger-kind", add.Options.Select(o => o.Name));
|
|
}
|
|
|
|
/// <summary>Verifies that the script update command exposes a trigger-kind option.</summary>
|
|
[Fact]
|
|
public void ScriptUpdate_HasTriggerKindOption()
|
|
{
|
|
var update = ScriptGroup().Subcommands.Single(c => c.Name == "update");
|
|
Assert.Contains("--trigger-kind", update.Options.Select(o => o.Name));
|
|
}
|
|
|
|
// ── AlarmTriggerConfigJson.Build — analysisKind injection ────────────────
|
|
|
|
/// <summary>Verifies that building an expression alarm config with the Strict kind injects an analysis kind of Strict.</summary>
|
|
[Fact]
|
|
public void AlarmConfigBuild_Expression_StrictKind_InjectsAnalysisKindStrict()
|
|
{
|
|
// --trigger-kind strict with --expression set → config carries analysisKind:"Strict"
|
|
var json = AlarmTriggerConfigJson.Build(
|
|
triggerType: "Expression",
|
|
attribute: null,
|
|
matchValue: null, notEquals: false,
|
|
min: null, max: null,
|
|
thresholdPerSecond: null, windowSeconds: null, direction: null,
|
|
loLo: null, lo: null, hi: null, hiHi: null,
|
|
expression: "Temp > 80",
|
|
analysisKind: "strict");
|
|
|
|
Assert.NotNull(json);
|
|
using var doc = JsonDocument.Parse(json!);
|
|
Assert.Equal("Strict", doc.RootElement.GetProperty("analysisKind").GetString());
|
|
}
|
|
|
|
/// <summary>Verifies that building an expression alarm config with the Advisory kind omits the analysis kind key.</summary>
|
|
[Fact]
|
|
public void AlarmConfigBuild_Expression_AdvisoryKind_OmitsAnalysisKindKey()
|
|
{
|
|
// --trigger-kind advisory (default) → key omitted
|
|
var json = AlarmTriggerConfigJson.Build(
|
|
triggerType: "Expression",
|
|
attribute: null,
|
|
matchValue: null, notEquals: false,
|
|
min: null, max: null,
|
|
thresholdPerSecond: null, windowSeconds: null, direction: null,
|
|
loLo: null, lo: null, hi: null, hiHi: null,
|
|
expression: "Temp > 80",
|
|
analysisKind: "advisory");
|
|
|
|
Assert.NotNull(json);
|
|
using var doc = JsonDocument.Parse(json!);
|
|
Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
|
|
}
|
|
|
|
/// <summary>Verifies that building an expression alarm config with no kind omits the analysis kind key.</summary>
|
|
[Fact]
|
|
public void AlarmConfigBuild_Expression_NullKind_OmitsAnalysisKindKey()
|
|
{
|
|
// Omitted --trigger-kind → null → Advisory default → key omitted
|
|
var json = AlarmTriggerConfigJson.Build(
|
|
triggerType: "Expression",
|
|
attribute: null,
|
|
matchValue: null, notEquals: false,
|
|
min: null, max: null,
|
|
thresholdPerSecond: null, windowSeconds: null, direction: null,
|
|
loLo: null, lo: null, hi: null, hiHi: null,
|
|
expression: "Temp > 80",
|
|
analysisKind: null);
|
|
|
|
Assert.NotNull(json);
|
|
using var doc = JsonDocument.Parse(json!);
|
|
Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
|
|
}
|
|
|
|
/// <summary>Verifies that a non-expression alarm config does not emit an analysis kind even when the Strict kind is given.</summary>
|
|
[Fact]
|
|
public void AlarmConfigBuild_NonExpression_StrictKind_DoesNotEmitAnalysisKind()
|
|
{
|
|
// analysisKind is meaningless for non-expression triggers — must be suppressed
|
|
var json = AlarmTriggerConfigJson.Build(
|
|
triggerType: "RangeViolation",
|
|
attribute: "Temp",
|
|
matchValue: null, notEquals: false,
|
|
min: 0, max: 100,
|
|
thresholdPerSecond: null, windowSeconds: null, direction: null,
|
|
loLo: null, lo: null, hi: null, hiHi: null,
|
|
expression: null,
|
|
analysisKind: "strict");
|
|
|
|
Assert.NotNull(json);
|
|
using var doc = JsonDocument.Parse(json!);
|
|
Assert.False(doc.RootElement.TryGetProperty("analysisKind", out _));
|
|
}
|
|
|
|
/// <summary>Verifies that the Strict kind is recognized case-insensitively when building an expression alarm config.</summary>
|
|
[Fact]
|
|
public void AlarmConfigBuild_Expression_StrictKind_CaseInsensitive()
|
|
{
|
|
// "STRICT" (uppercase) must also produce the canonical "Strict" value
|
|
var json = AlarmTriggerConfigJson.Build(
|
|
triggerType: "Expression",
|
|
attribute: null,
|
|
matchValue: null, notEquals: false,
|
|
min: null, max: null,
|
|
thresholdPerSecond: null, windowSeconds: null, direction: null,
|
|
loLo: null, lo: null, hi: null, hiHi: null,
|
|
expression: "x > 0",
|
|
analysisKind: "STRICT");
|
|
|
|
Assert.NotNull(json);
|
|
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");
|
|
}
|