Support ScheduledTask control and persistent logs
This commit is contained in:
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using BizTalkPlatformManagementTool.Models;
|
||||
using BizTalkPlatformManagementTool.Services;
|
||||
@@ -35,6 +36,10 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Run("LoggerSinkFailureCannotAbortPlanExecution", LoggerSinkFailureCannotAbortPlanExecution);
|
||||
Run("PlanExecutionSkipsAlreadySatisfiedState", PlanExecutionSkipsAlreadySatisfiedState);
|
||||
Run("ExecutionReportRoundTripPreservesFailure", ExecutionReportRoundTripPreservesFailure);
|
||||
Run("ScheduledReceivePlanCarriesAdapterMetadata", ScheduledReceivePlanCarriesAdapterMetadata);
|
||||
Run("AdapterAssemblyResolverRequiresMatchingIdentity", AdapterAssemblyResolverRequiresMatchingIdentity);
|
||||
Run("OperationLogPersistsCompressesAndRetainsThirtyDays", OperationLogPersistsCompressesAndRetainsThirtyDays);
|
||||
Run("ExceptionDiagnosticsContainSupportContext", ExceptionDiagnosticsContainSupportContext);
|
||||
Run("CsvNeutralizesFormulaValues", CsvNeutralizesFormulaValues);
|
||||
Run("PackageManifestRejectsTampering", PackageManifestRejectsTampering);
|
||||
Run("PackageManifestRejectsUndeclaredAndTraversalFiles", PackageManifestRejectsUndeclaredAndTraversalFiles);
|
||||
@@ -169,6 +174,96 @@ namespace BizTalkPlatformManagementTool.Tests
|
||||
Assert(lastSendPort < firstHost, "host instances were not globally last");
|
||||
}
|
||||
|
||||
/// <summary>Prüft, dass Scheduler-Erkennung auch nach Planpersistenz möglich bleibt.</summary>
|
||||
private static void ScheduledReceivePlanCarriesAdapterMetadata()
|
||||
{
|
||||
var snapshot = Snapshot("APP", "SP", ArtifactStates.SendPortStarted);
|
||||
snapshot.Applications[0].ReceiveLocations.Add(new ReceiveLocationState
|
||||
{
|
||||
Application = "APP",
|
||||
Name = "RV_PMP_Trigger_Schedule",
|
||||
Enabled = true,
|
||||
AdapterName = "Schedule",
|
||||
Address = "scheduler://PMP-trigger"
|
||||
});
|
||||
var service = new BizTalkOperationService(null);
|
||||
var shutdown = service.CreateShutdownPlan(snapshot, snapshot.Server);
|
||||
var step = shutdown.Steps.Single(x => x.Kind == "ReceiveLocation");
|
||||
Assert(step.AdapterName == "Schedule", "adapter name missing from shutdown plan");
|
||||
Assert(step.Address == "scheduler://PMP-trigger", "scheduler URI missing from shutdown plan");
|
||||
|
||||
InTemp(directory =>
|
||||
{
|
||||
var path = Path.Combine(directory, "plan.json");
|
||||
JsonFileStore.Save(path, shutdown);
|
||||
var loaded = JsonFileStore.Load<OperationPlan>(path);
|
||||
Assert(loaded.Steps.Single(x => x.Kind == "ReceiveLocation").Address.StartsWith("scheduler:", StringComparison.OrdinalIgnoreCase), "scheduler metadata did not survive serialization");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Prüft Dateinamen- und vollständige Assemblyidentität vor prozesslokalem Laden.</summary>
|
||||
private static void AdapterAssemblyResolverRequiresMatchingIdentity()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var source = typeof(BizTalkOperationService).Assembly.Location;
|
||||
var identity = AssemblyName.GetAssemblyName(source);
|
||||
var candidate = Path.Combine(directory, identity.Name + ".dll");
|
||||
File.Copy(source, candidate);
|
||||
Assert(AdapterAssemblyResolver.FindCandidateFile(identity.Name, identity, new[] { directory }) == candidate, "matching assembly identity was not found");
|
||||
|
||||
var wrongVersion = new AssemblyName(identity.FullName);
|
||||
wrongVersion.Version = new Version(identity.Version.Major + 1, 0, 0, 0);
|
||||
Assert(AdapterAssemblyResolver.FindCandidateFile(identity.Name, wrongVersion, new[] { directory }) == null, "wrong assembly version was accepted");
|
||||
|
||||
File.Copy(source, Path.Combine(directory, AdapterAssemblyResolver.SchedulerAssemblyName + ".dll"));
|
||||
Assert(AdapterAssemblyResolver.FindCandidateFile(AdapterAssemblyResolver.SchedulerAssemblyName, null, new[] { directory }) == null, "file-name-only scheduler impostor was accepted");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Prüft Neustart-Rehydration, Vortagskompression und die 30-Tage-Grenze.</summary>
|
||||
private static void OperationLogPersistsCompressesAndRetainsThirtyDays()
|
||||
{
|
||||
InTemp(directory =>
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
var yesterday = Path.Combine(directory, "BizTalkPlatformManagementTool-" + today.AddDays(-1).ToString("yyyy-MM-dd") + ".log");
|
||||
var expired = Path.Combine(directory, "BizTalkPlatformManagementTool-" + today.AddDays(-30).ToString("yyyy-MM-dd") + ".log");
|
||||
File.WriteAllText(yesterday, "[" + today.AddDays(-1).ToString("yyyy-MM-dd") + " 08:00:00][WARNING] retained warning\r\n", Encoding.UTF8);
|
||||
File.WriteAllText(expired, "[" + today.AddDays(-30).ToString("yyyy-MM-dd") + " 08:00:00][INFO] expired\r\n", Encoding.UTF8);
|
||||
|
||||
var logger = new OperationLogger(null, directory);
|
||||
Assert(File.Exists(yesterday + ".gz") && !File.Exists(yesterday), "completed daily log was not compressed");
|
||||
Assert(!File.Exists(expired), "log outside 30-day retention was not deleted");
|
||||
logger.Error("durable failure marker");
|
||||
|
||||
var restarted = new OperationLogger(null, directory);
|
||||
var entries = restarted.ReadRecentEntries(100);
|
||||
Assert(entries.Any(x => x.Level == LogLevel.Warning && x.Message == "retained warning"), "compressed history was not restored");
|
||||
Assert(entries.Any(x => x.Level == LogLevel.Error && x.Message == "durable failure marker"), "current history was not restored after restart");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Prüft Typ, HRESULT, innere Ausnahme und Stacktrace für Supportfälle.</summary>
|
||||
private static void ExceptionDiagnosticsContainSupportContext()
|
||||
{
|
||||
Exception failure;
|
||||
try
|
||||
{
|
||||
throw new InvalidOperationException("outer detail", new FileNotFoundException("scheduler dependency missing"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failure = ex;
|
||||
}
|
||||
var diagnostic = ExceptionDiagnostics.Format(failure);
|
||||
Assert(diagnostic.Contains("Type=System.InvalidOperationException"), "exception type missing");
|
||||
Assert(diagnostic.Contains("HResult=0x"), "HRESULT missing");
|
||||
Assert(diagnostic.Contains("InnerException[1]"), "inner exception index missing");
|
||||
Assert(diagnostic.Contains("scheduler dependency missing"), "inner message missing");
|
||||
Assert(diagnostic.Contains("StackTrace="), "stack trace missing");
|
||||
}
|
||||
|
||||
/// <summary>Prüft den vollständigen Shutdown-Fortgang von Receive Location bis Host Instance nach einem frühen Fehler.</summary>
|
||||
private static void ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user