Resolve 6 of 7 stability review findings and close test coverage gaps
Fixes P1 StaComThread hang (crash-path faulting via WorkItem queue), P1 subscription fire-and-forget (block+log or ContinueWith on 5 call sites), P2 continuation point leak (PurgeExpired on Retrieve/Release), P2 dashboard bind failure (localhost prefix, bool Start), P3 background loop double-start (task handles + join on stop in 3 files), and P3 config logging exposure (SqlConnectionStringBuilder password masking). Adds FakeMxAccessClient fault injection and 12 new tests. Documents required runtime assemblies in ServiceHosting.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,6 +46,26 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// </summary>
|
||||
public int ReconnectCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set, <see cref="SubscribeAsync"/> returns a faulted task with this exception.
|
||||
/// </summary>
|
||||
public Exception? SubscribeException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set, <see cref="UnsubscribeAsync"/> returns a faulted task with this exception.
|
||||
/// </summary>
|
||||
public Exception? UnsubscribeException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set, <see cref="ReadAsync"/> returns a faulted task with this exception.
|
||||
/// </summary>
|
||||
public Exception? ReadException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When set, <see cref="WriteAsync"/> returns a faulted task with this exception.
|
||||
/// </summary>
|
||||
public Exception? WriteException { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when tests explicitly simulate a connection-state transition.
|
||||
/// </summary>
|
||||
@@ -82,6 +102,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <param name="callback">The callback that should receive simulated value changes.</param>
|
||||
public Task SubscribeAsync(string fullTagReference, Action<string, Vtq> callback)
|
||||
{
|
||||
if (SubscribeException != null)
|
||||
return Task.FromException(SubscribeException);
|
||||
_subscriptions[fullTagReference] = callback;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -92,6 +114,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <param name="fullTagReference">The Galaxy attribute reference to stop monitoring.</param>
|
||||
public Task UnsubscribeAsync(string fullTagReference)
|
||||
{
|
||||
if (UnsubscribeException != null)
|
||||
return Task.FromException(UnsubscribeException);
|
||||
_subscriptions.TryRemove(fullTagReference, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -104,6 +128,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <returns>The seeded VTQ value or a bad not-connected VTQ when the tag was not populated.</returns>
|
||||
public Task<Vtq> ReadAsync(string fullTagReference, CancellationToken ct = default)
|
||||
{
|
||||
if (ReadException != null)
|
||||
return Task.FromException<Vtq>(ReadException);
|
||||
if (TagValues.TryGetValue(fullTagReference, out var vtq))
|
||||
return Task.FromResult(vtq);
|
||||
return Task.FromResult(Vtq.Bad(Quality.BadNotConnected));
|
||||
@@ -118,6 +144,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <returns>A completed task returning the configured write outcome.</returns>
|
||||
public Task<bool> WriteAsync(string fullTagReference, object value, CancellationToken ct = default)
|
||||
{
|
||||
if (WriteException != null)
|
||||
return Task.FromException<bool>(WriteException);
|
||||
WrittenValues.Add((fullTagReference, value));
|
||||
if (WriteResult)
|
||||
TagValues[fullTagReference] = Vtq.Good(value);
|
||||
|
||||
Reference in New Issue
Block a user