Harden shutdown against logging exceptions
This commit is contained in:
@@ -27,9 +27,12 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
|
||||
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
|
||||
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
|
||||
Run("ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure", ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure);
|
||||
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
|
||||
Run("HostInstancePlanAcceptsShortAndFqdnServer", HostInstancePlanAcceptsShortAndFqdnServer);
|
||||
Run("PlanExecutionContinuesAfterSchedulerFailure", PlanExecutionContinuesAfterSchedulerFailure);
|
||||
Run("PlanExecutionContinuesAfterMultipleUnexpectedExceptions", PlanExecutionContinuesAfterMultipleUnexpectedExceptions);
|
||||
Run("LoggerSinkFailureCannotAbortPlanExecution", LoggerSinkFailureCannotAbortPlanExecution);
|
||||
Run("PlanExecutionSkipsAlreadySatisfiedState", PlanExecutionSkipsAlreadySatisfiedState);
|
||||
Run("ExecutionReportRoundTripPreservesFailure", ExecutionReportRoundTripPreservesFailure);
|
||||
Run("CsvNeutralizesFormulaValues", CsvNeutralizesFormulaValues);
|
||||
@@ -163,6 +166,22 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Assert(lastSendPort < firstHost, "host instances were not globally last");
|
||||
}
|
||||
|
||||
/// <summary>Prüft den vollständigen Shutdown-Fortgang von Receive Location bis Host Instance nach einem frühen Fehler.</summary>
|
||||
private static void ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure()
|
||||
{
|
||||
var snapshot = Snapshot("APP", "SEND", ArtifactStates.SendPortStarted);
|
||||
snapshot.Applications[0].ReceiveLocations.Add(new ReceiveLocationState { Application = "APP", Name = "RV_PMP_Trigger_Schedule", Enabled = true });
|
||||
snapshot.Applications[0].Orchestrations.Add(new OrchestrationState { Application = "APP", Name = "ORCHESTRATION", OrchestrationStatus = ArtifactStates.OrchestrationStarted });
|
||||
snapshot.HostInstances.Add(new HostInstanceState { InstanceName = "HOST:SERVER", HostName = "HOST", Server = snapshot.Server, RawState = ArtifactStates.HostStarted });
|
||||
|
||||
var plan = new BizTalkOperationService(null).CreateShutdownPlan(snapshot, snapshot.Server);
|
||||
var runtime = new FakeOperationStepRuntime { FailingName = "RV_PMP_Trigger_Schedule" };
|
||||
var report = new OperationPlanExecutor(null).Execute(plan, TestOptions(false), runtime);
|
||||
|
||||
Assert(runtime.Calls.SequenceEqual(new[] { "RV_PMP_Trigger_Schedule", "ORCHESTRATION", "SEND", "HOST:SERVER" }), "shutdown did not continue through every later artifact category");
|
||||
Assert(report.FailedCount == 1 && report.SucceededCount == 3, "cross-category shutdown outcome is incomplete");
|
||||
}
|
||||
|
||||
/// <summary>Prüft ENTSSO als erste Voraussetzung des Emergency Restore.</summary>
|
||||
private static void EmergencyRestorePlanStartsSsoFirst()
|
||||
{
|
||||
@@ -202,6 +221,27 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Assert(report.Steps[1].Error.Contains("Microsoft.BizTalk.Scheduler"), "nested Scheduler exception missing from report");
|
||||
}
|
||||
|
||||
/// <summary>Prüft die Fortsetzung auch bei mehreren unterschiedlichen, nicht adapterspezifischen Exceptions.</summary>
|
||||
private static void PlanExecutionContinuesAfterMultipleUnexpectedExceptions()
|
||||
{
|
||||
var runtime = new MultipleFailureOperationStepRuntime();
|
||||
var report = new OperationPlanExecutor(null).Execute(TestPlan("INVALID", "IO", "TIMEOUT", "CONTINUE"), TestOptions(false), runtime);
|
||||
Assert(runtime.Calls.SequenceEqual(new[] { "INVALID", "IO", "TIMEOUT", "CONTINUE" }), "an unexpected exception stopped later independent steps");
|
||||
Assert(report.FailedCount == 3 && report.SucceededCount == 1, "multiple failure summary is incomplete");
|
||||
Assert(report.Steps.All(x => !string.IsNullOrWhiteSpace(x.FinishedAt)), "a failure result has no completion timestamp");
|
||||
}
|
||||
|
||||
/// <summary>Prüft, dass selbst eine defekte GUI-Logweiterleitung keine fachlichen Schritte verhindert.</summary>
|
||||
private static void LoggerSinkFailureCannotAbortPlanExecution()
|
||||
{
|
||||
var logger = new OperationLogger(entry => { throw new InvalidOperationException("simulated UI sink failure"); });
|
||||
var runtime = new FakeOperationStepRuntime { FailingName = "TWO" };
|
||||
var report = new OperationPlanExecutor(logger).Execute(TestPlan("ONE", "TWO", "THREE"), TestOptions(false), runtime);
|
||||
Assert(runtime.Calls.SequenceEqual(new[] { "ONE", "TWO", "THREE" }), "logging failure stopped plan execution");
|
||||
Assert(report.SucceededCount == 2 && report.FailedCount == 1, "logging failure changed business outcomes");
|
||||
Assert(report.Steps[1].Error.Contains("Microsoft.BizTalk.Scheduler"), "business failure was lost while the log sink was failing");
|
||||
}
|
||||
|
||||
/// <summary>Prüft idempotentes Überspringen eines bereits erreichten Sollzustands.</summary>
|
||||
private static void PlanExecutionSkipsAlreadySatisfiedState()
|
||||
{
|
||||
@@ -610,6 +650,32 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Wirft verschiedene Exception-Typen und liefert danach wieder ein erfolgreiches Ergebnis.</summary>
|
||||
private sealed class MultipleFailureOperationStepRuntime : IOperationStepRuntime
|
||||
{
|
||||
/// <summary>Initialisiert die vollständige Aufrufliste.</summary>
|
||||
public MultipleFailureOperationStepRuntime()
|
||||
{
|
||||
Calls = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>Gets all attempted step names.</summary>
|
||||
public List<string> Calls { get; private set; }
|
||||
|
||||
/// <summary>Wirft je nach Testschritt eine andere unerwartete Ausnahme.</summary>
|
||||
/// <param name="step">Der auszuführende Testschritt.</param>
|
||||
/// <param name="options">Die ungenutzten Testoptionen.</param>
|
||||
/// <returns>Erfolg für den abschließenden Fortsetzungsschritt.</returns>
|
||||
public RuntimeStepOutcome Execute(OperationStep step, OperationOptions options)
|
||||
{
|
||||
Calls.Add(step.Name);
|
||||
if (step.Name == "INVALID") throw new InvalidOperationException("unexpected state error");
|
||||
if (step.Name == "IO") throw new IOException("unexpected provider I/O error");
|
||||
if (step.Name == "TIMEOUT") throw new TimeoutException("unexpected provider timeout");
|
||||
return RuntimeStepOutcome.Succeeded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Erstellt einen minimalen Snapshot für Vergleiche und Planprüfungen.</summary>
|
||||
/// <param name="application">Der Name der Testanwendung.</param>
|
||||
/// <param name="port">Der Name des Test-Send-Ports.</param>
|
||||
|
||||
Reference in New Issue
Block a user