Files
natsdotnet/tests/NATS.Server.JetStream.Tests/JetStream/Cluster/JetStreamAssignmentProcessingTests.cs
Joseph Doherty 78b4bc2486 refactor: extract NATS.Server.JetStream.Tests project
Move 225 JetStream-related test files from NATS.Server.Tests into a
dedicated NATS.Server.JetStream.Tests project. This includes root-level
JetStream*.cs files, storage test files (FileStore, MemStore,
StreamStoreContract), and the full JetStream/ subfolder tree (Api,
Cluster, Consumers, MirrorSource, Snapshots, Storage, Streams).

Updated all namespaces, added InternalsVisibleTo, registered in the
solution file, and added the JETSTREAM_INTEGRATION_MATRIX define.
2026-03-12 15:58:10 -04:00

170 lines
5.6 KiB
C#

using NATS.Server.JetStream.Cluster;
namespace NATS.Server.JetStream.Tests.JetStream.Cluster;
/// <summary>
/// Tests for validated stream/consumer assignment processing.
/// Go reference: jetstream_cluster.go:4541-5925.
/// </summary>
public class JetStreamAssignmentProcessingTests
{
[Fact]
public void ProcessStreamAssignment_validates_config()
{
var meta = new JetStreamMetaGroup(3);
var sa = new StreamAssignment
{
StreamName = "valid-stream",
Group = new RaftGroup { Name = "rg-1", Peers = ["n1", "n2", "n3"] },
ConfigJson = """{"subjects":["test.>"]}""",
};
meta.ProcessStreamAssignment(sa).ShouldBeTrue();
meta.StreamCount.ShouldBe(1);
}
[Fact]
public void ProcessStreamAssignment_rejects_empty_name()
{
var meta = new JetStreamMetaGroup(3);
var sa = CreateStreamAssignment("", "rg-1");
meta.ProcessStreamAssignment(sa).ShouldBeFalse();
meta.StreamCount.ShouldBe(0);
}
[Fact]
public void ProcessUpdateStreamAssignment_applies_config_change()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("updatable", "rg-u", """{"subjects":["old.>"]}"""));
var updated = CreateStreamAssignment("updatable", "rg-u", """{"subjects":["new.>"]}""");
meta.ProcessUpdateStreamAssignment(updated).ShouldBeTrue();
var assignment = meta.GetStreamAssignment("updatable");
assignment!.ConfigJson.ShouldContain("new.>");
}
[Fact]
public void ProcessUpdateStreamAssignment_returns_false_for_nonexistent()
{
var meta = new JetStreamMetaGroup(3);
var sa = CreateStreamAssignment("ghost", "rg-g");
meta.ProcessUpdateStreamAssignment(sa).ShouldBeFalse();
}
[Fact]
public void ProcessConsumerAssignment_requires_existing_stream()
{
var meta = new JetStreamMetaGroup(3);
var ca = new ConsumerAssignment
{
ConsumerName = "orphan-consumer",
StreamName = "nonexistent-stream",
Group = new RaftGroup { Name = "rg-c", Peers = ["n1", "n2", "n3"] },
};
meta.ProcessConsumerAssignment(ca).ShouldBeFalse();
}
[Fact]
public void ProcessConsumerAssignment_succeeds_with_existing_stream()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("s1", "rg-s1"));
var ca = new ConsumerAssignment
{
ConsumerName = "c1",
StreamName = "s1",
Group = new RaftGroup { Name = "rg-c1", Peers = ["n1", "n2", "n3"] },
};
meta.ProcessConsumerAssignment(ca).ShouldBeTrue();
meta.ConsumerCount.ShouldBe(1);
}
[Fact]
public void ProcessStreamRemoval_cascades_to_consumers()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("cascade", "rg-cas"));
meta.ProcessConsumerAssignment(new ConsumerAssignment
{
ConsumerName = "c1",
StreamName = "cascade",
Group = new RaftGroup { Name = "rg-c1", Peers = ["n1", "n2", "n3"] },
});
meta.ProcessStreamRemoval("cascade").ShouldBeTrue();
meta.StreamCount.ShouldBe(0);
meta.ConsumerCount.ShouldBe(0);
}
[Fact]
public void ProcessStreamRemoval_returns_false_for_nonexistent()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamRemoval("nope").ShouldBeFalse();
}
[Fact]
public void ProcessConsumerRemoval_returns_false_for_nonexistent_stream()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessConsumerRemoval("ghost", "c1").ShouldBeFalse();
}
[Fact]
public void ProcessConsumerRemoval_returns_false_for_nonexistent_consumer()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("s1", "rg-s1"));
meta.ProcessConsumerRemoval("s1", "nope").ShouldBeFalse();
}
[Fact]
public void ProcessConsumerRemoval_succeeds()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("s1", "rg-s1"));
meta.ProcessConsumerAssignment(new ConsumerAssignment
{
ConsumerName = "c1",
StreamName = "s1",
Group = new RaftGroup { Name = "rg-c1", Peers = ["n1", "n2"] },
});
meta.ProcessConsumerRemoval("s1", "c1").ShouldBeTrue();
meta.ConsumerCount.ShouldBe(0);
}
[Fact]
public void ProcessUpdateStreamAssignment_preserves_consumers()
{
var meta = new JetStreamMetaGroup(3);
meta.ProcessStreamAssignment(CreateStreamAssignment("s1", "rg-s1", """{"subjects":["old"]}"""));
meta.ProcessConsumerAssignment(new ConsumerAssignment
{
ConsumerName = "c1",
StreamName = "s1",
Group = new RaftGroup { Name = "rg-c1", Peers = ["n1", "n2"] },
});
var updated = CreateStreamAssignment("s1", "rg-s1", """{"subjects":["new"]}""");
meta.ProcessUpdateStreamAssignment(updated).ShouldBeTrue();
meta.ConsumerCount.ShouldBe(1);
meta.GetConsumerAssignment("s1", "c1").ShouldNotBeNull();
}
// Helper to create a StreamAssignment (StreamName is `required` so we must always provide it)
private static StreamAssignment CreateStreamAssignment(string name, string groupName, string config = "{}")
=> new()
{
StreamName = name,
Group = new RaftGroup { Name = groupName, Peers = ["n1", "n2", "n3"] },
ConfigJson = config,
};
}