using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.LeafNodes; namespace NATS.Server.LeafNodes.Tests.LeafNodes; /// /// Unit tests for leaf node TLS certificate hot-reload (Gap 12.1). /// Verifies that correctly tracks cert/key /// paths, increments the reload counter only on genuine changes, and returns accurate /// values. /// Go reference: leafnode.go — reloadTLSConfig, TestLeafNodeTLSCertReload. /// public class LeafTlsReloadTests { private static LeafNodeManager CreateManager() => new( options: new LeafNodeOptions { Host = "127.0.0.1", Port = 0 }, stats: new ServerStats(), serverId: "test-server", remoteSubSink: _ => { }, messageSink: _ => { }, logger: NullLogger.Instance); // Go: TestLeafNodeTLSCertReload — leafnode_test.go, first reload call [Fact] public void UpdateTlsConfig_NewCert_ReturnsChanged() { var manager = CreateManager(); var result = manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); result.Changed.ShouldBeTrue(); result.NewCertPath.ShouldBe("/certs/leaf.pem"); } // Go: TestLeafNodeTLSCertReload — no-op reload when cert unchanged [Fact] public void UpdateTlsConfig_SameCert_ReturnsUnchanged() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); var result = manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); result.Changed.ShouldBeFalse(); } // Go: TestLeafNodeTLSCertReload — cert rotation path [Fact] public void UpdateTlsConfig_ChangedCert_UpdatesPath() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/old.pem", "/certs/old-key.pem"); manager.UpdateTlsConfig("/certs/new.pem", "/certs/new-key.pem"); manager.CurrentCertPath.ShouldBe("/certs/new.pem"); } // Go: TestLeafNodeTLSCertReload — disabling TLS by passing null cert [Fact] public void UpdateTlsConfig_ClearCert_ReturnsChanged() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); var result = manager.UpdateTlsConfig(null, null); result.Changed.ShouldBeTrue(); result.NewCertPath.ShouldBeNull(); } // Go: TestLeafNodeTLS — IsTlsEnabled when no cert configured [Fact] public void IsTlsEnabled_NoCert_ReturnsFalse() { var manager = CreateManager(); manager.IsTlsEnabled.ShouldBeFalse(); } // Go: TestLeafNodeTLS — IsTlsEnabled when cert is configured [Fact] public void IsTlsEnabled_WithCert_ReturnsTrue() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); manager.IsTlsEnabled.ShouldBeTrue(); } // Go: TestLeafNodeTLSCertReload — reload counter increments on each genuine change [Fact] public void TlsReloadCount_IncrementedOnChange() { var manager = CreateManager(); manager.TlsReloadCount.ShouldBe(0); manager.UpdateTlsConfig("/certs/a.pem", "/certs/a-key.pem"); manager.TlsReloadCount.ShouldBe(1); manager.UpdateTlsConfig("/certs/b.pem", "/certs/b-key.pem"); manager.TlsReloadCount.ShouldBe(2); } // Go: TestLeafNodeTLSCertReload — reload counter unchanged when config identical [Fact] public void TlsReloadCount_NotIncrementedOnNoChange() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); manager.TlsReloadCount.ShouldBe(1); } // Go: TestLeafNodeTLSCertReload — result carries previous cert path [Fact] public void UpdateTlsConfig_ReportsPreviousPath() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/first.pem", "/certs/first-key.pem"); var result = manager.UpdateTlsConfig("/certs/second.pem", "/certs/second-key.pem"); result.PreviousCertPath.ShouldBe("/certs/first.pem"); result.NewCertPath.ShouldBe("/certs/second.pem"); } // Go: TestLeafNodeTLSCertReload — key path tracked alongside cert path [Fact] public void UpdateTlsConfig_UpdatesKeyPath() { var manager = CreateManager(); manager.UpdateTlsConfig("/certs/leaf.pem", "/certs/leaf-key.pem"); manager.CurrentKeyPath.ShouldBe("/certs/leaf-key.pem"); } }