Harden shutdown against logging exceptions
This commit is contained in:
+2
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Projekt und Sicherheitsziel
|
## Projekt und Sicherheitsziel
|
||||||
|
|
||||||
Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.2.1.
|
Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.2.2.
|
||||||
|
|
||||||
## Installerinvarianten
|
## Installerinvarianten
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ Die zentrale Implementierung liegt in `src/BizTalkPlatformManagementTool.Setup/I
|
|||||||
- Ein älterer kompatibler Snapshot, insbesondere aus 2.1.3, darf nicht allein anhand seines `ToolVersion`-Werts abgelehnt werden.
|
- Ein älterer kompatibler Snapshot, insbesondere aus 2.1.3, darf nicht allein anhand seines `ToolVersion`-Werts abgelehnt werden.
|
||||||
- Der echte Emergency Restore erzeugt nach Möglichkeit automatisch einen timestamp-basierten Soll/Ist-Diff aus Recovery-Quelle und Nachher-Snapshot.
|
- Der echte Emergency Restore erzeugt nach Möglichkeit automatisch einen timestamp-basierten Soll/Ist-Diff aus Recovery-Quelle und Nachher-Snapshot.
|
||||||
- Die zentrale best-effort Orchestrierung liegt in `OperationPlanExecutor`; die WMI-/Service-Zustandsprüfung bleibt im produktiven Runtime-Adapter.
|
- Die zentrale best-effort Orchestrierung liegt in `OperationPlanExecutor`; die WMI-/Service-Zustandsprüfung bleibt im produktiven Runtime-Adapter.
|
||||||
|
- Datei-Logging und optionale GUI-Logweiterleitung sind Diagnosekanäle und dürfen niemals einen fachlichen Planschritt oder dessen Fehlerbehandlung unterbrechen.
|
||||||
|
|
||||||
## Versionierung
|
## Versionierung
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.2.2] - 2026-08-19
|
||||||
|
### Fixed
|
||||||
|
- A failure in the optional GUI log sink can no longer escape step-level error handling or stop later independent shutdown/restore steps.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Failure-injection coverage for multiple consecutive unexpected exception types and a permanently failing log-display sink.
|
||||||
|
|
||||||
## [2.2.1] - 2026-08-19
|
## [2.2.1] - 2026-08-19
|
||||||
### Added
|
### Added
|
||||||
- WMI-free **Validate State** preflight with snapshot metadata, server matching and artifact counts.
|
- WMI-free **Validate State** preflight with snapshot metadata, server matching and artifact counts.
|
||||||
|
|||||||
+1
-1
@@ -89,7 +89,7 @@ Ein erfolgreicher Installer-Self-Test bestätigt Paket, Programmstart und lokale
|
|||||||
|
|
||||||
## Emergency Restore nach einem Teilabbruch
|
## Emergency Restore nach einem Teilabbruch
|
||||||
|
|
||||||
Version 2.2.1 kann einen Wiederanlauf allein aus einer erhaltenen `before.json` vorbereiten und ausführen. Eine mit Version 2.1.3 erzeugte Datei ist kompatibel; zusätzliche Plan- oder Nachher-Dateien des fehlgeschlagenen Laufs sind nicht erforderlich.
|
Version 2.2.2 kann einen Wiederanlauf allein aus einer erhaltenen `before.json` vorbereiten und ausführen. Eine mit Version 2.1.3 erzeugte Datei ist kompatibel; zusätzliche Plan- oder Nachher-Dateien des fehlgeschlagenen Laufs sind nicht erforderlich. Auch ein Fehler der GUI-Logweiterleitung ist vom fachlichen Executor isoliert und kann die späteren Planschritte nicht abbrechen.
|
||||||
|
|
||||||
1. Die erhaltene `before.json` außerhalb des Arbeitsverzeichnisses zusätzlich sichern.
|
1. Die erhaltene `before.json` außerhalb des Arbeitsverzeichnisses zusätzlich sichern.
|
||||||
2. Anwendung als Administrator starten und denselben Zielserver wählen, der im Snapshot gespeichert ist.
|
2. Anwendung als Administrator starten und denselben Zielserver wählen, der im Snapshot gespeichert ist.
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
Stand: 19.08.2026
|
Stand: 19.08.2026
|
||||||
Ausführung: Donnerstag, 20.08.2026, oder Montag, 24.08.2026
|
Ausführung: Donnerstag, 20.08.2026, oder Montag, 24.08.2026
|
||||||
Zielserver: `AV23AGPWBI01`
|
Zielserver: `AV23AGPWBI01`
|
||||||
Benötigte Toolversion: `2.2.1`
|
Benötigte Toolversion: `2.2.2`
|
||||||
|
|
||||||
## Kurzantwort
|
## Kurzantwort
|
||||||
|
|
||||||
Ja. Die vorhandene, mit Version 2.1.3 erzeugte `before.json` kann von Version 2.2.1 direkt als Recovery-Quelle verwendet werden. Zusätzliche Dateien des abgebrochenen Shutdowns sind nicht erforderlich. Die Kompatibilität wird durch einen Regressionstest abgedeckt; die echte BizTalk-/ACC-Ausführung muss noch vor Ort bestätigt werden.
|
Ja. Die vorhandene, mit Version 2.1.3 erzeugte `before.json` kann von Version 2.2.2 direkt als Recovery-Quelle verwendet werden. Zusätzliche Dateien des abgebrochenen Shutdowns sind nicht erforderlich. Die Kompatibilität wird durch einen Regressionstest abgedeckt; die echte BizTalk-/ACC-Ausführung muss noch vor Ort bestätigt werden.
|
||||||
|
|
||||||
Für diesen Teilzustand ausschließlich **Emergency Restore** verwenden. Dieser Modus startet zuerst `ENTSSO`, arbeitet danach den gespeicherten Sollzustand idempotent ab, setzt nach Einzelfehlern fort und überschreibt die Quell-`before.json` nicht.
|
Für diesen Teilzustand ausschließlich **Emergency Restore** verwenden. Dieser Modus startet zuerst `ENTSSO`, arbeitet danach den gespeicherten Sollzustand idempotent ab, setzt nach Einzelfehlern fort und überschreibt die Quell-`before.json` nicht.
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ type BizTalkPlatformManagementTool-Setup.zip.sha256.txt
|
|||||||
|
|
||||||
- [ ] Nur fortfahren, wenn beide ZIP-Hashes exakt gleich sind.
|
- [ ] Nur fortfahren, wenn beide ZIP-Hashes exakt gleich sind.
|
||||||
- [ ] ZIP in einen neuen Ordner entpacken, die laufende Toolinstanz schließen und `Setup.exe` als Administrator starten.
|
- [ ] ZIP in einen neuen Ordner entpacken, die laufende Toolinstanz schließen und `Setup.exe` als Administrator starten.
|
||||||
- [ ] Installation/Update auf Version `2.2.1` vollständig abschließen.
|
- [ ] Installation/Update auf Version `2.2.2` vollständig abschließen.
|
||||||
- [ ] Ein neues, leeres Ausgabeverzeichnis anlegen, zum Beispiel `C:\BizTalk-Recovery\2026-08-20` oder `C:\BizTalk-Recovery\2026-08-24`.
|
- [ ] Ein neues, leeres Ausgabeverzeichnis anlegen, zum Beispiel `C:\BizTalk-Recovery\2026-08-20` oder `C:\BizTalk-Recovery\2026-08-24`.
|
||||||
|
|
||||||
Wichtig: Die Recovery-Quelle außerhalb dieses Ausgabeverzeichnisses lassen. Während der Recovery nicht **Snapshot Before** anklicken, damit kein neuer Teilzustand als vermeintlicher Sollzustand gespeichert wird.
|
Wichtig: Die Recovery-Quelle außerhalb dieses Ausgabeverzeichnisses lassen. Während der Recovery nicht **Snapshot Before** anklicken, damit kein neuer Teilzustand als vermeintlicher Sollzustand gespeichert wird.
|
||||||
@@ -86,7 +86,7 @@ Die reale Reihenfolge ist:
|
|||||||
4. Orchestrations abgleichen; im Snapshot nur `Bound` gebliebene Orchestrations werden bewusst zur manuellen Kontrolle markiert.
|
4. Orchestrations abgleichen; im Snapshot nur `Bound` gebliebene Orchestrations werden bewusst zur manuellen Kontrolle markiert.
|
||||||
5. Receive Locations zuletzt abgleichen.
|
5. Receive Locations zuletzt abgleichen.
|
||||||
|
|
||||||
Vor jeder Mutation liest Version 2.2.1 den Istzustand. Ein bereits korrekter Zustand wird als `AlreadySatisfied` protokolliert und nicht erneut per WMI geändert. Ein einzelner Fehler wird als `Failed` gespeichert; alle späteren unabhängigen Schritte werden trotzdem versucht.
|
Vor jeder Mutation liest Version 2.2.2 den Istzustand. Ein bereits korrekter Zustand wird als `AlreadySatisfied` protokolliert und nicht erneut per WMI geändert. Ein einzelner Fehler wird als `Failed` gespeichert; alle späteren unabhängigen Schritte werden trotzdem versucht. Auch ein Fehler der optionalen GUI-Loganzeige kann die fachliche Ausführung nicht unterbrechen.
|
||||||
|
|
||||||
## 5. Ergebnis abnehmen
|
## 5. Ergebnis abnehmen
|
||||||
|
|
||||||
@@ -112,8 +112,8 @@ Wenn sie deaktiviert ist, obwohl `before.json` sie aktiviert erwartet, versucht
|
|||||||
|
|
||||||
## Wenn der Lauf erneut unterbrochen wird
|
## Wenn der Lauf erneut unterbrochen wird
|
||||||
|
|
||||||
Die ursprüngliche `before.json` bleibt unverändert. Version 2.2.1 kann mit derselben Recovery-Quelle erneut gestartet werden, weil bereits erreichte Zustände als `AlreadySatisfied` übersprungen werden. Vor dem Wiederholen immer den jüngsten Ergebnisreport sichern und prüfen, welcher Schritt tatsächlich fehlgeschlagen ist.
|
Die ursprüngliche `before.json` bleibt unverändert. Version 2.2.2 kann mit derselben Recovery-Quelle erneut gestartet werden, weil bereits erreichte Zustände als `AlreadySatisfied` übersprungen werden. Vor dem Wiederholen immer den jüngsten Ergebnisreport sichern und prüfen, welcher Schritt tatsächlich fehlgeschlagen ist.
|
||||||
|
|
||||||
## Go/No-Go für PROD
|
## Go/No-Go für PROD
|
||||||
|
|
||||||
ACC ist erst ein belastbarer PROD-Nachweis, wenn der echte Emergency Restore abgeschlossen wurde, der Ergebnisreport vollständig ist, der automatische Diff geprüft wurde und jeder verbliebene `Failed`-Schritt fachlich behandelt wurde. Die lokalen 24 Regressionstests und der Self-Test ersetzen diesen realen BizTalk-/Windows-Nachweis nicht.
|
ACC ist erst ein belastbarer PROD-Nachweis, wenn der echte Emergency Restore abgeschlossen wurde, der Ergebnisreport vollständig ist, der automatische Diff geprüft wurde und jeder verbliebene `Failed`-Schritt fachlich behandelt wurde. Die lokalen 27 Regressionstests und der Self-Test ersetzen diesen realen BizTalk-/Windows-Nachweis nicht.
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ Der neue Button **Emergency Restore** benötigt außer der erhaltenen `before.js
|
|||||||
- Ein 2.1.3-Snapshot wird ausdrücklich als kompatible Recovery-Quelle regressionsgetestet; `ToolVersion` ist keine Versionssperre.
|
- Ein 2.1.3-Snapshot wird ausdrücklich als kompatible Recovery-Quelle regressionsgetestet; `ToolVersion` ist keine Versionssperre.
|
||||||
- Nach einem echten Emergency Restore wird bei erfolgreichem Nachher-Snapshot automatisch `emergency-restore-diff-*` als JSON, CSV und HTML geschrieben.
|
- Nach einem echten Emergency Restore wird bei erfolgreichem Nachher-Snapshot automatisch `emergency-restore-diff-*` als JSON, CSV und HTML geschrieben.
|
||||||
|
|
||||||
|
## Zusätzliche Exception-Grenze in Version 2.2.2
|
||||||
|
|
||||||
|
Ein erneuter Audit des gesamten Shutdown-Kontrollflusses bestätigte, dass WMI-Lookup, Istzustandsprüfung, Methodenaufruf, Zustandswartezeit und Objektfreigabe innerhalb der Per-Step-Exception-Grenze liegen. Zusätzlich ist nun auch die optionale GUI-Weiterleitung des Loggers vollständig isoliert: Selbst wenn die Anzeige eines Logeintrags eine Exception wirft, laufen der aktuelle fachliche Schritt und alle späteren unabhängigen Schritte weiter. Failure-Injection-Tests decken mehrere aufeinanderfolgende `InvalidOperationException`-, `IOException`- und `TimeoutException`-Fehler sowie einen dauerhaft werfenden Log-Sink ab.
|
||||||
|
|
||||||
### Globale Reihenfolge
|
### Globale Reihenfolge
|
||||||
|
|
||||||
Shutdown:
|
Shutdown:
|
||||||
@@ -86,11 +90,11 @@ Die portable Regressionstestsuite enthält ab Version 2.2.0 unter anderem folgen
|
|||||||
- globale Shutdown-/Restore-Reihenfolge mit mehreren Anwendungen,
|
- globale Shutdown-/Restore-Reihenfolge mit mehreren Anwendungen,
|
||||||
- Kurzname/FQDN-Gleichheit für Host-Instance-Schritte.
|
- Kurzname/FQDN-Gleichheit für Host-Instance-Schritte.
|
||||||
|
|
||||||
Die lokale Suite umfasst ab Version 2.2.1 insgesamt 24 Tests einschließlich der expliziten 2.1.3-Snapshot-Kompatibilität. Die abschließende Freigabe für PROD benötigt weiterhin einen Windows-/BizTalk-Test auf ACC: echter Scheduler-Fehlerpfad, vollständige Fortsetzung, Ergebnisreport, Emergency-Dry-run und realer Wiederanlauf aus einer Kopie der vorhandenen `before.json`.
|
Die lokale Suite umfasst ab Version 2.2.2 insgesamt 27 Tests einschließlich der expliziten 2.1.3-Snapshot-Kompatibilität, zusätzlicher Failure-Injection für unerwartete Exceptions und eines vollständigen Shutdown-Fortsetzungstests über Receive Location, Orchestration, Send Port und Host Instance. Die abschließende Freigabe für PROD benötigt weiterhin einen Windows-/BizTalk-Test auf ACC: echter Scheduler-Fehlerpfad, vollständige Fortsetzung, Ergebnisreport, Emergency-Dry-run und realer Wiederanlauf aus einer Kopie der vorhandenen `before.json`.
|
||||||
|
|
||||||
## PROD-Freigabekriterien
|
## PROD-Freigabekriterien
|
||||||
|
|
||||||
- Version 2.2.1 installieren und Installer-Self-Test bestätigen.
|
- Version 2.2.2 installieren und Installer-Self-Test bestätigen.
|
||||||
- `Diagnose` und einen vollständigen Shutdown-Dry-run ausführen.
|
- `Diagnose` und einen vollständigen Shutdown-Dry-run ausführen.
|
||||||
- Kontrolliert nachweisen, dass eine isolierte Scheduler-Exception spätere Schritte nicht blockiert.
|
- Kontrolliert nachweisen, dass eine isolierte Scheduler-Exception spätere Schritte nicht blockiert.
|
||||||
- `shutdown-result.json` und Nachher-Snapshot prüfen.
|
- `shutdown-result.json` und Nachher-Snapshot prüfen.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace BizTalkPlatformManagementTool.Setup
|
|||||||
private const string ProductName = "BizTalk Platform Management Tool";
|
private const string ProductName = "BizTalk Platform Management Tool";
|
||||||
|
|
||||||
/// <summary>Aktuelle Produktversion des Installers und Uninstall-Eintrags.</summary>
|
/// <summary>Aktuelle Produktversion des Installers und Uninstall-Eintrags.</summary>
|
||||||
private const string ProductVersion = "2.2.1";
|
private const string ProductVersion = "2.2.2";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wartezeiten zwischen Wiederholungen atomarer Verzeichnisverschiebungen.
|
/// Wartezeiten zwischen Wiederholungen atomarer Verzeichnisverschiebungen.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace BizTalkPlatformManagementTool.Setup
|
|||||||
{
|
{
|
||||||
AutoSize = true,
|
AutoSize = true,
|
||||||
Font = new Font(Font.FontFamily, 14, FontStyle.Bold),
|
Font = new Font(Font.FontFamily, 14, FontStyle.Bold),
|
||||||
Text = "BizTalk Platform Management Tool 2.2.1"
|
Text = "BizTalk Platform Management Tool 2.2.2"
|
||||||
});
|
});
|
||||||
root.Controls.Add(new Label
|
root.Controls.Add(new Label
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyProduct("BizTalk Platform Management Tool")]
|
[assembly: AssemblyProduct("BizTalk Platform Management Tool")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
[assembly: Guid("675b68a9-bd80-46a5-b8c5-3b11b0b374e2")]
|
[assembly: Guid("675b68a9-bd80-46a5-b8c5-3b11b0b374e2")]
|
||||||
[assembly: AssemblyVersion("2.2.1.0")]
|
[assembly: AssemblyVersion("2.2.2.0")]
|
||||||
[assembly: AssemblyFileVersion("2.2.1.0")]
|
[assembly: AssemblyFileVersion("2.2.2.0")]
|
||||||
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<assemblyIdentity version="2.2.1.0" name="BizTalkPlatformManagementTool.Setup" />
|
<assemblyIdentity version="2.2.2.0" name="BizTalkPlatformManagementTool.Setup" />
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
<security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false" /></requestedPrivileges></security>
|
<security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false" /></requestedPrivileges></security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyCopyright("Copyright © 2026")]
|
[assembly: AssemblyCopyright("Copyright © 2026")]
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
[assembly: Guid("2c5b2c0a-f407-46c2-9e3b-1fa09fa8445a")]
|
[assembly: Guid("2c5b2c0a-f407-46c2-9e3b-1fa09fa8445a")]
|
||||||
[assembly: AssemblyVersion("2.2.1.0")]
|
[assembly: AssemblyVersion("2.2.2.0")]
|
||||||
[assembly: AssemblyFileVersion("2.2.1.0")]
|
[assembly: AssemblyFileVersion("2.2.2.0")]
|
||||||
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace BizTalkPlatformManagementTool.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current tool version written into generated snapshots.
|
/// Current tool version written into generated snapshots.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Version = "2.2.1-net461";
|
public const string Version = "2.2.2-net461";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fallback application name used when WMI does not expose an application property.
|
/// Fallback application name used when WMI does not expose an application property.
|
||||||
|
|||||||
@@ -171,8 +171,23 @@ namespace BizTalkPlatformManagementTool.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
_sink(entry);
|
_sink(entry);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Auch die optionale GUI-Weiterleitung ist nur ein Diagnosekanal. Ein Fehler
|
||||||
|
// dort darf insbesondere nicht die Exception-Behandlung eines BizTalk-Schritts
|
||||||
|
// erneut unterbrechen und dadurch alle späteren Planschritte verhindern.
|
||||||
|
WriteToFile(new LogEntry
|
||||||
|
{
|
||||||
|
Timestamp = DateTime.Now,
|
||||||
|
Level = LogLevel.Warning,
|
||||||
|
Message = "Log display sink failed; runtime operation continues. Error: " + ex.Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Appends one entry to the current daily log file.
|
/// Appends one entry to the current daily log file.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<assemblyIdentity version="2.2.1.0" name="BizTalkPlatformManagementTool" />
|
<assemblyIdentity version="2.2.2.0" name="BizTalkPlatformManagementTool" />
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges>
|
<requestedPrivileges>
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ namespace BizTalkPlatformManagementTool.Tests
|
|||||||
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
|
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
|
||||||
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
|
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
|
||||||
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
|
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
|
||||||
|
Run("ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure", ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure);
|
||||||
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
|
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
|
||||||
Run("HostInstancePlanAcceptsShortAndFqdnServer", HostInstancePlanAcceptsShortAndFqdnServer);
|
Run("HostInstancePlanAcceptsShortAndFqdnServer", HostInstancePlanAcceptsShortAndFqdnServer);
|
||||||
Run("PlanExecutionContinuesAfterSchedulerFailure", PlanExecutionContinuesAfterSchedulerFailure);
|
Run("PlanExecutionContinuesAfterSchedulerFailure", PlanExecutionContinuesAfterSchedulerFailure);
|
||||||
|
Run("PlanExecutionContinuesAfterMultipleUnexpectedExceptions", PlanExecutionContinuesAfterMultipleUnexpectedExceptions);
|
||||||
|
Run("LoggerSinkFailureCannotAbortPlanExecution", LoggerSinkFailureCannotAbortPlanExecution);
|
||||||
Run("PlanExecutionSkipsAlreadySatisfiedState", PlanExecutionSkipsAlreadySatisfiedState);
|
Run("PlanExecutionSkipsAlreadySatisfiedState", PlanExecutionSkipsAlreadySatisfiedState);
|
||||||
Run("ExecutionReportRoundTripPreservesFailure", ExecutionReportRoundTripPreservesFailure);
|
Run("ExecutionReportRoundTripPreservesFailure", ExecutionReportRoundTripPreservesFailure);
|
||||||
Run("CsvNeutralizesFormulaValues", CsvNeutralizesFormulaValues);
|
Run("CsvNeutralizesFormulaValues", CsvNeutralizesFormulaValues);
|
||||||
@@ -163,6 +166,22 @@ namespace BizTalkPlatformManagementTool.Tests
|
|||||||
Assert(lastSendPort < firstHost, "host instances were not globally last");
|
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>
|
/// <summary>Prüft ENTSSO als erste Voraussetzung des Emergency Restore.</summary>
|
||||||
private static void EmergencyRestorePlanStartsSsoFirst()
|
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");
|
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>
|
/// <summary>Prüft idempotentes Überspringen eines bereits erreichten Sollzustands.</summary>
|
||||||
private static void PlanExecutionSkipsAlreadySatisfiedState()
|
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>
|
/// <summary>Erstellt einen minimalen Snapshot für Vergleiche und Planprüfungen.</summary>
|
||||||
/// <param name="application">Der Name der Testanwendung.</param>
|
/// <param name="application">Der Name der Testanwendung.</param>
|
||||||
/// <param name="port">Der Name des Test-Send-Ports.</param>
|
/// <param name="port">Der Name des Test-Send-Ports.</param>
|
||||||
|
|||||||
Reference in New Issue
Block a user