Improve installer diagnostics and rollback reporting
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -25,6 +26,9 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Run("InstallerDoesNotMutateOnStagingFailure", InstallerDoesNotMutateOnStagingFailure);
|
||||
Run("InstallerRollsBackFailedActivatedSelfTest", InstallerRollsBackFailedActivatedSelfTest);
|
||||
Run("InstallerUninstallRemovesProgramDirectory", InstallerUninstallRemovesProgramDirectory);
|
||||
Run("InstallerDiagnosticLogContainsContextAndExceptionChain", InstallerDiagnosticLogContainsContextAndExceptionChain);
|
||||
Run("InstallerDiagnosticLogFallsBackToTemp", InstallerDiagnosticLogFallsBackToTemp);
|
||||
Run("InstallerSurvivesUiReportFailure", InstallerSurvivesUiReportFailure);
|
||||
Console.WriteLine(failures == 0 ? "ALL TESTS PASSED" : failures + " TEST(S) FAILED");
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
@@ -153,10 +157,18 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
var install = Path.Combine(directory, "install"); Directory.CreateDirectory(install);
|
||||
File.WriteAllText(Path.Combine(install, InstallerEngine.ApplicationExeName), "old");
|
||||
File.WriteAllText(Path.Combine(install, InstallerEngine.ApplicationExeName + ".config"), "old-config");
|
||||
var engine = new InstallerEngine(package, install, Path.Combine(directory, "data"), false, path => false);
|
||||
Expect<InvalidOperationException>(() => engine.Install(false, null));
|
||||
var data = Path.Combine(directory, "data");
|
||||
var messages = new List<string>();
|
||||
var engine = new InstallerEngine(package, install, data, false, path => false);
|
||||
var exception = Capture<InvalidOperationException>(() => engine.Install(false, messages.Add));
|
||||
Assert(File.ReadAllText(Path.Combine(install, InstallerEngine.ApplicationExeName)) == "old", "staging failure modified the installed payload");
|
||||
Assert(!Directory.GetDirectories(directory, "install.backup.*").Any(), "backup was created before staging passed");
|
||||
Assert(exception.Message.Contains("Fehlercode=SETUP-STAGING-VALIDATION"), "staging error code missing");
|
||||
Assert(exception.Message.Contains("Kein Rollback erforderlich"), "pre-mutation failure reported a rollback");
|
||||
Assert(messages.Any(x => x.Contains("Kein Rollback erforderlich")), "operator output did not distinguish pre-mutation failure");
|
||||
var log = File.ReadAllText(Directory.GetFiles(Path.Combine(data, "InstallerLogs"), "setup-*.log").Single());
|
||||
Assert(log.Contains("event=self_test_completed label=staging"), "staging self-test result missing from log");
|
||||
Assert(log.Contains("event=rollback_summary result=not_required"), "no-rollback evidence missing from log");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,10 +182,12 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
File.WriteAllText(Path.Combine(install, InstallerEngine.ApplicationExeName + ".config"), "old-config");
|
||||
var calls = 0;
|
||||
var engine = new InstallerEngine(package, install, Path.Combine(directory, "data"), false, path => ++calls == 1);
|
||||
Expect<InvalidOperationException>(() => engine.Install(false, null));
|
||||
var exception = Capture<InvalidOperationException>(() => engine.Install(false, null));
|
||||
Assert(File.ReadAllText(Path.Combine(install, InstallerEngine.ApplicationExeName)) == "old", "previous payload was not restored");
|
||||
Assert(!Directory.GetDirectories(directory, "install.staging.*").Any(), "staging directory remained");
|
||||
Assert(!Directory.GetDirectories(directory, "install.backup.*").Any(), "backup directory remained");
|
||||
Assert(exception.Message.Contains("Fehlercode=SETUP-ACTIVATED-SELFTEST"), "activated self-test error code missing");
|
||||
Assert(exception.Message.Contains("Rollback erfolgreich"), "successful rollback not reported");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -191,6 +205,62 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
});
|
||||
}
|
||||
|
||||
private static void InstallerDiagnosticLogContainsContextAndExceptionChain()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var log = SetupOperationLog.Create(directory, "diagnostic-test");
|
||||
Assert(!string.IsNullOrEmpty(log.FilePath), "diagnostic log was not created");
|
||||
Exception failure;
|
||||
try { throw new InvalidOperationException("outer", new IOException("inner")); }
|
||||
catch (Exception ex) { failure = ex; }
|
||||
log.WriteException("TEST-CODE", "Test phase", failure);
|
||||
var content = File.ReadAllText(log.FilePath);
|
||||
Assert(content.Contains("event=setup_started"), "setup context header missing");
|
||||
Assert(content.Contains("process_bitness="), "process bitness missing");
|
||||
Assert(content.Contains("identity="), "identity missing");
|
||||
Assert(content.Contains("error_code=TEST-CODE"), "error code missing");
|
||||
Assert(content.Contains("inner[1]"), "inner exception missing");
|
||||
Assert(content.Contains("hresult=0x"), "HRESULT missing");
|
||||
});
|
||||
}
|
||||
|
||||
private static void InstallerSurvivesUiReportFailure()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var package = CreatePackage(directory, "new");
|
||||
var install = Path.Combine(directory, "install");
|
||||
var data = Path.Combine(directory, "data");
|
||||
var engine = new InstallerEngine(package, install, data, false, path => true);
|
||||
engine.Install(false, message => { throw new InvalidOperationException("UI unavailable"); });
|
||||
Assert(File.Exists(Path.Combine(install, InstallerEngine.ApplicationExeName)), "UI report failure interrupted installation");
|
||||
var log = File.ReadAllText(Directory.GetFiles(Path.Combine(data, "InstallerLogs"), "setup-*.log").Single());
|
||||
Assert(log.Contains("event=ui_report_failed"), "UI report failure was not diagnosed");
|
||||
});
|
||||
}
|
||||
|
||||
private static void InstallerDiagnosticLogFallsBackToTemp()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var blockedDataDirectory = Path.Combine(directory, "blocked-data-directory");
|
||||
File.WriteAllText(blockedDataDirectory, "not a directory");
|
||||
var log = SetupOperationLog.Create(blockedDataDirectory, "fallback-test");
|
||||
try
|
||||
{
|
||||
Assert(log.IsFallback, "diagnostic log did not use the Temp fallback");
|
||||
Assert(!string.IsNullOrEmpty(log.CreationError), "primary logging failure was not retained");
|
||||
Assert(File.Exists(log.FilePath), "fallback diagnostic log was not created");
|
||||
Assert(File.ReadAllText(log.FilePath).Contains("event=primary_log_creation_failed"), "fallback reason missing from log");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(log.FilePath)) File.Delete(log.FilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static BizTalkSnapshot Snapshot(string application, string port, int state)
|
||||
{
|
||||
var result = new BizTalkSnapshot { ToolVersion = "test", CreatedAt = DateTimeOffset.Now.ToString("o"), Server = Environment.MachineName };
|
||||
@@ -225,5 +295,12 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
catch (T) { return; }
|
||||
throw new InvalidOperationException("Expected exception " + typeof(T).Name);
|
||||
}
|
||||
|
||||
private static T Capture<T>(Action action) where T : Exception
|
||||
{
|
||||
try { action(); }
|
||||
catch (T ex) { return ex; }
|
||||
throw new InvalidOperationException("Expected exception " + typeof(T).Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user