Add BizTalk endpoint reachability monitoring
This commit is contained in:
@@ -28,6 +28,10 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
Run("UnknownReceiveLocationStateIsNotGreen", UnknownReceiveLocationStateIsNotGreen);
|
||||
Run("SendPortAllowlistSeparatesExpectedState", SendPortAllowlistSeparatesExpectedState);
|
||||
Run("ArtifactSummaryIsBounded", ArtifactSummaryIsBounded);
|
||||
Run("EndpointAddressesResolveWithoutSecrets", EndpointAddressesResolveWithoutSecrets);
|
||||
Run("EndpointCatalogPreservesManualOverrides", EndpointCatalogPreservesManualOverrides);
|
||||
Run("EndpointCatalogRoundTrip", EndpointCatalogRoundTrip);
|
||||
Run("EndpointOutputListsOnlyUnavailableTargets", EndpointOutputListsOnlyUnavailableTargets);
|
||||
Run("SnapshotRoundTripPreservesLines", SnapshotRoundTripPreservesLines);
|
||||
Run("SnapshotRejectsTampering", SnapshotRejectsTampering);
|
||||
Run("SnapshotRejectsStaleData", SnapshotRejectsStaleData);
|
||||
@@ -52,19 +56,20 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
private static void SelfTestEmitsAllStableServices()
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
AssertEqual(8, lines.Length, "self-test line count");
|
||||
AssertEqual(8, lines.Distinct(StringComparer.Ordinal).Count(), "unique self-test lines");
|
||||
AssertEqual(9, lines.Length, "self-test line count");
|
||||
AssertEqual(9, lines.Distinct(StringComparer.Ordinal).Count(), "unique self-test lines");
|
||||
Assert(lines.All(x => x.StartsWith("0 \"BizTalk ", StringComparison.Ordinal)), "every self-test line must be OK");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Event Log\"")), "Event Log service missing");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Receive Locations\"")), "Receive Locations service missing");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Send Ports\"")), "Send Ports service missing");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Endpoint Reachability\"")), "Endpoint Reachability service missing");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Orchestrations\"")), "Orchestrations service missing");
|
||||
}
|
||||
|
||||
private static void UnavailableSourcesAreUnknown()
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).Format(new ProbeResult()).Take(8).ToArray();
|
||||
AssertEqual(8, lines.Length, "stable service count");
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).Format(new ProbeResult()).Take(9).ToArray();
|
||||
AssertEqual(9, lines.Length, "stable service count");
|
||||
Assert(lines.All(x => x.StartsWith("3 \"BizTalk ", StringComparison.Ordinal)), "unavailable sources must be UNKNOWN");
|
||||
}
|
||||
|
||||
@@ -73,7 +78,7 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
var options = new MonitoringOptions { EmitPerApplicationSuspensionServices = true };
|
||||
var result = CreateSuspensionResult("(unknown)");
|
||||
var lines = new CheckmkLocalFormatter(options).Format(result).ToArray();
|
||||
AssertEqual(8, lines.Length, "unknown application must not create a dynamic service");
|
||||
AssertEqual(9, lines.Length, "unknown application must not create a dynamic service");
|
||||
}
|
||||
|
||||
private static void KnownApplicationCreatesService()
|
||||
@@ -81,7 +86,7 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
var options = new MonitoringOptions { EmitPerApplicationSuspensionServices = true };
|
||||
var result = CreateSuspensionResult("Orders");
|
||||
var lines = new CheckmkLocalFormatter(options).Format(result).ToArray();
|
||||
AssertEqual(9, lines.Length, "known application should create one dynamic service");
|
||||
AssertEqual(10, lines.Length, "known application should create one dynamic service");
|
||||
Assert(lines.Any(x => x.Contains("\"BizTalk Suspended Orders\"")), "known application service missing");
|
||||
}
|
||||
|
||||
@@ -328,6 +333,166 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
});
|
||||
}
|
||||
|
||||
private static void EndpointAddressesResolveWithoutSecrets()
|
||||
{
|
||||
EndpointCatalogEntry endpoint;
|
||||
string reason;
|
||||
var candidate = new EndpointCandidate
|
||||
{
|
||||
ArtifactType = "SendPort",
|
||||
ApplicationName = "Orders",
|
||||
ArtifactName = "SP Orders",
|
||||
TransportRole = "Primary",
|
||||
AdapterName = "WCF-BasicHttp",
|
||||
Address = "https://api-user:top-secret@example.test/orders?q=secret",
|
||||
Active = true
|
||||
};
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "HTTPS endpoint should resolve: " + reason);
|
||||
AssertEqual("example.test", endpoint.Host, "HTTPS host");
|
||||
AssertEqual(443, endpoint.Port, "HTTPS port");
|
||||
Assert(endpoint.Key.IndexOf("secret", StringComparison.OrdinalIgnoreCase) < 0, "catalog key must not contain URI secrets");
|
||||
Assert(endpoint.Host.IndexOf("secret", StringComparison.OrdinalIgnoreCase) < 0, "catalog host must not contain URI secrets");
|
||||
|
||||
candidate.Address = @"\\fileserver\drop\%MessageID%.xml";
|
||||
candidate.AdapterName = "FILE";
|
||||
Assert(EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "UNC endpoint should resolve: " + reason);
|
||||
AssertEqual("fileserver", endpoint.Host, "UNC host");
|
||||
AssertEqual(445, endpoint.Port, "UNC SMB port");
|
||||
|
||||
candidate.Address = "recipient@example.test";
|
||||
candidate.AdapterName = "SMTP";
|
||||
Assert(!EndpointAddressParser.TryCreate(candidate, out endpoint, out reason), "SMTP recipient must not become a host probe");
|
||||
Assert(!EndpointAddressParser.IsPotentialExternalEndpoint(candidate), "SMTP recipient must not cause UNKNOWN");
|
||||
|
||||
candidate.Address = "/Orders/Receive.svc";
|
||||
candidate.AdapterName = "WCF-CustomIsolated";
|
||||
Assert(!EndpointAddressParser.IsPotentialExternalEndpoint(candidate), "relative local receive address must not cause UNKNOWN");
|
||||
}
|
||||
|
||||
private static void EndpointCatalogPreservesManualOverrides()
|
||||
{
|
||||
var candidate = new EndpointCandidate
|
||||
{
|
||||
ArtifactType = "SendPort",
|
||||
ApplicationName = "Orders",
|
||||
ArtifactName = "SP Orders",
|
||||
TransportRole = "Primary",
|
||||
AdapterName = "SFTP",
|
||||
Address = "sftp://discovered.example.test/out",
|
||||
Active = true
|
||||
};
|
||||
var inactive = new EndpointCandidate
|
||||
{
|
||||
ArtifactType = "ReceiveLocation",
|
||||
ApplicationName = "Orders",
|
||||
ArtifactName = "RL Disabled",
|
||||
TransportRole = "Inbound",
|
||||
AdapterName = "HTTPS",
|
||||
Address = "https://disabled.example.test/in",
|
||||
Active = false
|
||||
};
|
||||
var existing = new EndpointCatalog { EnvironmentName = "ACC" };
|
||||
existing.Entries.Add(new EndpointCatalogEntry
|
||||
{
|
||||
Key = candidate.Key,
|
||||
ArtifactType = candidate.ArtifactType,
|
||||
ApplicationName = candidate.ApplicationName,
|
||||
ArtifactName = candidate.ArtifactName,
|
||||
TransportRole = candidate.TransportRole,
|
||||
AdapterName = candidate.AdapterName,
|
||||
Protocol = "TCP",
|
||||
Host = "manual.example.test",
|
||||
Port = 2222,
|
||||
Enabled = true,
|
||||
AutoDiscovered = false
|
||||
});
|
||||
|
||||
var synchronized = EndpointConnectivityProbe.SynchronizeCatalog(existing, new[] { candidate, inactive }, DateTime.UtcNow);
|
||||
AssertEqual(1, synchronized.Entries.Count, "manual override count");
|
||||
AssertEqual("manual.example.test", synchronized.Entries[0].Host, "manual override host");
|
||||
AssertEqual(2222, synchronized.Entries[0].Port, "manual override port");
|
||||
Assert(!synchronized.Entries[0].AutoDiscovered, "manual override marker");
|
||||
Assert(synchronized.Entries.All(x => x.ArtifactName != "RL Disabled"), "inactive receive location must not be added");
|
||||
}
|
||||
|
||||
private static void EndpointOutputListsOnlyUnavailableTargets()
|
||||
{
|
||||
var result = new ProbeResult();
|
||||
result.EndpointConnectivity.RuntimeStateAvailable = true;
|
||||
result.EndpointConnectivity.CatalogAvailable = true;
|
||||
result.EndpointConnectivity.RefreshSucceeded = true;
|
||||
result.EndpointConnectivity.Configured = 2;
|
||||
result.EndpointConnectivity.Active = 2;
|
||||
result.EndpointConnectivity.Results.Add(new EndpointProbeResult
|
||||
{
|
||||
Endpoint = TestEndpoint("Reachable", "up.example.test"),
|
||||
Available = true
|
||||
});
|
||||
result.EndpointConnectivity.Results.Add(new EndpointProbeResult
|
||||
{
|
||||
Endpoint = TestEndpoint("Unavailable", "down.example.test"),
|
||||
Available = false,
|
||||
Failure = "TCP timeout"
|
||||
});
|
||||
|
||||
var line = FindServiceLine(result, "Endpoint Reachability");
|
||||
Assert(line.StartsWith("2 \"BizTalk Endpoint Reachability\"", StringComparison.Ordinal), "failed endpoint must be CRIT");
|
||||
Assert(line.Contains("Unavailable"), "unavailable endpoint name missing");
|
||||
Assert(line.Contains("down.example.test:443/TCP"), "unavailable host missing");
|
||||
Assert(line.IndexOf("Reachable", StringComparison.Ordinal) < 0, "reachable endpoint must not clutter output");
|
||||
Assert(line.IndexOf("up.example.test", StringComparison.Ordinal) < 0, "reachable host must not clutter output");
|
||||
}
|
||||
|
||||
private static void EndpointCatalogRoundTrip()
|
||||
{
|
||||
var directory = Path.Combine(Path.GetTempPath(), "BizTalkCheckmkPulse.CatalogTests." + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(directory);
|
||||
var path = Path.Combine(directory, "endpoints.xml");
|
||||
try
|
||||
{
|
||||
var catalog = new EndpointCatalog
|
||||
{
|
||||
EnvironmentName = "ACC",
|
||||
SynchronizedUtc = DateTime.UtcNow,
|
||||
ActiveCandidates = 1,
|
||||
UnsupportedCandidates = 0
|
||||
};
|
||||
catalog.Entries.Add(TestEndpoint("Orders", "api.example.test"));
|
||||
var store = new EndpointCatalogStore(path, 1048576, 500);
|
||||
store.Write(catalog);
|
||||
var loaded = store.Read("ACC");
|
||||
|
||||
AssertEqual(1, loaded.Entries.Count, "catalog entry count");
|
||||
AssertEqual("api.example.test", loaded.Entries[0].Host, "catalog host");
|
||||
AssertEqual(443, loaded.Entries[0].Port, "catalog port");
|
||||
Assert(File.ReadAllText(path).IndexOf("top-secret", StringComparison.OrdinalIgnoreCase) < 0, "catalog must not contain URI secrets");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(directory))
|
||||
{
|
||||
Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static EndpointCatalogEntry TestEndpoint(string name, string host)
|
||||
{
|
||||
return new EndpointCatalogEntry
|
||||
{
|
||||
Key = name,
|
||||
ArtifactType = "SendPort",
|
||||
ApplicationName = "Orders",
|
||||
ArtifactName = name,
|
||||
TransportRole = "Primary",
|
||||
Protocol = "TCP",
|
||||
Host = host,
|
||||
Port = 443,
|
||||
Enabled = true,
|
||||
AutoDiscovered = true
|
||||
};
|
||||
}
|
||||
|
||||
private static void SnapshotRejectsTampering()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
@@ -392,7 +557,7 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions())
|
||||
.FormatSnapshotFailure("Snapshot file does not exist.")
|
||||
.ToArray();
|
||||
AssertEqual(8, lines.Length, "snapshot failure stable service count");
|
||||
AssertEqual(9, lines.Length, "snapshot failure stable service count");
|
||||
Assert(lines.All(x => x.StartsWith("3 \"BizTalk ", StringComparison.Ordinal)), "snapshot failure must be UNKNOWN");
|
||||
Assert(lines.All(x => x.Contains("Scheduled Task")), "snapshot failure must contain provider action");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user