Files
natsdotnet/tests/NATS.Server.Tests/Configuration/JetStreamConfigReloadTests.cs
Joseph Doherty ff07137ea6 feat: add JetStream config change reload (Gap 14.6)
Add ApplyJetStreamConfigChanges to ConfigReloader for detecting MaxMemoryStore,
MaxFileStore, and Domain changes during hot reload. Add Domain property to
JetStreamOptions. Port 10 unit tests covering all change permutations.
2026-02-25 11:48:22 -05:00

172 lines
6.1 KiB
C#

// 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.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");
}
}