Make shutdown checkpoint dialogs unmistakable

This commit is contained in:
2026-08-27 20:17:16 +02:00
parent 61b5b7e452
commit 8b1c14b892
19 changed files with 385 additions and 48 deletions
@@ -7,6 +7,7 @@ using System.Text;
using BizTalkPlatformManagementTool.Models;
using BizTalkPlatformManagementTool.Services;
using BizTalkPlatformManagementTool.Setup;
using BizTalkPlatformManagementTool.Ui;
namespace BizTalkPlatformManagementTool.Tests
{
@@ -28,10 +29,12 @@ namespace BizTalkPlatformManagementTool.Tests
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
Run("ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations", ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations);
Run("ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure", ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure);
Run("ShutdownCheckpointDeclineStopsLaterPhases", ShutdownCheckpointDeclineStopsLaterPhases);
Run("ShutdownCheckpointMissingHandlerFailsClosed", ShutdownCheckpointMissingHandlerFailsClosed);
Run("ShutdownCheckpointDryRunDoesNotPrompt", ShutdownCheckpointDryRunDoesNotPrompt);
Run("ShutdownOperatorPromptsDistinguishDryRunAndRealDecision", ShutdownOperatorPromptsDistinguishDryRunAndRealDecision);
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
Run("HostInstancePlanAcceptsShortAndFqdnServer", HostInstancePlanAcceptsShortAndFqdnServer);
Run("PlanExecutionContinuesAfterSchedulerFailure", PlanExecutionContinuesAfterSchedulerFailure);
@@ -182,6 +185,18 @@ namespace BizTalkPlatformManagementTool.Tests
Assert(lastSendPort < firstHost, "host instances were not globally last");
}
/// <summary>Prüft den Checkpoint auch dann, wenn keine aktive Receive Location als Planzeile existiert.</summary>
private static void ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations()
{
var snapshot = Snapshot("APP", "SEND", ArtifactStates.SendPortStarted);
var plan = new BizTalkOperationService(null).CreateShutdownPlan(snapshot, snapshot.Server);
var checkpoint = plan.Steps.FindIndex(x => x.Kind == OperationStepKind.OperatorCheckpoint.ToString());
var sendPort = plan.Steps.FindIndex(x => x.Kind == OperationStepKind.SendPort.ToString());
Assert(checkpoint >= 0, "later shutdown work did not create an inbound-drain checkpoint");
Assert(checkpoint < sendPort, "checkpoint was not placed before later shutdown work");
}
/// <summary>Prüft, dass Scheduler-Erkennung auch nach Planpersistenz möglich bleibt.</summary>
private static void ScheduledReceivePlanCarriesAdapterMetadata()
{
@@ -430,6 +445,25 @@ namespace BizTalkPlatformManagementTool.Tests
Assert(!report.OperatorStopped && string.IsNullOrWhiteSpace(report.CheckpointDecision), "dry-run persisted a real checkpoint decision");
}
/// <summary>Prüft, dass Dry-run-Hinweis und echter Entscheidungsdialog nicht verwechselt werden können.</summary>
private static void ShutdownOperatorPromptsDistinguishDryRunAndRealDecision()
{
var snapshot = Snapshot("APP", "SEND", ArtifactStates.SendPortStarted);
snapshot.Applications[0].ReceiveLocations.Add(new ReceiveLocationState { Application = "APP", Name = "RL", Enabled = true });
var plan = new BizTalkOperationService(null).CreateShutdownPlan(snapshot, snapshot.Server);
var dryRun = ShutdownOperatorPrompts.BuildDryRunNotice(plan, @"C:\evidence\shutdown-plan.json");
var prepared = ShutdownOperatorPrompts.BuildPreparedPlanConfirmation("Shutdown", plan, snapshot.Server, @"C:\evidence\shutdown-plan.json");
var progress = new OperationExecutionReport();
progress.Steps.Add(new OperationStepResult { Kind = OperationStepKind.ReceiveLocation.ToString(), Outcome = OperationStepOutcomes.Succeeded });
progress.Steps.Add(new OperationStepResult { Kind = OperationStepKind.ReceiveLocation.ToString(), Outcome = OperationStepOutcomes.Failed });
var checkpoint = ShutdownOperatorPrompts.BuildInboundDrainConfirmation(progress);
Assert(dryRun.Contains("No receive location is disabled") && dryRun.Contains("will not open"), "dry-run notice does not explain the missing real dialog");
Assert(prepared.Contains("second, explicit decision"), "real-plan confirmation does not announce the later checkpoint dialog");
Assert(checkpoint.Contains("shutdown is PAUSED") && checkpoint.Contains("Disabled successfully: 1") && checkpoint.Contains("Failed: 1"), "checkpoint decision does not expose its blocking state and receive results");
Assert(checkpoint.Contains("closing this dialog = stop safely"), "checkpoint close behavior is not explicit");
}
/// <summary>Prüft ENTSSO als erste Voraussetzung des Emergency Restore.</summary>
private static void EmergencyRestorePlanStartsSsoFirst()
{