using XFEEDToolBox.Core.Navigation; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture]public class FlightControlCleanupTests { [Test]public async Task ScoConfirmationFailureStillSuspendsAndReleasesEveryIndependentOwnedControl() { var calls=new List();var sco=new FlightTelemetryUnavailableException("Waiting for the owned SCO to report its real stopped state."); try { await FlightControlCleanup.RunAllAsync( ()=>{calls.Add("sco");return Task.FromException(sco);}, ()=>{calls.Add("docking-reset");return Task.CompletedTask;}, ()=>{calls.Add("original-assists");return Task.CompletedTask;}, ()=>{calls.Add("primary-inputs");return Task.CompletedTask;}); } catch(FlightTelemetryUnavailableException error) {Check.That(ReferenceEquals(sco,error));Check.Equal("sco,docking-reset,original-assists,primary-inputs",string.Join(',',calls));return;} throw new Exception("Unconfirmed SCO release was swallowed"); } [Test]public async Task IndependentReleaseErrorsPreserveIdentityAndSafetyFailuresWhileStillReleasingAxes() { var identity=new InvalidDataException("Different ship");var health=new FlightHealthPauseException();var released=false; try { await FlightControlCleanup.RunAllAsync(()=>Task.FromException(identity),()=>Task.FromException(health),()=>{released=true;return Task.CompletedTask;}); } catch(AggregateException error) {Check.That(released&&error.InnerExceptions.Count==2&&ReferenceEquals(identity,error.InnerExceptions[0])&&ReferenceEquals(health,error.InnerExceptions[1])); Check.That(!NavigationRecovery.CanRetry(error,CancellationToken.None));Check.That(FlightSafetyPauseException.Present(error));return;} throw new Exception("Release failures lost"); } [Test]public async Task CleanupFailurePreservesTheActualThreatAndDiagnostics() { var primary=new FlightThreatPauseException();var release=new InvalidOperationException("Protection refused cancellation"); try{await FlightControlCleanup.RunAsync(()=>Task.FromException(primary),()=>Task.FromException(release));} catch(FlightControlCleanupException error){Check.That(ReferenceEquals(primary,error.InnerException));Check.That(ReferenceEquals(release,error.CleanupFailure));Check.That(FlightSafetyPauseException.Present(error));return;} throw new Exception("Threat lost"); } [Test]public async Task ASecondaryHealthFailureCannotConvertIdentityFailureIntoRetry() { var primary=new InvalidDataException("Identity changed"); try{await FlightControlCleanup.RunAsync(()=>Task.FromException(primary),()=>Task.FromException(new FlightHealthPauseException()));} catch(FlightControlCleanupException error){Check.That(!FlightSafetyPauseException.Present(error));return;} throw new Exception("Primary failure lost"); } [Test]public async Task UserCancellationRemainsCancellationEvenIfReleaseFails() { var cancellation=new OperationCanceledException("User stopped"); try{await FlightControlCleanup.RunAsync(()=>Task.FromException(cancellation),()=>Task.FromException(new FlightHealthPauseException()));} catch(OperationCanceledException error){Check.That(ReferenceEquals(cancellation,error));Check.That(error.Data.Contains("ControlReleaseFailure"));return;} throw new Exception("Cancellation lost"); } [Test]public async Task AReleaseFailureWithoutAPrimaryFailureRemainsTerminal() { var release=new InvalidOperationException("Release failed"); try{await FlightControlCleanup.RunAsync(()=>Task.CompletedTask,()=>Task.FromException(release));} catch(InvalidOperationException error){Check.That(ReferenceEquals(release,error));return;} throw new Exception("Release failure swallowed"); } }