// 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() { }
}