// Copyright 2012-2025 The NATS Authors // Licensed under the Apache License, Version 2.0 using Shouldly; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.Internal; /// /// Tests for NatsLogger / ServerLogging — mirrors tests from server/log_test.go. /// public class NatsLoggerTests { private sealed class TestLogger : INatsLogger { public List Messages { get; } = []; public void Noticef(string format, params object[] args) => Messages.Add($"[INF] {string.Format(format, args)}"); public void Warnf(string format, params object[] args) => Messages.Add($"[WRN] {string.Format(format, args)}"); public void Fatalf(string format, params object[] args) => Messages.Add($"[FTL] {string.Format(format, args)}"); public void Errorf(string format, params object[] args) => Messages.Add($"[ERR] {string.Format(format, args)}"); public void Debugf(string format, params object[] args) => Messages.Add($"[DBG] {string.Format(format, args)}"); public void Tracef(string format, params object[] args) => Messages.Add($"[TRC] {string.Format(format, args)}"); } /// /// Mirrors TestSetLogger — verify logger assignment and atomic flags. /// [Fact] // T:2017 public void SetLogger_ShouldSetLoggerAndFlags() { var logging = new ServerLogging(); var testLog = new TestLogger(); logging.SetLoggerV2(testLog, true, true, false); logging.IsDebug.ShouldBeTrue(); logging.IsTrace.ShouldBeTrue(); logging.IsTraceSysAcc.ShouldBeFalse(); logging.GetLogger().ShouldBe(testLog); } /// /// Verify all log methods produce output when flags enabled. /// [Fact] // T:2017 (continuation) public void AllLogMethods_ShouldProduceOutput() { var logging = new ServerLogging(); var testLog = new TestLogger(); logging.SetLoggerV2(testLog, true, true, false); logging.Noticef("notice {0}", "test"); logging.Errorf("error {0}", "test"); logging.Warnf("warn {0}", "test"); logging.Fatalf("fatal {0}", "test"); logging.Debugf("debug {0}", "test"); logging.Tracef("trace {0}", "test"); testLog.Messages.Count.ShouldBe(6); testLog.Messages[0].ShouldContain("[INF]"); testLog.Messages[1].ShouldContain("[ERR]"); testLog.Messages[4].ShouldContain("[DBG]"); testLog.Messages[5].ShouldContain("[TRC]"); } /// /// Debug/Trace should not produce output when flags disabled. /// [Fact] // T:2017 (continuation) public void DebugTrace_ShouldBeNoOpWhenDisabled() { var logging = new ServerLogging(); var testLog = new TestLogger(); logging.SetLoggerV2(testLog, false, false, false); logging.Debugf("debug"); logging.Tracef("trace"); testLog.Messages.ShouldBeEmpty(); } /// /// Verify null logger does not throw. /// [Fact] public void NullLogger_ShouldNotThrow() { var logging = new ServerLogging(); Should.NotThrow(() => logging.Noticef("test")); Should.NotThrow(() => logging.Errorf("test")); Should.NotThrow(() => logging.Debugf("test")); } /// /// Verify rate-limited logging suppresses duplicate messages. /// [Fact] // T:2017 (RateLimitWarnf behavior) public void RateLimitWarnf_ShouldSuppressDuplicates() { var logging = new ServerLogging(); var testLog = new TestLogger(); logging.SetLoggerV2(testLog, false, false, false); logging.RateLimitWarnf("duplicate message"); logging.RateLimitWarnf("duplicate message"); logging.RateLimitWarnf("different message"); // Should only log 2 unique messages, not 3. testLog.Messages.Count.ShouldBe(2); } /// /// Verify Errors/Errorc/Errorsc convenience methods. /// [Fact] public void ErrorVariants_ShouldFormatCorrectly() { var logging = new ServerLogging(); var testLog = new TestLogger(); logging.SetLoggerV2(testLog, false, false, false); logging.Errors("client", new Exception("conn reset")); logging.Errorc("TLS", new Exception("cert expired")); logging.Errorsc("route", "cluster", new Exception("timeout")); testLog.Messages.Count.ShouldBe(3); testLog.Messages[0].ShouldContain("client - conn reset"); testLog.Messages[1].ShouldContain("TLS: cert expired"); testLog.Messages[2].ShouldContain("route - cluster: timeout"); } }