// Copyright 2012-2025 The NATS Authors // Licensed under the Apache License, Version 2.0 using System.Runtime.InteropServices; using Shouldly; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.Internal; /// /// Tests for SignalHandler — mirrors tests from server/signal_test.go. /// public class SignalHandlerTests { /// /// Mirrors CommandToSignal mapping tests. /// [Fact] // T:3158 public void CommandToUnixSignal_ShouldMapCorrectly() { SignalHandler.CommandToUnixSignal(ServerCommand.Stop).ShouldBe(UnixSignal.SigKill); SignalHandler.CommandToUnixSignal(ServerCommand.Quit).ShouldBe(UnixSignal.SigInt); SignalHandler.CommandToUnixSignal(ServerCommand.Reopen).ShouldBe(UnixSignal.SigUsr1); SignalHandler.CommandToUnixSignal(ServerCommand.Reload).ShouldBe(UnixSignal.SigHup); } /// /// Mirrors SetProcessName test. /// [Fact] // T:3155 public void SetProcessName_ShouldNotThrow() { Should.NotThrow(() => SignalHandler.SetProcessName("test-server")); } /// /// Verify IsWindowsService returns false on non-Windows. /// [Fact] // T:3149 public void IsWindowsService_ShouldReturnFalse() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; // Skip on Windows SignalHandler.IsWindowsService().ShouldBeFalse(); } /// /// Mirrors Run — service.go Run() simply invokes the start function. /// [Fact] // T:3148 public void Run_ShouldInvokeStartAction() { var called = false; SignalHandler.Run(() => called = true); called.ShouldBeTrue(); } /// /// ProcessSignal with invalid PID expression should return error. /// [Fact] // T:3157 public void ProcessSignal_InvalidPid_ShouldReturnError() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; // Skip on Windows var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "not-a-pid"); err.ShouldNotBeNull(); } // --------------------------------------------------------------------------- // Tests ported from server/signal_test.go // --------------------------------------------------------------------------- /// /// Mirrors TestProcessSignalInvalidCommand. /// An out-of-range ServerCommand enum value is treated as an unknown signal /// and ProcessSignal returns a non-null error. /// [Fact] // T:2919 public void ProcessSignalInvalidCommand_ShouldSucceed() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; // Skip on Windows var err = SignalHandler.ProcessSignal((ServerCommand)99, "123"); err.ShouldNotBeNull(); } /// /// Mirrors TestProcessSignalInvalidPid. /// A non-numeric PID string returns an error containing "invalid pid". /// [Fact] // T:2920 public void ProcessSignalInvalidPid_ShouldSucceed() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; // Skip on Windows var err = SignalHandler.ProcessSignal(ServerCommand.Stop, "abc"); err.ShouldNotBeNull(); err!.Message.ShouldContain("invalid pid"); } // --------------------------------------------------------------------------- // Deferred signal tests — require pgrep/kill injection or real OS process spawning. // These cannot be unit-tested without refactoring SignalHandler to accept // injectable pgrep/kill delegates (as the Go source does). // --------------------------------------------------------------------------- /// Mirrors TestProcessSignalMultipleProcesses — deferred: requires pgrep injection. [Fact(Skip = "deferred: requires pgrep/kill injection")] // T:2913 public void ProcessSignalMultipleProcesses_ShouldSucceed() { } /// Mirrors TestProcessSignalMultipleProcessesGlob — deferred: requires pgrep injection. [Fact(Skip = "deferred: requires pgrep/kill injection")] // T:2914 public void ProcessSignalMultipleProcessesGlob_ShouldSucceed() { } /// Mirrors TestProcessSignalMultipleProcessesGlobPartial — deferred: requires pgrep injection. [Fact(Skip = "deferred: requires pgrep/kill injection")] // T:2915 public void ProcessSignalMultipleProcessesGlobPartial_ShouldSucceed() { } /// Mirrors TestProcessSignalPgrepError — deferred: requires pgrep injection. [Fact(Skip = "deferred: requires pgrep injection")] // T:2916 public void ProcessSignalPgrepError_ShouldSucceed() { } /// Mirrors TestProcessSignalPgrepMangled — deferred: requires pgrep injection. [Fact(Skip = "deferred: requires pgrep injection")] // T:2917 public void ProcessSignalPgrepMangled_ShouldSucceed() { } /// Mirrors TestProcessSignalResolveSingleProcess — deferred: requires pgrep and kill injection. [Fact(Skip = "deferred: requires pgrep/kill injection")] // T:2918 public void ProcessSignalResolveSingleProcess_ShouldSucceed() { } /// Mirrors TestProcessSignalQuitProcess — deferred: requires kill injection. [Fact(Skip = "deferred: requires kill injection")] // T:2921 public void ProcessSignalQuitProcess_ShouldSucceed() { } /// Mirrors TestProcessSignalTermProcess — deferred: requires kill injection and commandTerm equivalent. [Fact(Skip = "deferred: requires kill injection")] // T:2922 public void ProcessSignalTermProcess_ShouldSucceed() { } /// Mirrors TestProcessSignalReopenProcess — deferred: requires kill injection. [Fact(Skip = "deferred: requires kill injection")] // T:2923 public void ProcessSignalReopenProcess_ShouldSucceed() { } /// Mirrors TestProcessSignalReloadProcess — deferred: requires kill injection. [Fact(Skip = "deferred: requires kill injection")] // T:2924 public void ProcessSignalReloadProcess_ShouldSucceed() { } /// Mirrors TestProcessSignalLameDuckMode — deferred: requires kill injection and commandLDMode equivalent. [Fact(Skip = "deferred: requires kill injection")] // T:2925 public void ProcessSignalLameDuckMode_ShouldSucceed() { } /// Mirrors TestProcessSignalTermDuringLameDuckMode — deferred: requires full server (RunServer) and real OS signal. [Fact(Skip = "deferred: requires RunServer and real OS SIGTERM")] // T:2926 public void ProcessSignalTermDuringLameDuckMode_ShouldSucceed() { } /// Mirrors TestSignalInterruptHasSuccessfulExit — deferred: requires spawning a subprocess to test exit code on SIGINT. [Fact(Skip = "deferred: requires subprocess process spawning")] // T:2927 public void SignalInterruptHasSuccessfulExit_ShouldSucceed() { } /// Mirrors TestSignalTermHasSuccessfulExit — deferred: requires spawning a subprocess to test exit code on SIGTERM. [Fact(Skip = "deferred: requires subprocess process spawning")] // T:2928 public void SignalTermHasSuccessfulExit_ShouldSucceed() { } }