docs+ui: backfill XML doc comments and finish dashboard layout pass
Adds missing <summary>/<param> XML docs across 99 server, worker, and test files so CommentChecker reports zero issues (TreatWarningsAsErrors needs the analyzer clean). Bundles in WIP dashboard work: NavSection extraction, MainLayout/site.css/js styling alignment, and DashboardOptions/Auth tweaks.
This commit is contained in:
@@ -16,11 +16,14 @@ public sealed class AlarmClientDiscoveryTests
|
||||
{
|
||||
private readonly ITestOutputHelper output;
|
||||
|
||||
/// <summary>Initializes a new instance of the AlarmClientDiscoveryTests class.</summary>
|
||||
/// <param name="output">The xUnit test output helper.</param>
|
||||
public AlarmClientDiscoveryTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/// <summary>Dumps the public surface of the aaAlarmManagedClient assembly.</summary>
|
||||
[Fact(Skip = "Discovery probe — flip Skip=null to dump aaAlarmManagedClient surface")]
|
||||
public void DumpAlarmClientPublicSurface()
|
||||
{
|
||||
|
||||
@@ -1094,12 +1094,16 @@ public sealed class WorkerPipeSessionTests
|
||||
}
|
||||
|
||||
/// <summary>Records an informational log event.</summary>
|
||||
/// <param name="eventName">The event name.</param>
|
||||
/// <param name="fields">The event fields.</param>
|
||||
public void Information(string eventName, IReadOnlyDictionary<string, object?> fields)
|
||||
{
|
||||
Record(eventName, fields);
|
||||
}
|
||||
|
||||
/// <summary>Records an error log event.</summary>
|
||||
/// <param name="eventName">The event name.</param>
|
||||
/// <param name="fields">The event fields.</param>
|
||||
public void Error(string eventName, IReadOnlyDictionary<string, object?> fields)
|
||||
{
|
||||
Record(eventName, fields);
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
private const string SessionId = "S";
|
||||
private const string CorrelationId = "C";
|
||||
|
||||
/// <summary>Verifies that the handler routes alarm subscriptions and returns ok.</summary>
|
||||
[Fact]
|
||||
public void SubscribeAlarms_WithHandler_RoutesToHandlerAndReturnsOk()
|
||||
{
|
||||
@@ -46,6 +47,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(SessionId, handler.LastSessionId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that subscription without handler returns invalid request.</summary>
|
||||
[Fact]
|
||||
public void SubscribeAlarms_WithoutHandler_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -67,6 +69,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that empty subscription expression returns invalid request.</summary>
|
||||
[Fact]
|
||||
public void SubscribeAlarms_WithEmptyExpression_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -88,6 +91,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge routes native status into hresult and payload.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarm_WithHandler_RoutesNativeStatusIntoHresultAndPayload()
|
||||
{
|
||||
@@ -121,6 +125,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal("alice", handler.LastAckOperatorName);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that invalid alarm GUID returns invalid request.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarm_WithInvalidGuid_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -142,6 +147,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that nonzero native status carries a diagnostic message.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarm_WithNonzeroNativeStatus_CarriesDiagnostic()
|
||||
{
|
||||
@@ -165,6 +171,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Contains("-123", reply.DiagnosticMessage);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge by name routes tuple to handler.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarmByName_WithHandler_RoutesTupleToHandler()
|
||||
{
|
||||
@@ -198,6 +205,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal("alice", handler.LastAckOperatorName);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that empty alarm name returns invalid request.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarmByName_WithEmptyName_ReturnsInvalidRequest()
|
||||
{
|
||||
@@ -221,6 +229,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(ProtocolStatusCode.InvalidRequest, reply.ProtocolStatus.Code);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that query active alarms returns payload with snapshots.</summary>
|
||||
[Fact]
|
||||
public void QueryActiveAlarms_WithHandler_ReturnsPayloadWithSnapshots()
|
||||
{
|
||||
@@ -253,6 +262,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal("Galaxy!A", handler.LastFilterPrefix);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unsubscribe routes to handler.</summary>
|
||||
[Fact]
|
||||
public void UnsubscribeAlarms_WithHandler_RoutesToHandler()
|
||||
{
|
||||
@@ -273,6 +283,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.True(handler.UnsubscribeCalled);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unsubscribe without handler is an ok noop.</summary>
|
||||
[Fact]
|
||||
public void UnsubscribeAlarms_WithoutHandler_IsOkNoop()
|
||||
{
|
||||
@@ -291,6 +302,7 @@ public sealed class AlarmCommandExecutorTests
|
||||
Assert.Equal(ProtocolStatusCode.Ok, reply.ProtocolStatus.Code);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that handler exception returns MXAccess failure status.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeAlarm_WhenHandlerThrows_ReturnsMxaccessFailure()
|
||||
{
|
||||
@@ -331,28 +343,56 @@ public sealed class AlarmCommandExecutorTests
|
||||
|
||||
private sealed class FakeAlarmHandler : IAlarmCommandHandler
|
||||
{
|
||||
/// <summary>Gets the last subscription expression.</summary>
|
||||
public string? LastSubscription { get; private set; }
|
||||
|
||||
/// <summary>Gets the last session ID.</summary>
|
||||
public string? LastSessionId { get; private set; }
|
||||
|
||||
/// <summary>Gets a value indicating whether unsubscribe was called.</summary>
|
||||
public bool UnsubscribeCalled { get; private set; }
|
||||
|
||||
/// <summary>Gets the last acknowledge alarm GUID.</summary>
|
||||
public Guid LastAckGuid { get; private set; }
|
||||
|
||||
/// <summary>Gets the last acknowledge operator name.</summary>
|
||||
public string? LastAckOperatorName { get; private set; }
|
||||
|
||||
/// <summary>Gets or sets the value returned by acknowledge.</summary>
|
||||
public int AcknowledgeReturn { get; set; }
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether acknowledge throws.</summary>
|
||||
public bool AcknowledgeThrow { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the query result snapshots.</summary>
|
||||
public IReadOnlyList<ActiveAlarmSnapshot> QueryResult { get; set; } =
|
||||
Array.Empty<ActiveAlarmSnapshot>();
|
||||
|
||||
/// <summary>Gets the last alarm filter prefix.</summary>
|
||||
public string? LastFilterPrefix { get; private set; }
|
||||
|
||||
/// <summary>Records a subscription.</summary>
|
||||
/// <param name="subscription">The subscription expression.</param>
|
||||
/// <param name="sessionId">The session identifier.</param>
|
||||
public void Subscribe(string subscription, string sessionId)
|
||||
{
|
||||
LastSubscription = subscription;
|
||||
LastSessionId = sessionId;
|
||||
}
|
||||
|
||||
/// <summary>Records an unsubscribe request.</summary>
|
||||
public void Unsubscribe()
|
||||
{
|
||||
UnsubscribeCalled = true;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge request.</summary>
|
||||
/// <param name="alarmGuid">The alarm identifier.</param>
|
||||
/// <param name="comment">The acknowledge comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
public int Acknowledge(
|
||||
Guid alarmGuid, string comment, string operatorUser,
|
||||
string operatorNode, string operatorDomain, string operatorFullName)
|
||||
@@ -366,6 +406,15 @@ public sealed class AlarmCommandExecutorTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge by name request.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The group name.</param>
|
||||
/// <param name="comment">The acknowledge comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string comment, string operatorUser, string operatorNode,
|
||||
@@ -376,21 +425,27 @@ public sealed class AlarmCommandExecutorTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Gets the last acknowledge by name tuple.</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Queries the active alarms with the given filter prefix.</summary>
|
||||
/// <param name="alarmFilterPrefix">The alarm filter prefix for the query.</param>
|
||||
public IReadOnlyList<ActiveAlarmSnapshot> QueryActive(string? alarmFilterPrefix)
|
||||
{
|
||||
LastFilterPrefix = alarmFilterPrefix;
|
||||
return QueryResult;
|
||||
}
|
||||
|
||||
/// <summary>Gets the number of poll calls.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Increments the poll count.</summary>
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess;
|
||||
/// </summary>
|
||||
public sealed class AlarmCommandHandlerTests
|
||||
{
|
||||
/// <summary>Verifies that subscribe creates a consumer and forwards the subscription when not yet subscribed.</summary>
|
||||
[Fact]
|
||||
public void Subscribe_WhenNotYetSubscribed_CreatesConsumerAndCallsSubscribe()
|
||||
{
|
||||
@@ -26,6 +27,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.Equal(@"\\HOST\Galaxy!Area", consumer.LastSubscription);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that subscribe throws when already subscribed.</summary>
|
||||
[Fact]
|
||||
public void Subscribe_WhenAlreadySubscribed_Throws()
|
||||
{
|
||||
@@ -67,6 +69,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.True(consumer.Disposed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unsubscribe disposes consumer and clears state when subscribed.</summary>
|
||||
[Fact]
|
||||
public void Unsubscribe_WhenSubscribed_DisposesConsumerAndClearsState()
|
||||
{
|
||||
@@ -82,6 +85,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.True(consumer.Disposed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unsubscribe is a no-op when not yet subscribed.</summary>
|
||||
[Fact]
|
||||
public void Unsubscribe_WithoutPriorSubscribe_IsNoop()
|
||||
{
|
||||
@@ -92,6 +96,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.False(handler.IsSubscribed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge forwards to consumer with full operator identity when subscribed.</summary>
|
||||
[Fact]
|
||||
public void Acknowledge_WhenSubscribed_ForwardsToConsumerWithFullOperatorIdentity()
|
||||
{
|
||||
@@ -109,6 +114,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.Equal("u", consumer.LastAckOperatorName);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge throws invalid operation when called before subscribe.</summary>
|
||||
[Fact]
|
||||
public void Acknowledge_BeforeSubscribe_ThrowsInvalidOperation()
|
||||
{
|
||||
@@ -120,6 +126,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
() => handler.Acknowledge(Guid.Empty, "", "", "", "", ""));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that query active returns mapped proto snapshots when consumer has alarms.</summary>
|
||||
[Fact]
|
||||
public void QueryActive_WhenConsumerHasAlarms_ReturnsMappedProtoSnapshots()
|
||||
{
|
||||
@@ -151,6 +158,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.Equal(AlarmConditionState.Active, snapshots[0].CurrentState);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that query active filters by prefix when prefix is provided.</summary>
|
||||
[Fact]
|
||||
public void QueryActive_WithPrefix_FiltersByPrefix()
|
||||
{
|
||||
@@ -173,6 +181,7 @@ public sealed class AlarmCommandHandlerTests
|
||||
Assert.Equal("Galaxy!AreaA.Tag1", filtered[0].AlarmFullReference);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that dispose unsubscribes and disposes consumer when subscribed.</summary>
|
||||
[Fact]
|
||||
public void Dispose_WhenSubscribed_UnsubscribesAndDisposesConsumer()
|
||||
{
|
||||
@@ -281,18 +290,28 @@ public sealed class AlarmCommandHandlerTests
|
||||
private sealed class FakeConsumer : IMxAccessAlarmConsumer
|
||||
{
|
||||
#pragma warning disable CS0067 // Event never invoked — fake; AlarmCommandHandler tests don't drive transitions.
|
||||
/// <summary>Emitted when an alarm state transition occurs.</summary>
|
||||
public event EventHandler<MxAlarmTransitionEvent>? AlarmTransitionEmitted;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
/// <summary>Gets the last subscription request.</summary>
|
||||
public string? LastSubscription { get; private set; }
|
||||
/// <summary>Gets the last acknowledged alarm GUID.</summary>
|
||||
public Guid LastAckGuid { get; private set; }
|
||||
/// <summary>Gets the last acknowledged operator name.</summary>
|
||||
public string? LastAckOperatorName { get; private set; }
|
||||
/// <summary>Gets or sets the return value for acknowledge operations.</summary>
|
||||
public int AcknowledgeReturn { get; set; }
|
||||
/// <summary>Gets or sets the snapshot result to return.</summary>
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotResult { get; set; } =
|
||||
Array.Empty<MxAlarmSnapshotRecord>();
|
||||
/// <summary>Gets or sets a value indicating whether to throw on subscribe.</summary>
|
||||
public bool ThrowOnSubscribe { get; set; }
|
||||
/// <summary>Gets a value indicating whether the consumer has been disposed.</summary>
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
/// <summary>Subscribes to alarms with the given subscription string.</summary>
|
||||
/// <param name="subscription">The subscription reference.</param>
|
||||
public void Subscribe(string subscription)
|
||||
{
|
||||
LastSubscription = subscription;
|
||||
@@ -302,6 +321,13 @@ public sealed class AlarmCommandHandlerTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by GUID.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByGuid(
|
||||
Guid alarmGuid, string ackComment, string ackOperatorName,
|
||||
string ackOperatorNode, string ackOperatorDomain, string ackOperatorFullName)
|
||||
@@ -311,6 +337,15 @@ public sealed class AlarmCommandHandlerTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by name.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string ackComment, string ackOperatorName, string ackOperatorNode,
|
||||
@@ -321,17 +356,22 @@ public sealed class AlarmCommandHandlerTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Gets the last acknowledge-by-name parameters.</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Returns a snapshot of active alarms.</summary>
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms() => SnapshotResult;
|
||||
|
||||
/// <summary>Gets the number of times polled.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Polls once for alarm updates.</summary>
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed = true;
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class AlarmDispatcherTests
|
||||
{
|
||||
private const string SessionId = "session-001";
|
||||
|
||||
/// <summary>Verifies that alarm transitions land in the queue with correctly mapped fields.</summary>
|
||||
[Fact]
|
||||
public void OnTransition_WhenAlarmTransitionRaised_LandsInQueueWithMappedFields()
|
||||
{
|
||||
@@ -63,6 +64,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal(ts, body.TransitionTimestamp.ToDateTime());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unchanged alarm states do not emit transitions.</summary>
|
||||
[Fact]
|
||||
public void OnTransition_WithConsecutiveUnchangedState_DoesNotEmitTransition()
|
||||
{
|
||||
@@ -89,6 +91,10 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal(0, queue.Count);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that state transitions are mapped according to the state table.</summary>
|
||||
/// <param name="previous">The previous alarm state.</param>
|
||||
/// <param name="current">The current alarm state.</param>
|
||||
/// <param name="expected">The expected transition kind.</param>
|
||||
[Theory]
|
||||
[InlineData(MxAlarmStateKind.Unspecified, MxAlarmStateKind.UnackAlm, AlarmTransitionKind.Raise)]
|
||||
[InlineData(MxAlarmStateKind.UnackAlm, MxAlarmStateKind.AckAlm, AlarmTransitionKind.Acknowledge)]
|
||||
@@ -122,6 +128,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal(expected, evt!.Event.OnAlarmTransition.TransitionKind);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that subscribe calls are forwarded to the consumer.</summary>
|
||||
[Fact]
|
||||
public void Subscribe_WhenInvoked_ForwardsToConsumer()
|
||||
{
|
||||
@@ -135,6 +142,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal(@"\\HOST\Galaxy!Area1", consumer.LastSubscription);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge calls are forwarded to the consumer with operator identity.</summary>
|
||||
[Fact]
|
||||
public void Acknowledge_WhenInvoked_ForwardsToConsumerWithFullOperatorIdentity()
|
||||
{
|
||||
@@ -158,6 +166,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal("Alice Smith", consumer.LastAckOperatorFullName);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that acknowledge-by-name calls are forwarded to the consumer.</summary>
|
||||
[Fact]
|
||||
public void AcknowledgeByName_WhenInvoked_ForwardsToConsumerWithFullTuple()
|
||||
{
|
||||
@@ -184,6 +193,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal("TestArea", consumer.LastAckByNameTuple!.Value.Group);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that consumer alarm records are mapped to proto snapshots.</summary>
|
||||
[Fact]
|
||||
public void SnapshotActiveAlarms_WhenConsumerHasRecords_MapsRecordsToProtos()
|
||||
{
|
||||
@@ -232,6 +242,7 @@ public sealed class AlarmDispatcherTests
|
||||
Assert.Equal(AlarmConditionState.ActiveAcked, snapshots[1].CurrentState);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that dispose unsubscribes the handler and disposes the consumer.</summary>
|
||||
[Fact]
|
||||
public void Dispose_WhenSubscribed_UnsubscribesHandlerAndDisposesConsumer()
|
||||
{
|
||||
@@ -260,30 +271,52 @@ public sealed class AlarmDispatcherTests
|
||||
|
||||
private sealed class FakeAlarmConsumer : IMxAccessAlarmConsumer
|
||||
{
|
||||
/// <summary>Raised when an alarm transition occurs.</summary>
|
||||
public event EventHandler<MxAlarmTransitionEvent>? AlarmTransitionEmitted;
|
||||
|
||||
/// <summary>Gets the last subscription reference.</summary>
|
||||
public string? LastSubscription { get; private set; }
|
||||
/// <summary>Gets the GUID from the last acknowledge call.</summary>
|
||||
public Guid LastAckGuid { get; private set; }
|
||||
/// <summary>Gets the comment from the last acknowledge call.</summary>
|
||||
public string? LastAckComment { get; private set; }
|
||||
/// <summary>Gets the operator name from the last acknowledge call.</summary>
|
||||
public string? LastAckOperatorName { get; private set; }
|
||||
/// <summary>Gets the operator node from the last acknowledge call.</summary>
|
||||
public string? LastAckOperatorNode { get; private set; }
|
||||
/// <summary>Gets the operator domain from the last acknowledge call.</summary>
|
||||
public string? LastAckOperatorDomain { get; private set; }
|
||||
/// <summary>Gets the operator full name from the last acknowledge call.</summary>
|
||||
public string? LastAckOperatorFullName { get; private set; }
|
||||
/// <summary>Gets or sets the return code for acknowledge operations.</summary>
|
||||
public int AcknowledgeReturn { get; set; }
|
||||
/// <summary>Gets or sets the result collection for snapshot operations.</summary>
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotResult { get; set; } =
|
||||
Array.Empty<MxAlarmSnapshotRecord>();
|
||||
/// <summary>Gets a value indicating whether this instance has been disposed.</summary>
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
/// <summary>Raises an alarm transition event.</summary>
|
||||
/// <param name="transition">The alarm transition event.</param>
|
||||
public void RaiseTransition(MxAlarmTransitionEvent transition)
|
||||
{
|
||||
AlarmTransitionEmitted?.Invoke(this, transition);
|
||||
}
|
||||
|
||||
/// <summary>Records the subscription reference.</summary>
|
||||
/// <param name="subscription">The subscription reference.</param>
|
||||
public void Subscribe(string subscription)
|
||||
{
|
||||
LastSubscription = subscription;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge-by-GUID call with operator identity.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByGuid(
|
||||
Guid alarmGuid,
|
||||
string ackComment,
|
||||
@@ -301,6 +334,15 @@ public sealed class AlarmDispatcherTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Records an acknowledge-by-name call with alarm name, provider, and group.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="ackComment">The acknowledgment comment.</param>
|
||||
/// <param name="ackOperatorName">The operator name.</param>
|
||||
/// <param name="ackOperatorNode">The operator node.</param>
|
||||
/// <param name="ackOperatorDomain">The operator domain.</param>
|
||||
/// <param name="ackOperatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByName(
|
||||
string alarmName, string providerName, string groupName,
|
||||
string ackComment, string ackOperatorName, string ackOperatorNode,
|
||||
@@ -311,20 +353,25 @@ public sealed class AlarmDispatcherTests
|
||||
return AcknowledgeReturn;
|
||||
}
|
||||
|
||||
/// <summary>Gets the last acknowledge-by-name tuple (alarm name, provider, group).</summary>
|
||||
public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; }
|
||||
|
||||
/// <summary>Returns the current snapshot result collection.</summary>
|
||||
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms()
|
||||
{
|
||||
return SnapshotResult;
|
||||
}
|
||||
|
||||
/// <summary>Gets the count of poll operations.</summary>
|
||||
public int PollCount { get; private set; }
|
||||
|
||||
/// <summary>Increments the poll count.</summary>
|
||||
public void PollOnce()
|
||||
{
|
||||
PollCount++;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed = true;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess;
|
||||
/// </summary>
|
||||
public sealed class AlarmRecordTransitionMapperTests
|
||||
{
|
||||
/// <summary>Verifies that full alarm reference uses provider!group.name format.</summary>
|
||||
[Fact]
|
||||
public void ComposeFullReference_WithProviderAndGroup_UsesProviderBangGroupDotNameFormat()
|
||||
{
|
||||
@@ -24,6 +25,7 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal("GalaxyAlarmProvider!Tank01.Level.HiHi", reference);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reference omits provider when empty.</summary>
|
||||
[Fact]
|
||||
public void ComposeFullReference_WithEmptyProvider_DropsProvider()
|
||||
{
|
||||
@@ -32,6 +34,7 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal("Tank01.Level.HiHi", reference);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reference omits group when empty.</summary>
|
||||
[Fact]
|
||||
public void ComposeFullReference_WithEmptyGroup_DropsGroup()
|
||||
{
|
||||
@@ -40,6 +43,7 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal("GalaxyAlarmProvider!GlobalAlarm", reference);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reference returns alarm name when provider and group are empty.</summary>
|
||||
[Fact]
|
||||
public void ComposeFullReference_WithEmptyProviderAndGroup_ReturnsAlarmName()
|
||||
{
|
||||
@@ -48,6 +52,9 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal("Bare", reference);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that state string parsing decodes all valid state kinds.</summary>
|
||||
/// <param name="input">The state string to parse.</param>
|
||||
/// <param name="expected">The expected decoded state kind.</param>
|
||||
[Theory]
|
||||
[InlineData("UNACK_ALM", MxAlarmStateKind.UnackAlm)]
|
||||
[InlineData("ACK_ALM", MxAlarmStateKind.AckAlm)]
|
||||
@@ -63,6 +70,10 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal(expected, AlarmRecordTransitionMapper.ParseStateKind(input));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that state pair mapping decides the correct transition kind for all pairs.</summary>
|
||||
/// <param name="previous">The previous alarm state kind.</param>
|
||||
/// <param name="current">The current alarm state kind.</param>
|
||||
/// <param name="expected">The expected transition kind.</param>
|
||||
[Theory]
|
||||
// First sighting: new alarm in *_ALM → Raise.
|
||||
[InlineData(MxAlarmStateKind.Unspecified, MxAlarmStateKind.UnackAlm, AlarmTransitionKind.Raise)]
|
||||
@@ -91,6 +102,7 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal(expected, AlarmRecordTransitionMapper.MapTransition(previous, current));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that valid XML fields assemble into correct UTC timestamp.</summary>
|
||||
[Fact]
|
||||
public void ParseTransitionTimestampUtc_WithValidXmlFields_AssemblesUtc()
|
||||
{
|
||||
@@ -109,6 +121,7 @@ public sealed class AlarmRecordTransitionMapperTests
|
||||
Assert.Equal(709, utc.Millisecond);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unparseable inputs return DateTime.MinValue.</summary>
|
||||
[Fact]
|
||||
public void ParseTransitionTimestampUtc_WithUnparseableInputs_ReturnsMinValue()
|
||||
{
|
||||
|
||||
@@ -75,17 +75,24 @@ public sealed class MxAccessComServerTests
|
||||
private readonly int registerHandle;
|
||||
private readonly List<string> calls = new();
|
||||
|
||||
/// <summary>Initializes a new instance with the specified register handle.</summary>
|
||||
/// <param name="registerHandle">The initial server handle value to return from Register.</param>
|
||||
public RecordingMxAccessServer(int registerHandle)
|
||||
{
|
||||
this.registerHandle = registerHandle;
|
||||
}
|
||||
|
||||
/// <summary>Gets the client name passed to the most recent Register call.</summary>
|
||||
public string? RegisteredClientName { get; private set; }
|
||||
|
||||
/// <summary>Gets or sets an exception to throw from the Register method.</summary>
|
||||
public Exception? ThrowOnRegister { get; set; }
|
||||
|
||||
/// <summary>Gets the recorded method calls as strings.</summary>
|
||||
public IReadOnlyList<string> Calls => calls.ToArray();
|
||||
|
||||
/// <summary>Records a Register call and returns the configured handle.</summary>
|
||||
/// <param name="clientName">The client name to record.</param>
|
||||
public int Register(string clientName)
|
||||
{
|
||||
calls.Add($"Register:{clientName}");
|
||||
@@ -98,58 +105,103 @@ public sealed class MxAccessComServerTests
|
||||
return registerHandle;
|
||||
}
|
||||
|
||||
/// <summary>Records an Unregister call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
public void Unregister(int serverHandle)
|
||||
{
|
||||
calls.Add($"Unregister:{serverHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an AddItem call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
public int AddItem(int serverHandle, string itemDefinition)
|
||||
{
|
||||
calls.Add($"AddItem:{serverHandle}:{itemDefinition}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records an AddItem2 call and returns zero.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemDefinition">The item definition string to record.</param>
|
||||
/// <param name="itemContext">The item context string to record.</param>
|
||||
public int AddItem2(int serverHandle, string itemDefinition, string itemContext)
|
||||
{
|
||||
calls.Add($"AddItem2:{serverHandle}:{itemDefinition}:{itemContext}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>Records a RemoveItem call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public void RemoveItem(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"RemoveItem:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an Advise call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public void Advise(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"Advise:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an UnAdvise call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public void UnAdvise(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"UnAdvise:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records an AdviseSupervisory call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
public void AdviseSupervisory(int serverHandle, int itemHandle)
|
||||
{
|
||||
calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Write call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
public void Write(int serverHandle, int itemHandle, object? value, int userId)
|
||||
{
|
||||
calls.Add($"Write:{serverHandle}:{itemHandle}:{value}:{userId}");
|
||||
}
|
||||
|
||||
/// <summary>Records a Write2 call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestamp">The timestamp value.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId)
|
||||
{
|
||||
calls.Add($"Write2:{serverHandle}:{itemHandle}:{value}:{timestamp}:{userId}");
|
||||
}
|
||||
|
||||
/// <summary>Records a WriteSecured call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="currentUserId">The current user identifier.</param>
|
||||
/// <param name="verifierUserId">The verifier user identifier.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value)
|
||||
{
|
||||
calls.Add($"WriteSecured:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}");
|
||||
}
|
||||
|
||||
/// <summary>Records a WriteSecured2 call.</summary>
|
||||
/// <param name="serverHandle">The MXAccess server handle.</param>
|
||||
/// <param name="itemHandle">The MXAccess item handle.</param>
|
||||
/// <param name="currentUserId">The current user identifier.</param>
|
||||
/// <param name="verifierUserId">The verifier user identifier.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <param name="timestamp">The timestamp value.</param>
|
||||
public void WriteSecured2(
|
||||
int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp)
|
||||
{
|
||||
|
||||
@@ -1441,6 +1441,7 @@ public sealed class MxAccessCommandExecutorTests
|
||||
/// <param name="adviseException">Exception to throw from Advise, if any.</param>
|
||||
/// <param name="unAdviseException">Exception to throw from UnAdvise, if any.</param>
|
||||
/// <param name="adviseSupervisoryException">Exception to throw from AdviseSupervisory, if any.</param>
|
||||
/// <param name="writeExceptionByItemHandle">Map of item handles to exceptions thrown on write.</param>
|
||||
public FakeMxAccessComObject(
|
||||
int registerHandle,
|
||||
int addItemHandle = 0,
|
||||
|
||||
@@ -227,6 +227,7 @@ public sealed class MxAccessEventMapperTests
|
||||
}
|
||||
|
||||
/// <summary>Verifies unparseable or empty timestamp input is rejected without throwing.</summary>
|
||||
/// <param name="text">Unparseable or empty timestamp string.</param>
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
|
||||
@@ -474,45 +474,73 @@ public sealed class MxAccessStaSessionTests
|
||||
private int pollCount;
|
||||
private int? lastPollThreadId;
|
||||
|
||||
/// <summary>Gets a value indicating whether the alarm client is currently subscribed.</summary>
|
||||
public bool IsSubscribed { get; private set; }
|
||||
|
||||
/// <summary>Gets the last alarm subscription name.</summary>
|
||||
public string? LastSubscription { get; private set; }
|
||||
|
||||
/// <summary>Exception thrown by PollOnce; null to succeed.</summary>
|
||||
public Exception? PollException { get; set; }
|
||||
|
||||
/// <summary>Gets the count of PollOnce calls.</summary>
|
||||
public int PollCount
|
||||
{
|
||||
get { lock (gate) return pollCount; }
|
||||
}
|
||||
|
||||
/// <summary>Gets the managed thread ID of the last PollOnce call.</summary>
|
||||
public int? LastPollThreadId
|
||||
{
|
||||
get { lock (gate) return lastPollThreadId; }
|
||||
}
|
||||
|
||||
/// <summary>Subscribes to alarm events.</summary>
|
||||
/// <param name="subscription">The subscription descriptor.</param>
|
||||
/// <param name="sessionId">The session identifier.</param>
|
||||
public void Subscribe(string subscription, string sessionId)
|
||||
{
|
||||
IsSubscribed = true;
|
||||
LastSubscription = subscription;
|
||||
}
|
||||
|
||||
/// <summary>Unsubscribes from alarm events.</summary>
|
||||
public void Unsubscribe()
|
||||
{
|
||||
IsSubscribed = false;
|
||||
}
|
||||
|
||||
/// <summary>Acknowledges an alarm by guid.</summary>
|
||||
/// <param name="alarmGuid">The alarm GUID.</param>
|
||||
/// <param name="comment">The acknowledgment comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
public int Acknowledge(Guid alarmGuid, string comment, string operatorUser,
|
||||
string operatorNode, string operatorDomain, string operatorFullName)
|
||||
=> 0;
|
||||
|
||||
/// <summary>Acknowledges an alarm by name.</summary>
|
||||
/// <param name="alarmName">The alarm name.</param>
|
||||
/// <param name="providerName">The provider name.</param>
|
||||
/// <param name="groupName">The alarm group name.</param>
|
||||
/// <param name="comment">The acknowledgment comment.</param>
|
||||
/// <param name="operatorUser">The operator user name.</param>
|
||||
/// <param name="operatorNode">The operator node name.</param>
|
||||
/// <param name="operatorDomain">The operator domain.</param>
|
||||
/// <param name="operatorFullName">The operator full name.</param>
|
||||
public int AcknowledgeByName(string alarmName, string providerName, string groupName,
|
||||
string comment, string operatorUser, string operatorNode,
|
||||
string operatorDomain, string operatorFullName)
|
||||
=> 0;
|
||||
|
||||
/// <summary>Queries active alarms.</summary>
|
||||
/// <param name="alarmFilterPrefix">Optional alarm name filter prefix.</param>
|
||||
public IReadOnlyList<ActiveAlarmSnapshot> QueryActive(string? alarmFilterPrefix)
|
||||
=> Array.Empty<ActiveAlarmSnapshot>();
|
||||
|
||||
/// <summary>Polls for alarm events once.</summary>
|
||||
public void PollOnce()
|
||||
{
|
||||
lock (gate)
|
||||
@@ -527,6 +555,7 @@ public sealed class MxAccessStaSessionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess;
|
||||
/// </summary>
|
||||
public sealed class MxAccessValueCacheTests
|
||||
{
|
||||
/// <summary>Verifies that cache returns the last value with incrementing versions.</summary>
|
||||
[Fact]
|
||||
public void Set_ThenTryGet_ReturnsLastValueWithIncrementingVersion()
|
||||
{
|
||||
@@ -45,6 +46,7 @@ public sealed class MxAccessValueCacheTests
|
||||
Assert.Equal(999, other.Value.Int32Value);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryGet returns false for unknown handles.</summary>
|
||||
[Fact]
|
||||
public void TryGet_WithUnknownHandle_ReturnsFalse()
|
||||
{
|
||||
@@ -53,6 +55,7 @@ public sealed class MxAccessValueCacheTests
|
||||
Assert.False(cache.TryGet(serverHandle: 7, itemHandle: 21, out _));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that Remove drops entries and resets versions.</summary>
|
||||
[Fact]
|
||||
public void Remove_DropsEntryAndResetsVersion()
|
||||
{
|
||||
@@ -71,6 +74,7 @@ public sealed class MxAccessValueCacheTests
|
||||
Assert.Equal(1UL, reset.Version);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that CurrentVersion returns zero for unknown handles and the latest for known ones.</summary>
|
||||
[Fact]
|
||||
public void CurrentVersion_ReturnsZeroForUnknown_AndLatestForKnown()
|
||||
{
|
||||
@@ -83,23 +87,7 @@ public sealed class MxAccessValueCacheTests
|
||||
Assert.Equal(2UL, cache.CurrentVersion(7, 21));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Worker.Tests-020: pins the contract that <c>TryWaitForUpdate</c>
|
||||
/// returns <c>false</c> when the deadline has elapsed with no
|
||||
/// <c>Set</c>, yields a default <c>CachedValue</c>, and invokes
|
||||
/// <c>pumpStep</c> at least once so MXAccess Windows messages can
|
||||
/// be dispatched. Earlier revisions of this test asserted both an
|
||||
/// elapsed-time floor (<c>stopwatch.ElapsedMilliseconds >= 60</c>)
|
||||
/// and <c>pumpCalls > 1</c> — the same wall-clock-floor race
|
||||
/// pattern Worker.Tests-003/004/013 corrected. To eliminate the
|
||||
/// timing dependency entirely (the equivalent of a manual time
|
||||
/// source for a <c>DateTime.UtcNow</c>-based deadline), the test
|
||||
/// now supplies a deadline already in the past: the loop pumps
|
||||
/// once, observes the passed deadline, and returns false
|
||||
/// deterministically without any <c>Thread.Sleep</c>. The
|
||||
/// deadline-honouring contract is what this test exists to pin;
|
||||
/// elapsed time and pump-iteration count are incidental.
|
||||
/// </summary>
|
||||
/// <summary>Verifies that TryWaitForUpdate returns false after the deadline expires.</summary>
|
||||
[Fact]
|
||||
public void TryWaitForUpdate_ReturnsFalseAfterDeadline_WhenNoSetOccurs()
|
||||
{
|
||||
@@ -126,6 +114,7 @@ public sealed class MxAccessValueCacheTests
|
||||
Assert.Equal(1, pumpCalls);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that TryWaitForUpdate returns true when the cache is updated after the baseline.</summary>
|
||||
[Fact]
|
||||
public async Task TryWaitForUpdate_ReturnsTrue_WhenSetFiresAfterBaselineVersion()
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
private const string EmptyXml =
|
||||
"<?xml version=\"1.0\"?><ALARM_RECORDS COUNT=\"0\"></ALARM_RECORDS>";
|
||||
|
||||
/// <summary>Verifies that empty XML payload returns an empty dictionary.</summary>
|
||||
[Fact]
|
||||
public void ParseSnapshotXml_WithEmptyPayload_ReturnsEmptyDictionary()
|
||||
{
|
||||
@@ -42,6 +43,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Empty(records);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that null or whitespace payload returns an empty dictionary.</summary>
|
||||
[Fact]
|
||||
public void ParseSnapshotXml_WithNullOrWhitespace_ReturnsEmptyDictionary()
|
||||
{
|
||||
@@ -49,6 +51,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Empty(WnWrapAlarmConsumer.ParseSnapshotXml(" "));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that single alarm XML payload decodes the record correctly.</summary>
|
||||
[Fact]
|
||||
public void ParseSnapshotXml_WithSingleActiveAlarm_DecodesRecord()
|
||||
{
|
||||
@@ -74,6 +77,7 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Equal(26, record.TransitionTimestampUtc.Minute);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that invalid GUIDs in XML payload are silently dropped.</summary>
|
||||
[Fact]
|
||||
public void ParseSnapshotXml_WithInvalidGuids_SilentlyDropsRecords()
|
||||
{
|
||||
@@ -83,6 +87,9 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Empty(WnWrapAlarmConsumer.ParseSnapshotXml(xml));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that dashless 32-character hex GUIDs parse correctly.</summary>
|
||||
/// <param name="hex">The dashless hex string.</param>
|
||||
/// <param name="expected">The expected canonical GUID form.</param>
|
||||
[Theory]
|
||||
[InlineData("BCC4705395424D65BDAABCDEA6A32A73", "BCC47053-9542-4D65-BDAA-BCDEA6A32A73")]
|
||||
[InlineData("00000000000000000000000000000000", "00000000-0000-0000-0000-000000000000")]
|
||||
@@ -92,6 +99,8 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Equal(new Guid(expected), guid);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that canonical dashed GUID format is accepted.</summary>
|
||||
/// <param name="canonical">The canonical GUID form.</param>
|
||||
[Theory]
|
||||
[InlineData("BCC47053-9542-4D65-BDAA-BCDEA6A32A73")]
|
||||
public void TryParseHexGuid_WithCanonicalDashedForm_Accepts(string canonical)
|
||||
@@ -100,6 +109,8 @@ public sealed class WnWrapAlarmConsumerXmlTests
|
||||
Assert.Equal(new Guid(canonical), guid);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that invalid GUID inputs are rejected.</summary>
|
||||
/// <param name="hex">The invalid GUID hex string.</param>
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
|
||||
@@ -135,11 +135,14 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
private IntPtr probeWindow = IntPtr.Zero;
|
||||
private string? registeredClass;
|
||||
|
||||
/// <summary>Initializes a new instance of the test class.</summary>
|
||||
/// <param name="output">The test output helper.</param>
|
||||
public AlarmClientWmProbeTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/// <summary>Probes the alarm client for window message behavior (requires AVEVA installed).</summary>
|
||||
[Fact(Skip = "Runtime probe — flip Skip=null on the dev rig (AVEVA installed) to capture alarm-path behavior")]
|
||||
public void ProbeAlarmClient_OnDevRig_LogsAlarmWindowMessages()
|
||||
{
|
||||
@@ -772,6 +775,7 @@ public sealed class AlarmClientWmProbeTests : IDisposable
|
||||
log.Enqueue($"[t={elapsed.Elapsed.TotalSeconds:F3}s] {line}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (wndProcHandle.IsAllocated) wndProcHandle.Free();
|
||||
|
||||
@@ -36,11 +36,14 @@ public sealed class AlarmsLiveSmokeTests
|
||||
private readonly Stopwatch elapsed = Stopwatch.StartNew();
|
||||
private readonly ConcurrentQueue<string> log = new ConcurrentQueue<string>();
|
||||
|
||||
/// <summary>Initializes a new instance of the AlarmsLiveSmokeTests class.</summary>
|
||||
/// <param name="output">Test output helper for logging.</param>
|
||||
public AlarmsLiveSmokeTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/// <summary>Verifies the alarm pipeline raises and acknowledges alarms correctly.</summary>
|
||||
[Fact(Skip = "Live dev-rig smoke test — flip Skip=null with AVEVA + the alarm flip script running. Verified working 2026-05-01.")]
|
||||
public void Alarms_FullPipelineRoundTrip_RaisesAndAcknowledges()
|
||||
{
|
||||
|
||||
@@ -46,11 +46,14 @@ public sealed class WnWrapConsumerProbeTests
|
||||
private readonly ConcurrentQueue<string> log = new ConcurrentQueue<string>();
|
||||
private readonly Stopwatch elapsed = Stopwatch.StartNew();
|
||||
|
||||
/// <summary>Initializes a new probe test with the given test output helper.</summary>
|
||||
/// <param name="output">The xUnit test output helper.</param>
|
||||
public WnWrapConsumerProbeTests(ITestOutputHelper output)
|
||||
{
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
/// <summary>Probes wnwrap consumer on dev rig and logs XML alarm stream output.</summary>
|
||||
[Fact(Skip = "Runtime probe — flip Skip=null on the dev rig (AVEVA installed) to capture wnwrapConsumer XML alarm output. Verified working 2026-05-01.")]
|
||||
public void ProbeWnWrapConsumer_OnDevRig_LogsXmlAlarmStream()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user