98 lines
3.6 KiB
C#
98 lines
3.6 KiB
C#
using System.Reflection;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.Host.Tests;
|
|
|
|
/// <summary>
|
|
/// WP-16: Tests for CoordinatedShutdown configuration.
|
|
/// Verifies no Environment.Exit calls exist in source and HOCON config is correct.
|
|
/// </summary>
|
|
public class CoordinatedShutdownTests
|
|
{
|
|
[Fact]
|
|
public void HostSource_DoesNotContainEnvironmentExit()
|
|
{
|
|
var hostProjectDir = FindHostProjectDirectory();
|
|
Assert.NotNull(hostProjectDir);
|
|
|
|
var sourceFiles = Directory.GetFiles(hostProjectDir, "*.cs", SearchOption.AllDirectories);
|
|
Assert.NotEmpty(sourceFiles);
|
|
|
|
foreach (var file in sourceFiles)
|
|
{
|
|
var content = File.ReadAllText(file);
|
|
Assert.DoesNotContain("Environment.Exit", content,
|
|
StringComparison.Ordinal);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void AkkaHostedService_HoconConfig_IncludesCoordinatedShutdownSettings()
|
|
{
|
|
// Read the AkkaHostedService source to verify HOCON configuration
|
|
var hostProjectDir = FindHostProjectDirectory();
|
|
Assert.NotNull(hostProjectDir);
|
|
|
|
var akkaServiceFile = Path.Combine(hostProjectDir, "Actors", "AkkaHostedService.cs");
|
|
Assert.True(File.Exists(akkaServiceFile), $"AkkaHostedService.cs not found at {akkaServiceFile}");
|
|
|
|
var content = File.ReadAllText(akkaServiceFile);
|
|
|
|
// Verify critical HOCON settings are present
|
|
Assert.Contains("run-by-clr-shutdown-hook = on", content);
|
|
Assert.Contains("run-coordinated-shutdown-when-down = on", content);
|
|
}
|
|
|
|
[Fact]
|
|
public void AllCentralSingletons_RegisterThroughRegistrarWithDrain()
|
|
{
|
|
var hostProjectDir = FindHostProjectDirectory();
|
|
Assert.NotNull(hostProjectDir);
|
|
var content = File.ReadAllText(Path.Combine(hostProjectDir!, "Actors", "AkkaHostedService.cs"));
|
|
|
|
// Review 01 [Medium]: notification-outbox and audit-log-ingest were the
|
|
// only central singletons WITHOUT a cluster-leave drain task. All seven
|
|
// now go through SingletonRegistrar.Start, which always adds the
|
|
// PhaseClusterLeave GracefulStop drain.
|
|
Assert.Contains("SingletonRegistrar.Start(", content);
|
|
foreach (var name in new[] { "notification-outbox", "audit-log-ingest", "site-call-audit",
|
|
"audit-log-purge", "site-audit-reconciliation",
|
|
"kpi-history-recorder", "pending-deployment-purge" })
|
|
{
|
|
Assert.Contains($"\"{name}\"", content);
|
|
}
|
|
|
|
// No hand-rolled ClusterSingletonManager registrations remain in the
|
|
// CENTRAL branch. The only two left are the SITE singletons
|
|
// (deployment-manager, event-log-handler), which stay hand-rolled because
|
|
// they are role-scoped (.WithRole(siteRole)).
|
|
Assert.Equal(2, CountOccurrences(content, "ClusterSingletonManager.Props("));
|
|
}
|
|
|
|
private static int CountOccurrences(string haystack, string needle)
|
|
{
|
|
int count = 0, i = 0;
|
|
while ((i = haystack.IndexOf(needle, i, StringComparison.Ordinal)) >= 0)
|
|
{
|
|
count++;
|
|
i += needle.Length;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
private static string? FindHostProjectDirectory()
|
|
{
|
|
var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
|
var dir = new DirectoryInfo(assemblyDir);
|
|
|
|
while (dir != null)
|
|
{
|
|
var hostPath = Path.Combine(dir.FullName, "src", "ZB.MOM.WW.ScadaBridge.Host");
|
|
if (Directory.Exists(hostPath))
|
|
return hostPath;
|
|
dir = dir.Parent;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|