using Microsoft.Extensions.Logging.Abstractions; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Host.OpcUa; namespace ZB.MOM.WW.OtOpcUa.Host.Tests.OpcUa; public sealed class FailedServiceLevelBindingCleanupTests { [Fact] public async Task Successful_cleanup_observes_stop_before_disposal_and_releases_ownership() { var events = new List(); var released = await FailedServiceLevelBindingCleanup.StopAndDisposeAsync( () => { events.Add("stop"); return Task.CompletedTask; }, () => { events.Add("dispose"); return ValueTask.CompletedTask; }, NullLogger.Instance); events.ShouldBe(new[] { "stop", "dispose" }); released.ShouldBeTrue(); } [Fact] public async Task Failed_stop_still_disposes_and_retains_ownership_for_retry() { var events = new List(); var released = await FailedServiceLevelBindingCleanup.StopAndDisposeAsync( () => { events.Add("stop"); throw new InvalidOperationException("controlled stop failure"); }, () => { events.Add("dispose"); return ValueTask.CompletedTask; }, NullLogger.Instance); events.ShouldBe(new[] { "stop", "dispose" }); released.ShouldBeFalse(); } [Fact] public async Task Failed_disposal_retains_ownership_without_escaping_the_binding_failure_handler() { var events = new List(); var released = await FailedServiceLevelBindingCleanup.StopAndDisposeAsync( () => { events.Add("stop"); return Task.CompletedTask; }, () => { events.Add("dispose"); throw new InvalidOperationException("controlled disposal failure"); }, NullLogger.Instance); events.ShouldBe(new[] { "stop", "dispose" }); released.ShouldBeFalse(); } }