fix(core): resolve Low code-review findings (Core-004,008,009,010,011,012)
- Core-004: add ConfigureAwait(false) to DriverHost.RegisterAsync / UnregisterAsync / DisposeAsync. - Core-008: rewrite the BuildAddressSpaceAsync XML doc to correctly name the caller (OpcUaApplicationHost.PopulateAddressSpaces) that owns the per-driver isolation. - Core-009: snapshot DriverResilienceOptions once per non-idempotent write in CapabilityInvoker.ExecuteWriteAsync. - Core-010: switch DriverResilienceOptions.Resolve to TryGetValue with a diagnostic error message when a tier table is missing a capability. - Core-011: add an optional diagnostic callback to PermissionTrieBuilder so production callers can surface scope-path mismatches. - Core-012: correct the stale WedgeDetector ctor summary and add the Reconnecting row to DriverHealthReport's state matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -143,6 +143,41 @@ public sealed class GenericDriverNodeManagerTests
|
||||
nm.BuildAddressSpaceAsync(new RecordingBuilder(), CancellationToken.None));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core-008 regression: the XML doc states exception isolation is the caller's
|
||||
/// responsibility — exceptions from <see cref="ITagDiscovery.DiscoverAsync"/> must propagate
|
||||
/// out of <c>BuildAddressSpaceAsync</c> unhandled so the Server layer's per-driver try/catch
|
||||
/// (<c>OpcUaApplicationHost.PopulateAddressSpaces</c>) can mark the subtree Faulted.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task BuildAddressSpaceAsync_Propagates_Discovery_Exceptions_To_Caller()
|
||||
{
|
||||
var driver = new ThrowingDiscoveryDriver();
|
||||
using var nm = new GenericDriverNodeManager(driver);
|
||||
|
||||
var ex = await Should.ThrowAsync<InvalidOperationException>(() =>
|
||||
nm.BuildAddressSpaceAsync(new RecordingBuilder(), CancellationToken.None));
|
||||
ex.Message.ShouldBe("discovery boom",
|
||||
"exceptions from DiscoverAsync must propagate unhandled — exception isolation is the caller's responsibility (e.g. OpcUaApplicationHost)");
|
||||
}
|
||||
|
||||
/// <summary>Driver whose DiscoverAsync throws — exercises the exception-isolation boundary.</summary>
|
||||
private sealed class ThrowingDiscoveryDriver : IDriver, ITagDiscovery
|
||||
{
|
||||
public string DriverInstanceId => "throwing";
|
||||
public string DriverType => "Throwing";
|
||||
|
||||
public Task InitializeAsync(string _, CancellationToken __) => Task.CompletedTask;
|
||||
public Task ReinitializeAsync(string _, CancellationToken __) => Task.CompletedTask;
|
||||
public Task ShutdownAsync(CancellationToken _) => Task.CompletedTask;
|
||||
public DriverHealth GetHealth() => new(DriverState.Healthy, null, null);
|
||||
public long GetMemoryFootprint() => 0;
|
||||
public Task FlushOptionalCachesAsync(CancellationToken _) => Task.CompletedTask;
|
||||
|
||||
public Task DiscoverAsync(IAddressSpaceBuilder builder, CancellationToken ct)
|
||||
=> throw new InvalidOperationException("discovery boom");
|
||||
}
|
||||
|
||||
// --- test doubles ---
|
||||
|
||||
private sealed class FakeDriver : IDriver, ITagDiscovery, IAlarmSource
|
||||
|
||||
Reference in New Issue
Block a user