// Port of Go server/reload.go — JetStream config change detection tests. // Reference: golang/nats-server/server/reload.go — jetStreamOption.Apply. using NATS.Server.Configuration; using Shouldly; namespace NATS.Server.Core.Tests.Configuration; public sealed class JetStreamConfigReloadTests { // ─── helpers ──────────────────────────────────────────────────── private static NatsOptions BaseOpts() => new(); private static NatsOptions OptsWithJs(long maxMemory = 0, long maxStore = 0, string? domain = null) => new() { JetStream = new JetStreamOptions { MaxMemoryStore = maxMemory, MaxFileStore = maxStore, Domain = domain } }; // ─── tests ────────────────────────────────────────────────────── [Fact] public void No_changes_returns_no_changes() { // Identical JetStream config on both sides → no changes detected. var oldOpts = OptsWithJs(maxMemory: 512, maxStore: 1024, domain: "hub"); var newOpts = OptsWithJs(maxMemory: 512, maxStore: 1024, domain: "hub"); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.HasChanges.ShouldBeFalse(); result.MaxMemoryChanged.ShouldBeFalse(); result.MaxStoreChanged.ShouldBeFalse(); result.DomainChanged.ShouldBeFalse(); } [Fact] public void MaxMemory_changed_detected() { // Changing MaxMemoryStore must set MaxMemoryChanged and HasChanges. var oldOpts = OptsWithJs(maxMemory: 1024 * 1024); var newOpts = OptsWithJs(maxMemory: 2 * 1024 * 1024); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.MaxMemoryChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); result.MaxStoreChanged.ShouldBeFalse(); result.DomainChanged.ShouldBeFalse(); } [Fact] public void MaxStore_changed_detected() { // Changing MaxFileStore must set MaxStoreChanged and HasChanges. var oldOpts = OptsWithJs(maxStore: 10 * 1024 * 1024); var newOpts = OptsWithJs(maxStore: 20 * 1024 * 1024); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.MaxStoreChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); result.MaxMemoryChanged.ShouldBeFalse(); result.DomainChanged.ShouldBeFalse(); } [Fact] public void Domain_changed_detected() { // Changing the domain name must set DomainChanged and HasChanges. var oldOpts = OptsWithJs(domain: "hub"); var newOpts = OptsWithJs(domain: "edge"); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.DomainChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); result.MaxMemoryChanged.ShouldBeFalse(); result.MaxStoreChanged.ShouldBeFalse(); } [Fact] public void OldMaxMemory_and_NewMaxMemory_set() { // When MaxMemoryStore changes the old and new values must be captured. var oldOpts = OptsWithJs(maxMemory: 100_000); var newOpts = OptsWithJs(maxMemory: 200_000); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.OldMaxMemory.ShouldBe(100_000L); result.NewMaxMemory.ShouldBe(200_000L); } [Fact] public void OldMaxStore_and_NewMaxStore_set() { // When MaxFileStore changes the old and new values must be captured. var oldOpts = OptsWithJs(maxStore: 500_000); var newOpts = OptsWithJs(maxStore: 1_000_000); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.OldMaxStore.ShouldBe(500_000L); result.NewMaxStore.ShouldBe(1_000_000L); } [Fact] public void OldDomain_and_NewDomain_set() { // When Domain changes both old and new values must be captured. var oldOpts = OptsWithJs(domain: "alpha"); var newOpts = OptsWithJs(domain: "beta"); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.OldDomain.ShouldBe("alpha"); result.NewDomain.ShouldBe("beta"); } [Fact] public void Multiple_changes_detected() { // Changing MaxMemory, MaxStore, and Domain together must flag all three. var oldOpts = OptsWithJs(maxMemory: 1024, maxStore: 2048, domain: "primary"); var newOpts = OptsWithJs(maxMemory: 4096, maxStore: 8192, domain: "secondary"); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.MaxMemoryChanged.ShouldBeTrue(); result.MaxStoreChanged.ShouldBeTrue(); result.DomainChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); } [Fact] public void Zero_to_nonzero_memory_detected() { // Going from the default (0 = unlimited) to a concrete limit must be detected. var oldOpts = BaseOpts(); // JetStream is null → effective MaxMemoryStore = 0 var newOpts = OptsWithJs(maxMemory: 512 * 1024 * 1024); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.MaxMemoryChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); result.OldMaxMemory.ShouldBe(0L); result.NewMaxMemory.ShouldBe(512 * 1024 * 1024L); } [Fact] public void Domain_null_to_value_detected() { // Going from no JetStream config (domain null/empty) to a named domain must be detected. var oldOpts = BaseOpts(); // JetStream is null → effective domain = "" var newOpts = OptsWithJs(domain: "cloud"); var result = ConfigReloader.ApplyJetStreamConfigChanges(oldOpts, newOpts); result.DomainChanged.ShouldBeTrue(); result.HasChanges.ShouldBeTrue(); result.OldDomain.ShouldBe(string.Empty); result.NewDomain.ShouldBe("cloud"); } }