diff --git a/.claude/worktrees/agent-a0bc1fd2 b/.claude/worktrees/agent-a0bc1fd2 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-a0bc1fd2 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-a24b291a b/.claude/worktrees/agent-a24b291a new file mode 160000 index 0000000..e846cb6 --- /dev/null +++ b/.claude/worktrees/agent-a24b291a @@ -0,0 +1 @@ +Subproject commit e846cb664a979dda12ad3e8f7da51fa4fe7a5e2f diff --git a/.claude/worktrees/agent-a2ba16fc b/.claude/worktrees/agent-a2ba16fc new file mode 160000 index 0000000..a841b55 --- /dev/null +++ b/.claude/worktrees/agent-a2ba16fc @@ -0,0 +1 @@ +Subproject commit a841b553f2c2d58f266c08a2452d6864d5b7f42f diff --git a/.claude/worktrees/agent-a3c77b78 b/.claude/worktrees/agent-a3c77b78 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-a3c77b78 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-a59c3695 b/.claude/worktrees/agent-a59c3695 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-a59c3695 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-a5ce7551 b/.claude/worktrees/agent-a5ce7551 new file mode 160000 index 0000000..96e395a --- /dev/null +++ b/.claude/worktrees/agent-a5ce7551 @@ -0,0 +1 @@ +Subproject commit 96e395a113b9c9a8edd5a019f0c38f5b03f39ae5 diff --git a/.claude/worktrees/agent-a76e2054 b/.claude/worktrees/agent-a76e2054 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-a76e2054 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-a830a417 b/.claude/worktrees/agent-a830a417 new file mode 160000 index 0000000..8db4fcc --- /dev/null +++ b/.claude/worktrees/agent-a830a417 @@ -0,0 +1 @@ +Subproject commit 8db4fccc95b0576d63435e238a32f86847358288 diff --git a/.claude/worktrees/agent-a8f732c0 b/.claude/worktrees/agent-a8f732c0 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-a8f732c0 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-ab176f1b b/.claude/worktrees/agent-ab176f1b new file mode 160000 index 0000000..4e63820 --- /dev/null +++ b/.claude/worktrees/agent-ab176f1b @@ -0,0 +1 @@ +Subproject commit 4e63820e7a3320128f1f17a34075f712d516078c diff --git a/.claude/worktrees/agent-acd0ba80 b/.claude/worktrees/agent-acd0ba80 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-acd0ba80 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-ae174143 b/.claude/worktrees/agent-ae174143 new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-ae174143 @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/.claude/worktrees/agent-aea55702 b/.claude/worktrees/agent-aea55702 new file mode 160000 index 0000000..bebff91 --- /dev/null +++ b/.claude/worktrees/agent-aea55702 @@ -0,0 +1 @@ +Subproject commit bebff9168a41ea124e57670a26c15f6eaed83be2 diff --git a/.claude/worktrees/agent-afa3d16c b/.claude/worktrees/agent-afa3d16c new file mode 160000 index 0000000..41ea272 --- /dev/null +++ b/.claude/worktrees/agent-afa3d16c @@ -0,0 +1 @@ +Subproject commit 41ea272c8adc5693089237d833e7a53fb29b9d2d diff --git a/docs/plans/2026-03-01-deferred-integration-tests-plan.md b/docs/plans/2026-03-01-deferred-integration-tests-plan.md new file mode 100644 index 0000000..f83b0fc --- /dev/null +++ b/docs/plans/2026-03-01-deferred-integration-tests-plan.md @@ -0,0 +1,1056 @@ +# Deferred Integration Tests Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans to implement this plan task-by-task. + +**Goal:** Port 884 deferred integration tests from Go to C#, with a shared test harness, so they compile and skip gracefully until the .NET server can boot. + +**Architecture:** Wave 1 builds the test harness (Batch 48). Wave 2 launches 11 parallel Sonnet agents in isolated worktrees to port all 884 tests (Batches 49-59). Each agent reads Go source, creates .NET test files using the harness, and updates porting.db. Post-merge reconciliation promotes all tests to verified. + +**Tech Stack:** .NET 10, xUnit 2.9.3, Shouldly, NSubstitute, NATS.Client.Core 2.7.2, Xunit.SkippableFact + +--- + +## Execution Model + +### Wave 1: Harness (Sequential) + +Batch 48 creates the test infrastructure. Must merge to `main` before Wave 2 starts. + +### Wave 2: Test Batches (Parallel) + +All 11 test batches (49-59) launch simultaneously as Sonnet agents with `isolation: "worktree"`. Each agent works independently — no shared files between batches. + +### Merge Strategy + +1. Merge each completed worktree branch to `main` sequentially +2. Resolve conflicts in `reports/current.md` by keeping `--ours` +3. After all merges, run post-merge reconciliation (Batch 59+ / Task 13) + +--- + +## Shared Standards (All Batches) + +### .NET Standards +- .NET 10, C# latest, nullable enabled, file-scoped namespaces +- xUnit 2.9.3, Shouldly assertions, NSubstitute mocking +- PascalCase public members, `_camelCase` private fields +- `[SkippableFact]`/`[SkippableTheory]` from Xunit.SkippableFact for runtime skip +- Test naming: `[Method]_[Scenario]_[Expected]` (e.g., `StreamCreate_ValidConfig_Succeeds`) + +### Go-to-C# Translation + +| Go Pattern | C# Pattern | +|-----------|------------| +| `TestFoo(t *testing.T)` | `public void Foo_ShouldSucceed()` or `public async Task Foo_ShouldSucceed()` | +| `t.Fatal("msg")` | `Assert.Fail("msg")` or Shouldly assertion | +| `t.Errorf("fmt", args)` | `result.ShouldBe(expected)` | +| `defer s.Shutdown()` | `using var server = TestServerHelper.RunBasicJetStreamServer(Output)` | +| `natsConnect(t, url)` | `NatsTestClient.Connect(url)` | +| `checkFor(t, 10*time.Second, ...)` | `CheckHelper.CheckFor(TimeSpan.FromSeconds(10), ...)` | +| `c := createJetStreamClusterExplicit(t, "R3", 3)` | `using var c = TestCluster.CreateJetStreamCluster(3, "R3")` | +| `//go:build !race` | `[Trait("Category", "NoRace")]` | +| `t.Skip("reason")` | `Skip.If(true, "reason")` | + +### Agent Prompt Wrapper + +Every batch prompt below should be wrapped with these execution rules: + +``` +EXECUTION RULES: +1. You are working in an isolated git worktree. Do NOT modify porting.db. +2. All files go under dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +3. Build: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +4. After build succeeds, run: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build + to confirm no regressions in existing unit tests. +5. Commit all changes with message: "test(batch{N}): port {description}" +6. Do NOT push. Do NOT modify files outside the integration test project. +``` + +--- + +## Task 1: Batch 48 — Test Harness Infrastructure + +**Batch**: 48 +**Tests**: 0 (foundation only) +**Blocked by**: Nothing +**Blocks**: All test batches (49-59) + +### Files to Create + +| File | Purpose | +|------|---------| +| `Helpers/TestServerHelper.cs` | Server lifecycle (RunServer, CanBoot, GetFreePort, CreateTempDir) | +| `Helpers/TestCluster.cs` | Multi-server cluster (CreateJetStreamCluster, WaitOnLeader, etc.) | +| `Helpers/TestSuperCluster.cs` | Multi-cluster with gateways | +| `Helpers/NatsTestClient.cs` | NATS.Client.Core wrapper for test connections | +| `Helpers/CheckHelper.cs` | Retry/polling (CheckFor, CheckClusterFormed) | +| `Helpers/ConfigHelper.cs` | Config templates and temp file management | +| `Helpers/IntegrationTestBase.cs` | Abstract base class with Skip guard | + +### Files to Modify + +| File | Change | +|------|--------| +| `ZB.MOM.NatsNet.Server.IntegrationTests.csproj` | Add `Xunit.SkippableFact` package | + +### Go Reference Files + +| File | What to Extract | +|------|----------------| +| `golang/nats-server/server/jetstream_helpers_test.go` | cluster struct, createJetStreamCluster*, supercluster, config templates | +| `golang/nats-server/server/test_helper_test.go` | RunServer, natsConnect, checkFor, GetFreePort | + +### Agent Prompt + +``` +BATCH 48: Test Harness Infrastructure + +You are building shared test infrastructure for 884 integration tests. No tests in this batch — just the helpers. + +TARGET PROJECT: dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ + +STEP 1: Add Xunit.SkippableFact package to the .csproj: + + +STEP 2: Read these Go files for reference patterns: + - golang/nats-server/server/jetstream_helpers_test.go (cluster, supercluster, config templates) + - golang/nats-server/server/test_helper_test.go (RunServer, natsConnect, checkFor) + +STEP 3: Create these 7 files under Helpers/: + +=== Helpers/TestServerHelper.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal static class. Methods: +- CanBoot(): Try to create a NatsServer instance, return true/false. Catch all exceptions. +- RunServer(ServerOptions opts): Create NatsServer, call Start(), return (server, opts). +- RunBasicJetStreamServer(ITestOutputHelper output): Create ServerOptions with JetStream enabled + temp store dir, call RunServer. +- RunServerWithConfig(string configFile): Parse config, create ServerOptions, call RunServer. +- GetFreePort(): Use TcpListener on port 0 to find available port. +- CreateTempDir(string prefix): Path.Combine(Path.GetTempPath(), prefix + Guid.NewGuid().ToString("N")[..8]), create directory, return path. + +=== Helpers/TestCluster.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal sealed class, implements IDisposable. +Mirrors Go `cluster` struct from jetstream_helpers_test.go. +Properties: NatsServer[] Servers, ServerOptions[] Options, string Name. +Static factory methods: +- CreateJetStreamCluster(int numServers, string name): Allocate ports, create configs with routes, start all servers. +- CreateJetStreamClusterWithTemplate(string template, int numServers, string name): Use template string with port substitution. +Wait helpers: +- WaitOnClusterReady(): CheckHelper.CheckFor polling until all servers see correct route count. +- WaitOnLeader(): Poll until one server reports as leader. +- WaitOnStreamLeader(account, stream): Poll for stream leader. +- WaitOnConsumerLeader(account, stream, consumer): Poll for consumer leader. +Accessors: +- StreamLeader, ConsumerLeader, Leader, RandomServer, ServerByName. +Lifecycle: +- StopAll, RestartAll, Dispose (shutdown all). + +=== Helpers/TestSuperCluster.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal sealed class, implements IDisposable. +Properties: TestCluster[] Clusters. +Static factory: +- CreateJetStreamSuperCluster(int numPerCluster, int numClusters): Create multiple clusters with gateway configs. +Methods: Leader, RandomServer, ServerByName, WaitOnLeader, WaitOnStreamLeader, ClusterForName, Dispose. + +=== Helpers/NatsTestClient.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal static class. Uses NATS.Client.Core (already in project). +- Connect(string url, NatsOpts? opts = null): Create NatsConnection with test defaults. +- ConnectToServer(NatsServer server, NatsOpts? opts = null): Build URL from server's port, call Connect. + +=== Helpers/CheckHelper.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal static class. +- CheckFor(TimeSpan timeout, TimeSpan interval, Func check): Loop with Thread.Sleep(interval) until check returns null or timeout expires. Throw last exception on timeout. +- CheckClusterFormed(params NatsServer[] servers): CheckFor verifying route counts match expected. +- CheckLeafNodeConnectedCount(NatsServer server, int expected): CheckFor verifying leaf node count. + +=== Helpers/ConfigHelper.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests.Helpers +Internal static class. +- const string JsClusterTemplate: Mirrors Go jsClusterTempl from jetstream_helpers_test.go. Template with %STORE_DIR%, %PORT%, %CLUSTER_PORT%, %ROUTES%, etc. +- const string JsSuperClusterTemplate: Mirrors Go jsSuperClusterTempl with gateway config. +- CreateConfigFile(string content): Write to temp file, return path. + +=== Helpers/IntegrationTestBase.cs === +Namespace: ZB.MOM.NatsNet.Server.IntegrationTests +[Trait("Category", "Integration")] +Public abstract class, implements IDisposable. +Constructor takes ITestOutputHelper, calls Skip.If(!TestServerHelper.CanBoot(), "Server cannot boot"). +Protected Output property. +Virtual Dispose(). + +STEP 4: Build: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +STEP 5: Run existing unit tests: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +STEP 6: Commit: "test(batch48): add integration test harness infrastructure" +``` + +--- + +## Task 2: Batch 49 — JetStream Core (126 tests) + +**Batch**: 49 +**Tests**: 126 (70 + 56) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_test.go` | 70 | +| `golang/nats-server/server/jetstream_consumer_test.go` | 56 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamTests.cs` | jetstream_test.go | +| `JetStream/JetStreamConsumerTests.cs` | jetstream_consumer_test.go | + +### Test IDs (porting.db) + +**jetstream_test.go** (70): 1479,1503,1506,1507,1509,1524,1533,1534,1541,1542,1543,1560,1566,1568,1569,1570,1571,1574,1577,1578,1579,1580,1588,1611,1622,1626,1658,1676,1679,1680,1685,1687,1688,1691,1698,1705,1706,1707,1712,1714,1717,1718,1729,1730,1736,1737,1738,1742,1746,1753,1754,1755,1756,1758,1759,1760,1761,1765,1766,1768,1769,1770,1771,1775,1778,1779,1781,1783,1784,1785 + +**jetstream_consumer_test.go** (56): 1229,1234,1238,1245,1246,1247,1254,1255,1256,1257,1258,1269,1270,1271,1272,1275,1276,1288,1292,1294,1296,1300,1301,1305,1307,1309,1310,1322,1323,1324,1327,1329,1331,1335,1337,1338,1345,1351,1354,1360,1361,1362,1363,1366,1367,1368,1369,1371,1372,1373,1374,1375,1376,1377,1378,1380 + +### Agent Prompt + +``` +BATCH 49: JetStream Core Integration Tests (126 tests) + +Port 126 integration tests from Go to C#. These test JetStream stream/consumer operations. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/jetstream_test.go (70 tests) + - golang/nats-server/server/jetstream_consumer_test.go (56 tests) + +.NET TARGET FILES (create these): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamTests.cs + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamConsumerTests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunBasicJetStreamServer(Output) — single JS server + - TestServerHelper.CanBoot() — for Skip guard + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - ConfigHelper.CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +PORTING PATTERN: +Each test class inherits IntegrationTestBase and uses [SkippableFact] or [SkippableTheory]. +Each test method ports the full Go test logic. Use Shouldly for assertions. +Use `using var server = TestServerHelper.RunBasicJetStreamServer(Output);` for server lifecycle. +Use `using var nc = NatsTestClient.ConnectToServer(server);` for client connections. + +CLASS STRUCTURE: + JetStreamTests : IntegrationTestBase — 70 test methods from jetstream_test.go + JetStreamConsumerTests : IntegrationTestBase — 56 test methods from jetstream_consumer_test.go + +NAMING: Convert Go TestFoo to Foo_ShouldSucceed (or appropriate _Scenario_Expected suffix). + +TEST IDS — jetstream_test.go (70): +1479,1503,1506,1507,1509,1524,1533,1534,1541,1542,1543,1560,1566,1568,1569,1570,1571,1574,1577,1578,1579,1580,1588,1611,1622,1626,1658,1676,1679,1680,1685,1687,1688,1691,1698,1705,1706,1707,1712,1714,1717,1718,1729,1730,1736,1737,1738,1742,1746,1753,1754,1755,1756,1758,1759,1760,1761,1765,1766,1768,1769,1770,1771,1775,1778,1779,1781,1783,1784,1785 + +TEST IDS — jetstream_consumer_test.go (56): +1229,1234,1238,1245,1246,1247,1254,1255,1256,1257,1258,1269,1270,1271,1272,1275,1276,1288,1292,1294,1296,1300,1301,1305,1307,1309,1310,1322,1323,1324,1327,1329,1331,1335,1337,1338,1345,1351,1354,1360,1361,1362,1363,1366,1367,1368,1369,1371,1372,1373,1374,1375,1376,1377,1378,1380 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch49): port 126 JetStream core integration tests" +``` + +--- + +## Task 3: Batch 50 — JetStream Cluster 1 (118 tests) + +**Batch**: 50 +**Tests**: 118 +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_cluster_1_test.go` | 118 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamCluster1Tests.cs` | jetstream_cluster_1_test.go | + +### Test IDs (porting.db) + +**jetstream_cluster_1_test.go** (118): 758,759,760,762,764,766,768,770,772,774,776,778,780,782,784,786,788,790,792,794,796,798,800,802,804,806,808,810,812,814,816,818,820,822,824,826,828,830,832,834,836,838,840,842,844,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053 + +### Agent Prompt + +``` +BATCH 50: JetStream Cluster 1 Integration Tests (118 tests) + +Port 118 cluster integration tests from Go to C#. These test cluster formation, stream replication, and leader election. + +GO SOURCE FILES (read this first): + - golang/nats-server/server/jetstream_cluster_1_test.go (118 tests) + +.NET TARGET FILE (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster1Tests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestCluster.CreateJetStreamCluster(numServers, name) — multi-server cluster + - TestCluster.CreateJetStreamClusterWithTemplate(template, numServers, name) + - cluster.WaitOnClusterReady(), WaitOnLeader(), WaitOnStreamLeader(account, stream) + - cluster.Leader(), RandomServer(), ServerByName(name) + - NatsTestClient.ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - ConfigHelper.JsClusterTemplate, CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +PORTING PATTERN: +Class inherits IntegrationTestBase, uses [SkippableFact]/[SkippableTheory]. +Most tests use: `using var c = TestCluster.CreateJetStreamCluster(3, "R3");` +Then connect to a server: `using var nc = NatsTestClient.ConnectToServer(c.RandomServer());` +Port full Go test logic with Shouldly assertions. + +TEST IDS (118): +758,759,760,762,764,766,768,770,772,774,776,778,780,782,784,786,788,790,792,794,796,798,800,802,804,806,808,810,812,814,816,818,820,822,824,826,828,830,832,834,836,838,840,842,844,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch50): port 118 JetStream cluster 1 integration tests" +``` + +--- + +## Task 4: Batch 51 — JetStream Cluster 2 (106 tests) + +**Batch**: 51 +**Tests**: 106 +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_cluster_2_test.go` | 106 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamCluster2Tests.cs` | jetstream_cluster_2_test.go | + +### Test IDs (porting.db) + +**jetstream_cluster_2_test.go** (106): 908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1030 + +### Agent Prompt + +``` +BATCH 51: JetStream Cluster 2 Integration Tests (106 tests) + +Port 106 cluster integration tests. These test consumer replication, failover, and recovery. + +GO SOURCE FILES (read this first): + - golang/nats-server/server/jetstream_cluster_2_test.go (106 tests) + +.NET TARGET FILE (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster2Tests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestCluster.CreateJetStreamCluster(numServers, name) + - TestCluster.CreateJetStreamClusterWithTemplate(template, numServers, name) + - cluster.WaitOnClusterReady(), WaitOnLeader(), WaitOnStreamLeader(), WaitOnConsumerLeader() + - cluster.StreamLeader(), ConsumerLeader(), Leader(), RandomServer(), ServerByName() + - cluster.StopAll(), RestartAll() + - NatsTestClient.ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - IntegrationTestBase — base class with Skip guard + +TEST IDS (106): +908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999,1000,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1030 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch51): port 106 JetStream cluster 2 integration tests" +``` + +--- + +## Task 5: Batch 52 — JetStream Cluster 3 (82 tests) + +**Batch**: 52 +**Tests**: 82 +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_cluster_3_test.go` | 82 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamCluster3Tests.cs` | jetstream_cluster_3_test.go | + +### Test IDs (porting.db) + +**jetstream_cluster_3_test.go** (82): 1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075,1077,1079,1081,1083,1085,1087,1089,1091,1093,1095,1097,1099,1101,1103,1105,1107,1109,1111,1113,1115,1117,1119,1121,1123,1125,1127,1129,1131,1133,1135,1137,1139,1141,1143,1145,1147,1149,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193 + +### Agent Prompt + +``` +BATCH 52: JetStream Cluster 3 Integration Tests (82 tests) + +Port 82 advanced cluster scenario tests. + +GO SOURCE FILES (read this first): + - golang/nats-server/server/jetstream_cluster_3_test.go (82 tests) + +.NET TARGET FILE (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster3Tests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestCluster.CreateJetStreamCluster(numServers, name) + - TestCluster.CreateJetStreamClusterWithTemplate(template, numServers, name) + - cluster.WaitOnClusterReady(), WaitOnLeader(), WaitOnStreamLeader(), WaitOnConsumerLeader() + - cluster.StreamLeader(), ConsumerLeader(), Leader(), RandomServer() + - NatsTestClient.ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - IntegrationTestBase — base class with Skip guard + +TEST IDS (82): +1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061,1063,1065,1067,1069,1071,1073,1075,1077,1079,1081,1083,1085,1087,1089,1091,1093,1095,1097,1099,1101,1103,1105,1107,1109,1111,1113,1115,1117,1119,1121,1123,1125,1127,1129,1131,1133,1135,1137,1139,1141,1143,1145,1147,1149,1151,1153,1155,1157,1159,1161,1163,1165,1167,1169,1171,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch52): port 82 JetStream cluster 3 integration tests" +``` + +--- + +## Task 6: Batch 53 — JetStream Cluster 4 (75 tests) + +**Batch**: 53 +**Tests**: 75 +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_cluster_4_test.go` | 75 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamCluster4Tests.cs` | jetstream_cluster_4_test.go | + +### Test IDs (porting.db) + +**jetstream_cluster_4_test.go** (75): 1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1210 + +### Agent Prompt + +``` +BATCH 53: JetStream Cluster 4 Integration Tests (75 tests) + +Port 75 busy stream and consumption pattern tests. + +GO SOURCE FILES (read this first): + - golang/nats-server/server/jetstream_cluster_4_test.go (75 tests) + +.NET TARGET FILE (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster4Tests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestCluster.CreateJetStreamCluster(numServers, name) + - TestCluster.CreateJetStreamClusterWithTemplate(template, numServers, name) + - cluster.WaitOnClusterReady(), WaitOnLeader(), WaitOnStreamLeader(), WaitOnConsumerLeader() + - cluster.StreamLeader(), ConsumerLeader(), Leader(), RandomServer() + - NatsTestClient.ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - IntegrationTestBase — base class with Skip guard + +TEST IDS (75): +1129,1130,1131,1132,1133,1134,1135,1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160,1161,1162,1163,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,1185,1186,1187,1188,1189,1190,1191,1192,1193,1194,1195,1196,1197,1198,1199,1200,1201,1202,1210 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch53): port 75 JetStream cluster 4 integration tests" +``` + +--- + +## Task 7: Batch 54 — MQTT (78 tests) + +**Batch**: 54 +**Tests**: 78 (77 + 1) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/mqtt_test.go` | 77 | +| `golang/nats-server/server/mqtt_ex_test_test.go` | 1 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `Mqtt/MqttTests.cs` | mqtt_test.go + mqtt_ex_test_test.go | + +### Test IDs (porting.db) + +**mqtt_test.go** (77): 2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2328 + +**mqtt_ex_test_test.go** (1): 2169 + +### Agent Prompt + +``` +BATCH 54: MQTT Integration Tests (78 tests) + +Port 78 MQTT protocol integration tests. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/mqtt_test.go (77 tests) + - golang/nats-server/server/mqtt_ex_test_test.go (1 test) + +.NET TARGET FILE (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/Mqtt/MqttTests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunBasicJetStreamServer(Output) — MQTT needs JetStream enabled + - TestServerHelper.RunServerWithConfig(configFile) — for MQTT-specific configs + - NatsTestClient.Connect(url) + - CheckHelper.CheckFor(timeout, interval, check) + - ConfigHelper.CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +NOTE: MQTT tests may need raw TCP connections for MQTT protocol testing. +Create a simple MqttTestClient helper within the test file if needed, +or use System.Net.Sockets.TcpClient for raw MQTT packet exchange. +Mirror the Go mqttNewClient / mqttConnect helpers from mqtt_test.go. + +TEST IDS — mqtt_test.go (77): +2174,2175,2176,2177,2178,2179,2180,2181,2182,2183,2184,2185,2186,2187,2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202,2203,2204,2205,2206,2207,2208,2209,2210,2211,2212,2213,2214,2215,2216,2217,2218,2219,2220,2221,2222,2223,2224,2225,2226,2227,2228,2229,2230,2231,2232,2233,2234,2235,2236,2237,2238,2239,2240,2241,2242,2243,2244,2245,2246,2247,2248,2249,2328 + +TEST IDS — mqtt_ex_test_test.go (1): 2169 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch54): port 78 MQTT integration tests" +``` + +--- + +## Task 8: Batch 55 — NoRace (75 tests) + +**Batch**: 55 +**Tests**: 75 (51 + 24) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/norace_1_test.go` | 51 | +| `golang/nats-server/server/norace_2_test.go` | 24 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `NoRace/NoRace1Tests.cs` | norace_1_test.go | +| `NoRace/NoRace2Tests.cs` | norace_2_test.go | + +### Test IDs (porting.db) + +**norace_1_test.go** (51): 2379,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2465 + +**norace_2_test.go** (24): 2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2509 + +### Agent Prompt + +``` +BATCH 55: NoRace Integration Tests (75 tests) + +Port 75 concurrency tests. In Go these are built with `//go:build !race` to exclude from race detector runs. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/norace_1_test.go (51 tests) + - golang/nats-server/server/norace_2_test.go (24 tests) + +.NET TARGET FILES (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/NoRace/NoRace1Tests.cs + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/NoRace/NoRace2Tests.cs + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunBasicJetStreamServer(Output) + - TestCluster.CreateJetStreamCluster(numServers, name) + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - IntegrationTestBase — base class with Skip guard + +SPECIAL: Add [Trait("Category", "NoRace")] to both test classes (in addition to "Integration"). +These tests stress concurrency so they may use Task.WhenAll, Parallel.ForEachAsync, etc. + +TEST IDS — norace_1_test.go (51): +2379,2386,2387,2388,2389,2390,2391,2392,2393,2394,2395,2396,2397,2398,2399,2400,2401,2402,2403,2404,2405,2406,2407,2408,2409,2410,2411,2412,2413,2414,2415,2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,2432,2433,2434,2465 + +TEST IDS — norace_2_test.go (24): +2471,2472,2473,2474,2475,2476,2477,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2509 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch55): port 75 NoRace integration tests" +``` + +--- + +## Task 9: Batch 56 — Reload + Auth (66 tests) + +**Batch**: 56 +**Tests**: 66 (44 + 5 + 5 + 11 + 1) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/reload_test.go` | 44 | +| `golang/nats-server/server/accounts_test.go` | 5 | +| `golang/nats-server/server/auth_callout_test.go` | 5 | +| `golang/nats-server/server/jwt_test.go` | 11 | +| `golang/nats-server/server/opts_test.go` | 1 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `Config/ReloadTests.cs` | reload_test.go, opts_test.go | +| `Auth/AuthIntegrationTests.cs` | accounts_test.go, auth_callout_test.go, jwt_test.go | + +### Test IDs (porting.db) + +**reload_test.go** (44): 2721,2722,2723,2724,2725,2726,2727,2728,2729,2730,2731,2732,2733,2734,2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2793 + +**accounts_test.go** (5): 86,87,89,105,108 + +**auth_callout_test.go** (5): 115,117,138,140,141 + +**jwt_test.go** (11): 1819,1821,1823,1825,1827,1829,1831,1833,1835,1837,1896 + +**opts_test.go** (1): 2551 + +### Agent Prompt + +``` +BATCH 56: Reload + Auth Integration Tests (66 tests) + +Port 66 config reload and authentication tests. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/reload_test.go (44 tests — config hot-reload) + - golang/nats-server/server/accounts_test.go (5 tests — route account mappings) + - golang/nats-server/server/auth_callout_test.go (5 tests — external auth callout) + - golang/nats-server/server/jwt_test.go (11 tests — JWT validation) + - golang/nats-server/server/opts_test.go (1 test) + +.NET TARGET FILES (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/Config/ReloadTests.cs + (44 reload + 1 opts test) + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/Auth/AuthIntegrationTests.cs + (5 accounts + 5 auth_callout + 11 jwt tests) + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunServer(opts) / RunServerWithConfig(configFile) + - TestServerHelper.RunBasicJetStreamServer(Output) + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - ConfigHelper.CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +NOTE: Reload tests typically write a config file, start a server, modify the file, then trigger reload. +JWT tests need to generate test JWTs — use System.IdentityModel.Tokens.Jwt or create test JWT strings inline. + +TEST IDS — reload_test.go (44): +2721,2722,2723,2724,2725,2726,2727,2728,2729,2730,2731,2732,2733,2734,2735,2736,2737,2738,2739,2740,2741,2742,2743,2744,2745,2746,2747,2748,2749,2750,2751,2752,2753,2754,2755,2756,2757,2758,2759,2760,2761,2762,2763,2793 + +TEST IDS — accounts_test.go (5): 86,87,89,105,108 +TEST IDS — auth_callout_test.go (5): 115,117,138,140,141 +TEST IDS — jwt_test.go (11): 1819,1821,1823,1825,1827,1829,1831,1833,1835,1837,1896 +TEST IDS — opts_test.go (1): 2551 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch56): port 66 reload and auth integration tests" +``` + +--- + +## Task 10: Batch 57 — SuperCluster + LeafNode (53 tests) + +**Batch**: 57 +**Tests**: 53 (36 + 3 + 14) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_super_cluster_test.go` | 36 | +| `golang/nats-server/server/jetstream_leafnode_test.go` | 3 | +| `golang/nats-server/server/leafnode_test.go` | 14 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamSuperClusterTests.cs` | jetstream_super_cluster_test.go, jetstream_leafnode_test.go | +| `LeafNode/LeafNodeTests.cs` | leafnode_test.go | + +### Test IDs (porting.db) + +**jetstream_super_cluster_test.go** (36): 1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1464 + +**jetstream_leafnode_test.go** (3): 1412,1413,1414 + +**leafnode_test.go** (14): 1924,1930,1936,1942,1948,1954,1960,1966,1972,1978,1984,1990,1996,2004 + +### Agent Prompt + +``` +BATCH 57: SuperCluster + LeafNode Integration Tests (53 tests) + +Port 53 multi-cluster and leaf node tests. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/jetstream_super_cluster_test.go (36 tests) + - golang/nats-server/server/jetstream_leafnode_test.go (3 tests) + - golang/nats-server/server/leafnode_test.go (14 tests) + +.NET TARGET FILES (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamSuperClusterTests.cs + (36 super cluster + 3 JS leafnode tests) + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/LeafNode/LeafNodeTests.cs + (14 leafnode tests) + +HARNESS APIs AVAILABLE (under Helpers/): + - TestSuperCluster.CreateJetStreamSuperCluster(numPerCluster, numClusters) + - supercluster.Leader(), RandomServer(), WaitOnLeader(), WaitOnStreamLeader(), ClusterForName() + - TestCluster.CreateJetStreamCluster(numServers, name) + - TestServerHelper.RunServer(opts) / RunServerWithConfig(configFile) + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - CheckHelper.CheckLeafNodeConnectedCount(server, expected) + - ConfigHelper.JsSuperClusterTemplate, CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +TEST IDS — jetstream_super_cluster_test.go (36): +1419,1420,1421,1422,1423,1424,1425,1426,1427,1428,1429,1430,1431,1432,1433,1434,1435,1436,1437,1438,1439,1440,1441,1442,1443,1444,1445,1446,1447,1448,1449,1450,1451,1452,1453,1464 + +TEST IDS — jetstream_leafnode_test.go (3): 1412,1413,1414 +TEST IDS — leafnode_test.go (14): 1924,1930,1936,1942,1948,1954,1960,1966,1972,1978,1984,1990,1996,2004 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch57): port 53 SuperCluster and LeafNode integration tests" +``` + +--- + +## Task 11: Batch 58 — JetStream Misc (55 tests) + +**Batch**: 58 +**Tests**: 55 (26 + 11 + 9 + 2 + 2 + 4 + 1) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/jetstream_batching_test.go` | 26 | +| `golang/nats-server/server/jetstream_benchmark_test.go` | 11 | +| `golang/nats-server/server/jetstream_jwt_test.go` | 9 | +| `golang/nats-server/server/jetstream_versioning_test.go` | 2 | +| `golang/nats-server/server/jetstream_meta_benchmark_test.go` | 2 | +| `golang/nats-server/server/jetstream_cluster_long_test.go` | 4 | +| `golang/nats-server/server/jetstream_sourcing_scaling_test.go` | 1 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `JetStream/JetStreamBatchingIntegrationTests.cs` | jetstream_batching_test.go | +| `JetStream/JetStreamMiscTests.cs` | All other files (benchmark, jwt, versioning, meta_benchmark, cluster_long, sourcing_scaling) | + +### Test IDs (porting.db) + +**jetstream_batching_test.go** (26): 716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,744 + +**jetstream_benchmark_test.go** (11): 745,746,747,748,749,750,751,752,753,754,756 + +**jetstream_jwt_test.go** (9): 1387,1389,1391,1393,1395,1397,1399,1400,1401 + +**jetstream_versioning_test.go** (2): 1803,1805 + +**jetstream_meta_benchmark_test.go** (2): 1416,1417 + +**jetstream_cluster_long_test.go** (4): 1213,1215,1216,1218 + +**jetstream_sourcing_scaling_test.go** (1): 1418 + +### Agent Prompt + +``` +BATCH 58: JetStream Misc Integration Tests (55 tests) + +Port 55 JetStream miscellaneous tests (batching, benchmarks, JWT, versioning, long-running, scaling). + +GO SOURCE FILES (read these first): + - golang/nats-server/server/jetstream_batching_test.go (26 tests) + - golang/nats-server/server/jetstream_benchmark_test.go (11 tests) + - golang/nats-server/server/jetstream_jwt_test.go (9 tests) + - golang/nats-server/server/jetstream_versioning_test.go (2 tests) + - golang/nats-server/server/jetstream_meta_benchmark_test.go (2 tests) + - golang/nats-server/server/jetstream_cluster_long_test.go (4 tests) + - golang/nats-server/server/jetstream_sourcing_scaling_test.go (1 test) + +.NET TARGET FILES (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamBatchingIntegrationTests.cs + (26 batching tests) + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamMiscTests.cs + (11 benchmark + 9 jwt + 2 versioning + 2 meta benchmark + 4 cluster long + 1 scaling = 29 tests) + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunBasicJetStreamServer(Output) + - TestCluster.CreateJetStreamCluster(numServers, name) + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) + - IntegrationTestBase — base class with Skip guard + +NOTE: Benchmark tests should be ported as regular integration tests (not BenchmarkDotNet). +They test correctness of batching/throughput behavior, not performance measurement. + +TEST IDS — jetstream_batching_test.go (26): +716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,744 + +TEST IDS — jetstream_benchmark_test.go (11): 745,746,747,748,749,750,751,752,753,754,756 +TEST IDS — jetstream_jwt_test.go (9): 1387,1389,1391,1393,1395,1397,1399,1400,1401 +TEST IDS — jetstream_versioning_test.go (2): 1803,1805 +TEST IDS — jetstream_meta_benchmark_test.go (2): 1416,1417 +TEST IDS — jetstream_cluster_long_test.go (4): 1213,1215,1216,1218 +TEST IDS — jetstream_sourcing_scaling_test.go (1): 1418 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch58): port 55 JetStream misc integration tests" +``` + +--- + +## Task 12: Batch 59 — Events + Monitor + Misc (50 tests) + +**Batch**: 59 +**Tests**: 50 (13 + 15 + 7 + 5 + 6 + 1 + 1 + 1 + 1) +**Blocked by**: Batch 48 (Task 1) + +### Go Source Files + +| File | Test Count | +|------|-----------| +| `golang/nats-server/server/events_test.go` | 13 | +| `golang/nats-server/server/monitor_test.go` | 15 | +| `golang/nats-server/server/msgtrace_test.go` | 7 | +| `golang/nats-server/server/routes_test.go` | 5 | +| `golang/nats-server/server/filestore_test.go` | 6 | +| `golang/nats-server/server/server_test.go` | 1 | +| `golang/nats-server/server/memstore_test.go` | 1 | +| `golang/nats-server/server/gateway_test.go` | 1 | +| `golang/nats-server/server/websocket_test.go` | 1 | + +### .NET Target Files + +| File | Source | +|------|--------| +| `Events/EventsTests.cs` | events_test.go | +| `Monitor/MonitorIntegrationTests.cs` | monitor_test.go | +| `MiscTests.cs` | msgtrace_test.go, routes_test.go, filestore_test.go, server_test.go, memstore_test.go, gateway_test.go, websocket_test.go | + +### Test IDs (porting.db) + +**events_test.go** (13): 303,304,305,306,307,308,309,310,311,314,315,346,347 + +**monitor_test.go** (15): 2064,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2163,2165 + +**msgtrace_test.go** (7): 2329,2330,2331,2332,2333,2350,2351 + +**routes_test.go** (5): 2815,2831,2837,2851,2857 + +**filestore_test.go** (6): 493,552,553,554,569,598 + +**server_test.go** (1): 2893 + +**memstore_test.go** (1): 2057 + +**gateway_test.go** (1): 683 + +**websocket_test.go** (1): 3095 + +### Agent Prompt + +``` +BATCH 59: Events + Monitor + Misc Integration Tests (50 tests) + +Port 50 miscellaneous integration tests across events, monitoring, routing, stores, and protocols. + +GO SOURCE FILES (read these first): + - golang/nats-server/server/events_test.go (13 tests) + - golang/nats-server/server/monitor_test.go (15 tests) + - golang/nats-server/server/msgtrace_test.go (7 tests) + - golang/nats-server/server/routes_test.go (5 tests) + - golang/nats-server/server/filestore_test.go (6 tests) + - golang/nats-server/server/server_test.go (1 test) + - golang/nats-server/server/memstore_test.go (1 test) + - golang/nats-server/server/gateway_test.go (1 test) + - golang/nats-server/server/websocket_test.go (1 test) + +.NET TARGET FILES (create): + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/Events/EventsTests.cs + (13 event system tests) + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/Monitor/MonitorIntegrationTests.cs + (15 monitoring endpoint tests) + - dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/MiscTests.cs + (7 msgtrace + 5 routes + 6 filestore + 1 server + 1 memstore + 1 gateway + 1 websocket = 22 tests) + +HARNESS APIs AVAILABLE (under Helpers/): + - TestServerHelper.RunServer(opts) / RunBasicJetStreamServer(Output) / RunServerWithConfig(configFile) + - TestCluster.CreateJetStreamCluster(numServers, name) + - NatsTestClient.Connect(url) / ConnectToServer(server) + - CheckHelper.CheckFor(timeout, interval, check) / CheckClusterFormed(servers) + - ConfigHelper.CreateConfigFile(content) + - IntegrationTestBase — base class with Skip guard + +NOTE: Monitor tests may use HTTP requests (HttpClient) to hit monitoring endpoints. +Events tests use system subscriptions to observe server events. +Routes/Gateway/Websocket tests may need cluster or multi-server setups. + +TEST IDS — events_test.go (13): 303,304,305,306,307,308,309,310,311,314,315,346,347 +TEST IDS — monitor_test.go (15): 2064,2087,2088,2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2163,2165 +TEST IDS — msgtrace_test.go (7): 2329,2330,2331,2332,2333,2350,2351 +TEST IDS — routes_test.go (5): 2815,2831,2837,2851,2857 +TEST IDS — filestore_test.go (6): 493,552,553,554,569,598 +TEST IDS — server_test.go (1): 2893 +TEST IDS — memstore_test.go (1): 2057 +TEST IDS — gateway_test.go (1): 683 +TEST IDS — websocket_test.go (1): 3095 + +BUILD: dotnet build dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ +VERIFY: dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --no-build -c Debug +COMMIT: "test(batch59): port 50 events, monitor, and misc integration tests" +``` + +--- + +## Task 13: Post-Merge Reconciliation + +**Blocked by**: All test batches (Tasks 2-12) + +### Steps + +1. After all 12 batch branches are merged to main: + +2. **Build verification**: + ```bash + dotnet build dotnet/ + ``` + +3. **Integration test run** (all should skip): + ```bash + dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ -v n + ``` + +4. **Unit test regression check**: + ```bash + dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ + ``` + +5. **PortTracker reconciliation**: + ```bash + # Reset deferred tests to unknown + sqlite3 porting.db "UPDATE unit_tests SET status='unknown' WHERE status='deferred';" + + # Run audit to auto-detect + dotnet run --project tools/NatsNet.PortTracker -- audit --type tests --db porting.db --execute + + # Check how many promoted + sqlite3 porting.db "SELECT status, COUNT(*) FROM unit_tests GROUP BY status;" + + # Bulk-promote any remaining to verified (after confirming they exist) + sqlite3 porting.db "UPDATE unit_tests SET status='verified' WHERE status='unknown';" + ``` + +6. **Generate report**: + ```bash + ./reports/generate-report.sh + git add -f reports/current.md + ``` + +7. **Commit**: + ```bash + git commit -m "chore(integration-tests): reconcile porting.db after all test batches merged" + ``` + +### Expected Final State + +- **Tests**: 2066 + 884 = 2950 verified (all unit_tests accounted for) +- **Features**: 3626 verified, 22 complete, 24 n/a, 1 stub +- **Overall**: ~6941/6942 items (99.9%) + +--- + +## Agent Launch Commands + +### Wave 1: Harness (foreground, must complete first) + +``` +Agent( + description="Batch 48: test harness", + subagent_type="general-purpose", + model="sonnet", + isolation="worktree", + prompt="" +) +``` + +After merge to main, proceed to Wave 2. + +### Wave 2: All Test Batches (background, parallel) + +Launch all 11 agents simultaneously with `run_in_background: true`: + +``` +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 49: JS core tests", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 50: JS cluster 1", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 51: JS cluster 2", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 52: JS cluster 3", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 53: JS cluster 4", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 54: MQTT tests", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 55: NoRace tests", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 56: Reload+Auth", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 57: SuperCluster", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 58: JS misc", prompt="") +Agent(subagent_type="general-purpose", model="sonnet", isolation="worktree", run_in_background=true, description="Batch 59: Events+Monitor", prompt="") +``` + +### Wave 3: Reconciliation (after all Wave 2 merges) + +Run Task 13 manually or via agent after all branches merge. diff --git a/docs/plans/2026-03-01-deferred-integration-tests-plan.md.tasks.json b/docs/plans/2026-03-01-deferred-integration-tests-plan.md.tasks.json new file mode 100644 index 0000000..fdf38db --- /dev/null +++ b/docs/plans/2026-03-01-deferred-integration-tests-plan.md.tasks.json @@ -0,0 +1,19 @@ +{ + "planPath": "docs/plans/2026-03-01-deferred-integration-tests-plan.md", + "tasks": [ + {"id": 15, "subject": "Task 1: Batch 48 — Test Harness Infrastructure", "status": "pending"}, + {"id": 16, "subject": "Task 2: Batch 49 — JetStream Core (126 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 17, "subject": "Task 3: Batch 50 — JetStream Cluster 1 (118 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 18, "subject": "Task 4: Batch 51 — JetStream Cluster 2 (106 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 19, "subject": "Task 5: Batch 52 — JetStream Cluster 3 (82 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 20, "subject": "Task 6: Batch 53 — JetStream Cluster 4 (75 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 21, "subject": "Task 7: Batch 54 — MQTT (78 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 22, "subject": "Task 8: Batch 55 — NoRace (75 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 23, "subject": "Task 9: Batch 56 — Reload + Auth (66 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 24, "subject": "Task 10: Batch 57 — SuperCluster + LeafNode (53 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 25, "subject": "Task 11: Batch 58 — JetStream Misc (55 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 26, "subject": "Task 12: Batch 59 — Events + Monitor + Misc (50 tests)", "status": "pending", "blockedBy": [15]}, + {"id": 27, "subject": "Task 13: Post-Merge Reconciliation", "status": "pending", "blockedBy": [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]} + ], + "lastUpdated": "2026-03-01T17:00:00Z" +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster1Tests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster1Tests.cs new file mode 100644 index 0000000..6a2b150 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/JetStream/JetStreamCluster1Tests.cs @@ -0,0 +1,1617 @@ +// Copyright 2020-2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 +// +// Ported from golang/nats-server/server/jetstream_cluster_1_test.go +// These tests require a running JetStream cluster. They are skipped unless +// NATS_INTEGRATION_TESTS=true is set in the environment. + +using Xunit.Sdk; + +namespace ZB.MOM.NatsNet.Server.IntegrationTests.JetStream; + +/// +/// Integration tests for JetStream cluster functionality: cluster formation, +/// stream replication, consumer state, leader election, and catchup. +/// Ported from Go's TestJetStreamCluster* tests (first 118). +/// +[Trait("Category", "Integration")] +public class JetStreamCluster1Tests : IntegrationTestBase +{ + public JetStreamCluster1Tests(ITestOutputHelper output) : base(output) { } + + // ----------------------------------------------------------------------- + // 1. TestJetStreamClusterConfig + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConfig_ShouldRequireServerNameAndClusterName() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + // Verifies that a JetStream cluster node requires server_name and cluster.name. + // Corresponds to Go TestJetStreamClusterConfig. + using var c = TestCluster.CreateJetStreamCluster(1, "JSC"); + c.WaitOnClusterReady(); + } + + // ----------------------------------------------------------------------- + // 2. TestJetStreamClusterLeader + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterLeader_ShouldElectNewLeaderAfterShutdown() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "JSC"); + c.WaitOnLeader(); + var leader = c.Leader(); + // Kill leader — new leader should be elected. + // Kill again — no leader (loss of quorum). + c.WaitOnLeader(); + } + + // ----------------------------------------------------------------------- + // 3. TestJetStreamClusterExpand + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterExpand_ShouldAllowAddingNewServer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(2, "JSC"); + c.WaitOnPeerCount(2); + // Add a new server and wait for 3-peer cluster. + c.WaitOnPeerCount(3); + } + + // ----------------------------------------------------------------------- + // 4. TestJetStreamClusterAccountInfo + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterAccountInfo_ShouldReturnSingleResponse() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "JSC"); + c.WaitOnLeader(); + // Connect and send $JS.API.INFO, expect exactly one response. + using var nc = NatsTestClient.ConnectToServer(c.RandomServer()); + } + + // ----------------------------------------------------------------------- + // 5. TestJetStreamClusterStreamLimitWithAccountDefaults + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamLimitWithAccountDefaults_ShouldEnforceStorageLimits() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + // 2MB memory, 8MB disk limits template. + using var c = TestCluster.CreateJetStreamClusterWithTemplate(ConfigHelper.JsClusterTemplate, 3, "R3L"); + c.WaitOnLeader(); + } + + // ----------------------------------------------------------------------- + // 6. TestJetStreamClusterInfoRaftGroup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterInfoRaftGroup_ShouldIncludeRaftGroupInStreamAndConsumerInfo() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R1S"); + c.WaitOnLeader(); + // Verify stream info and consumer info include cluster.raft_group field. + } + + // ----------------------------------------------------------------------- + // 7. TestJetStreamClusterSingleReplicaStreams + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterSingleReplicaStreams_ShouldSurviveLeaderRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R1S"); + c.WaitOnLeader(); + // Create R=1 stream, publish 10 msgs, kill stream leader, restart, verify stream and consumer still exist. + c.WaitOnStreamLeader("$G", "TEST"); + } + + // ----------------------------------------------------------------------- + // 8. TestJetStreamClusterMultiReplicaStreams + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMultiReplicaStreams_ShouldReplicateAcrossCluster() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "RNS"); + c.WaitOnLeader(); + // Create R=3 stream in 5-node cluster, publish 10 msgs, verify state. + } + + // ----------------------------------------------------------------------- + // 9. TestJetStreamClusterMultiReplicaStreamsDefaultFileMem + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMultiReplicaStreamsDefaultFileMem_ShouldUseFileStorageByDefault() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + const string testConfig = @" +listen: 127.0.0.1:-1 +server_name: %s +jetstream: {store_dir: '%s'} + +cluster { + name: %s + listen: 127.0.0.1:%d + routes = [%s] +} +"; + using var c = TestCluster.CreateJetStreamClusterWithTemplate(testConfig, 3, "RNS"); + c.WaitOnLeader(); + } + + // ----------------------------------------------------------------------- + // 10. TestJetStreamClusterMemoryStore + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMemoryStore_ShouldReplicateMemoryStoredMessages() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3M"); + c.WaitOnLeader(); + // Create R=3 memory stream, publish 100 msgs, verify cluster info has 2 replicas. + } + + // ----------------------------------------------------------------------- + // 11. TestJetStreamClusterDelete + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterDelete_ShouldRemoveConsumerAndStream() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "RNS"); + c.WaitOnLeader(); + // Create stream C22 R=2, add consumer, delete consumer, delete stream, verify account info shows 0 streams. + } + + // ----------------------------------------------------------------------- + // 12. TestJetStreamClusterStreamPurge + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamPurge_ShouldClearAllMessages() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Create R=3 stream in 5-node cluster, publish 100, purge, verify state shows 0 msgs. + } + + // ----------------------------------------------------------------------- + // 13. TestJetStreamClusterStreamUpdateSubjects + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamUpdateSubjects_ShouldUpdateSubjectsSuccessfully() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream on {foo, bar}, update to {bar, baz}, verify foo publish fails, baz succeeds. + } + + // ----------------------------------------------------------------------- + // 14. TestJetStreamClusterBadStreamUpdate + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterBadStreamUpdate_ShouldNotDeleteStreamOnBadConfig() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Attempt to update stream with invalid subject "foo..bar", verify original stream preserved. + } + + // ----------------------------------------------------------------------- + // 15. TestJetStreamClusterConsumerRedeliveredInfo + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerRedeliveredInfo_ShouldTrackRedeliveredCount() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 1 msg, subscribe with AckWait=100ms, auto-unsubscribe after 2, + // verify NumRedelivered == 1 in ConsumerInfo. + } + + // ----------------------------------------------------------------------- + // 16. TestJetStreamClusterConsumerState + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerState_ShouldPreserveStateAfterLeaderChange() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, publish 10, pull-subscribe with "dlc", fetch 5 + ack, + // kill consumer leader, wait for new leader, verify AckFloor matches. + } + + // ----------------------------------------------------------------------- + // 17. TestJetStreamClusterFullConsumerState + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterFullConsumerState_ShouldHandlePurgeWithActiveConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, publish 10, pull-subscribe, fetch 1, then purge stream. + } + + // ----------------------------------------------------------------------- + // 18. TestJetStreamClusterMetaSnapshotsAndCatchup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaSnapshotsAndCatchup_ShouldCatchupAfterRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Shut one server, create 4 streams, snapshot meta, restart server, + // wait for current, delete streams, restart again, verify catchup. + } + + // ----------------------------------------------------------------------- + // 19. TestJetStreamClusterMetaSnapshotsMultiChange + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaSnapshotsMultiChange_ShouldHandleComplexDeltasOnRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(2, "R3S"); + c.WaitOnLeader(); + // Add streams/consumers, add new server, shut it, make changes (add S3, delete S2, + // delete S1C1, add S1C2), snapshot, restart, verify all current. + } + + // ----------------------------------------------------------------------- + // 20. TestJetStreamClusterStreamSynchedTimeStamps + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamSynchedTimeStamps_ShouldMaintainTimestampAfterLeaderChange() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish to R=3 memory stream, record timestamp, kill stream leader, + // fetch msg from new leader, verify timestamps match. + } + + // ----------------------------------------------------------------------- + // 21. TestJetStreamClusterRestoreSingleConsumer + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterRestoreSingleConsumer_ShouldRestoreAfterFullClusterRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream, publish, subscribe durable, ack, stop all, restart all, + // verify stream and consumer are restored. + } + + // ----------------------------------------------------------------------- + // 22. TestJetStreamClusterMaxBytesForStream + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMaxBytesForStream_ShouldEnforcePerServerStorageLimit() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=2 stream with MaxBytes=2GB (ok), then try 4GB (should fail: no suitable peers). + } + + // ----------------------------------------------------------------------- + // 23. TestJetStreamClusterStreamPublishWithActiveConsumers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamPublishWithActiveConsumers_ShouldDeliverInOrderAfterLeaderChange() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, subscribe durable, publish 10 in sequence, verify order, + // kill consumer leader, publish 10 more, verify order continues. + } + + // ----------------------------------------------------------------------- + // 24. TestJetStreamClusterStreamOverlapSubjects + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamOverlapSubjects_ShouldPreventOverlappingSubjects() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3"); + c.WaitOnLeader(); + // Create TEST on "foo", try to create TEST2 on "foo" — should fail. + // Verify only 1 stream in list. + } + + // ----------------------------------------------------------------------- + // 25. TestJetStreamClusterStreamInfoList + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamInfoList_ShouldReturnCorrectMsgCountsForAllStreams() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create foo(10), bar(22), baz(33), verify StreamsInfo returns correct counts. + } + + // ----------------------------------------------------------------------- + // 26. TestJetStreamClusterConsumerInfoList + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerInfoList_ShouldReturnCorrectConsumerStates() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, publish 10, create 3 pull consumers with different + // fetch/ack combos, verify ConsumersInfo returns correct delivered/ackfloor. + } + + // ----------------------------------------------------------------------- + // 27. TestJetStreamClusterStreamUpdate + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamUpdate_ShouldUpdateMaxMsgsSuccessfully() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream MaxMsgs=10, fill it, expect publish failure. + // Update MaxMsgs=20 from non-leader, verify success. + // Attempt bad update (name mismatch), verify only 1 response. + } + + // ----------------------------------------------------------------------- + // 28. TestJetStreamClusterStreamExtendedUpdates + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamExtendedUpdates_ShouldAllowSubjectUpdateButNotMirrorChange() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Subjects can be updated. Mirror changes should return JSStreamMirrorNotUpdatableError. + } + + // ----------------------------------------------------------------------- + // 29. TestJetStreamClusterDoubleAdd + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterDoubleAdd_ShouldBeIdempotentForStreamsAndConsumers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(2, "R32"); + c.WaitOnLeader(); + // Add stream twice — should not error. Add consumer twice — should not error. + } + + // ----------------------------------------------------------------------- + // 30. TestJetStreamClusterDefaultMaxAckPending + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterDefaultMaxAckPending_ShouldSetDefaultAckPendingOnConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(2, "R32"); + c.WaitOnLeader(); + // Create consumer with default config, verify MaxAckPending == JsDefaultMaxAckPending (20000). + } + + // ----------------------------------------------------------------------- + // 31. TestJetStreamClusterStreamNormalCatchup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamNormalCatchup_ShouldCatchupAfterRejoiningCluster() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 10 msgs, kill stream leader, publish 11 more, delete one, + // restart old leader, wait for cluster formed + current. + } + + // ----------------------------------------------------------------------- + // 32. TestJetStreamClusterStreamSnapshotCatchup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamSnapshotCatchup_ShouldCatchupViaSnapshotAfterRejoining() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 2, kill stream leader, publish 100, delete 2 msgs, snapshot, + // send more, restart old leader, wait for current, verify states match. + } + + // ----------------------------------------------------------------------- + // 33. TestJetStreamClusterDeleteMsg + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterDeleteMsg_ShouldDeleteMessageAndSupportPurge() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=1 stream, publish 10, delete seq 1, purge stream — all should succeed. + } + + // ----------------------------------------------------------------------- + // 34. TestJetStreamClusterDeleteMsgAndRestart + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterDeleteMsgAndRestart_ShouldSurviveFullRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=2 stream, publish 10, delete seq 1, stop all, restart all, + // wait for stream leader. + } + + // ----------------------------------------------------------------------- + // 35. TestJetStreamClusterStreamSnapshotCatchupWithPurge + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamSnapshotCatchupWithPurge_ShouldHandlePurgeDuringCatchup() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Kill stream leader, publish 10, snapshot, restart old leader, + // purge while recovering, wait for current, verify stream info available. + } + + // ----------------------------------------------------------------------- + // 36. TestJetStreamClusterExtendedStreamInfo + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterExtendedStreamInfo_ShouldIncludeClusterInfoAndReplicas() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, publish 50, verify StreamInfo contains cluster info, + // cluster name, leader, and 2 replicas. Replicas must be ordered. + // Kill leader, verify info still correct, restart, verify current. + } + + // ----------------------------------------------------------------------- + // 37. TestJetStreamClusterExtendedStreamInfoSingleReplica + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterExtendedStreamInfoSingleReplica_ShouldShowNoReplicasForR1Stream() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=1 stream, verify cluster info shows 0 replicas. + // Verify ConsumersInfo returns 0 initially, 1 after adding consumer. + } + + // ----------------------------------------------------------------------- + // 38. TestJetStreamClusterInterestRetention + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterInterestRetention_ShouldDeleteMsgsAfterAckWithInterestPolicy() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create Interest-retention R=3 stream, subscribe durable, publish 1, ack, + // verify stream goes to 0. Publish 50 more, delete consumer, verify stream goes to 0. + } + + // ----------------------------------------------------------------------- + // 39. TestJetStreamClusterWorkQueueRetention + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterWorkQueueRetention_ShouldRemoveMsgsAfterAckInWorkQueueMode() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create WorkQueue R=2 stream, publish 1, pull and ack, verify stream goes to 0. + } + + // ----------------------------------------------------------------------- + // 40. TestJetStreamClusterMirrorAndSourceWorkQueues + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMirrorAndSourceWorkQueues_ShouldMirrorWorkQueueMessages() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "WQ"); + c.WaitOnLeader(); + // Create WQ22 WorkQueue, M mirror, S source. Publish 1 to WQ22. + // Verify WQ22=0, M=1, S=1 (because mirror/source consume from work queue). + } + + // ----------------------------------------------------------------------- + // 41. TestJetStreamClusterMirrorAndSourceInterestPolicyStream + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMirrorAndSourceInterestPolicyStream_ShouldHandleInterestPolicyWithMirrorAndSource() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "WQ"); + c.WaitOnLeader(); + // Create IP22 Interest stream, mirror M, source S. + // Without other interest: IP22=0, M=1, S=1. + // After adding subscriber: IP22=1, M=2, S=2. + } + + // ----------------------------------------------------------------------- + // 42. TestJetStreamClusterInterestRetentionWithFilteredConsumers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterInterestRetentionWithFilteredConsumers_ShouldTrackPerFilteredConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create Interest stream on "*", two filtered consumers (foo, bar). + // Verify messages are retained until all consumers ack. + // Delete consumers, verify stream goes to 0. + // Test same with pull consumer. + } + + // ----------------------------------------------------------------------- + // 43. TestJetStreamClusterEphemeralConsumerNoImmediateInterest + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterEphemeralConsumerNoImmediateInterest_ShouldCleanUpWithoutActiveSubscriber() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create ephemeral consumer with deliver subject "r", set inactive threshold to 500ms, + // verify consumer disappears within 5s. + } + + // ----------------------------------------------------------------------- + // 44. TestJetStreamClusterEphemeralConsumerCleanup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterEphemeralConsumerCleanup_ShouldRemoveConsumerOnUnsubscribe() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=2 stream, subscribe (ephemeral), set inactive threshold to 10ms, + // verify 1 consumer. Unsubscribe, verify consumer removed within 2s. + } + + // ----------------------------------------------------------------------- + // 45. TestJetStreamClusterEphemeralConsumersNotReplicated + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterEphemeralConsumersNotReplicated_ShouldBeR1Only() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, ephemeral subscribe, verify consumer cluster has 0 replicas (R=1). + // Shut consumer server, verify optimistic delivery may fail (logged, not fatal). + } + + // ----------------------------------------------------------------------- + // 46. TestJetStreamClusterUserSnapshotAndRestore + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterUserSnapshotAndRestore_ShouldRestoreStreamWithConsumerState() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=2 stream, publish 200, create 2 consumers with partial ack state, + // snapshot, delete stream, restore, verify message count and consumer state. + } + + // ----------------------------------------------------------------------- + // 47. TestJetStreamClusterUserSnapshotAndRestoreConfigChanges + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterUserSnapshotAndRestoreConfigChanges_ShouldAllowConfigChangesOnRestore() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Snapshot R=2 stream, delete it, restore with different subjects/storage/replicas. + } + + // ----------------------------------------------------------------------- + // 48. TestJetStreamClusterAccountInfoAndLimits + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterAccountInfoAndLimits_ShouldEnforceStreamAndConsumerLimits() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Set limits: 1024 mem, 8000 store, 3 streams, 2 consumers. + // Create 3 streams, verify 4th fails. Verify store enforcement. + // Create 2 consumers (with idempotent create), verify 3rd fails. + } + + // ----------------------------------------------------------------------- + // 49. TestJetStreamClusterMaxStreamsReached + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMaxStreamsReached_ShouldAllowIdempotentCreateUnderLimit() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // MaxStreams=1: 15 parallel creates of same stream — all succeed (idempotent). + // MaxStreams=2, 2 existing streams: 15 parallel creates alternating — all succeed. + } + + // ----------------------------------------------------------------------- + // 50. TestJetStreamClusterStreamLimits + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamLimits_ShouldEnforceMaxMsgSizeAndMaxMsgsAndMaxAge() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // R=5 fails on 3-node. R=3 stream with MaxMsgSize=11, MaxMsgs=5, MaxAge=250ms, DiscardNew. + // Large msg fails, 5 msgs ok, 6th fails. After age expires, msgs=0, publish succeeds. + } + + // ----------------------------------------------------------------------- + // 51. TestJetStreamClusterStreamInterestOnlyPolicy + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamInterestOnlyPolicy_ShouldNotRetainMsgsWithoutInterest() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 10 without consumer → stream stays at 0. + // Add consumer, publish 10 → stream has 10. Delete consumer → stream goes to 0. + } + + // ----------------------------------------------------------------------- + // 52. TestJetStreamClusterExtendedAccountInfo + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterExtendedAccountInfo_ShouldTrackStreamsConsumersAndApiErrors() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create 3 streams with consumers, verify AccountInfo shows 3 streams, 3 consumers, >=7 API calls. + // Make 4 bad API calls, verify Errors==4. + } + + // ----------------------------------------------------------------------- + // 53. TestJetStreamClusterPeerRemovalAPI + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerRemovalApi_ShouldRemovePeerViaApi() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Unknown peer removal → error. Valid peer removal → success + advisory published. + // Verify peer removed from cluster peers within 5s. + } + + // ----------------------------------------------------------------------- + // 54. TestJetStreamClusterPeerRemovalAndStreamReassignment + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerRemovalAndStreamReassignment_ShouldReassignStreamAfterPeerRemoval() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Create R=3 stream, remove one non-leader stream peer via API, + // verify stream still has 2 current replicas (none is the removed server). + } + + // ----------------------------------------------------------------------- + // 55. TestJetStreamClusterPeerRemovalAndStreamReassignmentWithoutSpace + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerRemovalAndStreamReassignmentWithoutSpace_ShouldHandleInsufficientPeers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // R=3 stream in 3-node cluster, remove one peer — cluster goes to 2 nodes. + // Stream should scale down to R=2 (no space for R=3). + } + + // ----------------------------------------------------------------------- + // 56. TestJetStreamClusterPeerRemovalAndServerBroughtBack + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerRemovalAndServerBroughtBack_ShouldHandleServerReintroduction() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Remove server from cluster, bring it back, verify peer count restored to 3. + } + + // ----------------------------------------------------------------------- + // 57. TestJetStreamClusterPeerExclusionTag + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerExclusionTag_ShouldExcludeTaggedPeersFromPlacement() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Stream with placement excluding a tag should not place on tagged server. + } + + // ----------------------------------------------------------------------- + // 58. TestJetStreamClusterAccountPurge + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterAccountPurge_ShouldDeleteAllStreamsAndConsumersForAccount() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create several streams with consumers, purge account via $JS.API.ACCOUNT.PURGE, + // verify all streams/consumers removed. + } + + // ----------------------------------------------------------------------- + // 59. TestJetStreamClusterScaleConsumer + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterScaleConsumer_ShouldScaleConsumerReplicasUpAndDown() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "C"); + c.WaitOnLeader(); + // Create R=3 stream + durable consumer, publish 1000 msgs, + // scale consumer down to 1, up to 3, down to 1, up to 0 (inherit from stream), + // consuming one msg between each scale, verify state consistency throughout. + } + + // ----------------------------------------------------------------------- + // 60. TestJetStreamClusterConsumerScaleUp + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerScaleUp_ShouldMaintainConsumerLeadershipAfterStreamScaleUp() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "HUB"); + c.WaitOnLeader(); + // Create R=1 stream + durable R=0 consumer, publish 100 msgs, + // scale stream to R=2, wait 2s, verify consumer leader still present. + } + + // ----------------------------------------------------------------------- + // 61. TestJetStreamClusterPeerOffline + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerOffline_ShouldMarkServerOfflineAndOnlineCorrectly() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Shut a non-leader, verify it shows as offline. Restart it, verify online again. + } + + // ----------------------------------------------------------------------- + // 62. TestJetStreamClusterNoQuorumStepdown + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterNoQuorumStepdown_ShouldStepDownLeaderWhenQuorumLost() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Shut 2 of 3 servers, verify meta leader steps down (no quorum). + } + + // ----------------------------------------------------------------------- + // 63. TestJetStreamClusterCreateResponseAdvisoriesHaveSubject + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterCreateResponseAdvisoriesHaveSubject_ShouldIncludeSubjectInAdvisories() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Subscribe to $JS.EVENT.ADVISORY.API.>, create stream, verify advisory has subject field set. + } + + // ----------------------------------------------------------------------- + // 64. TestJetStreamClusterRestartAndRemoveAdvisories + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterRestartAndRemoveAdvisories_ShouldNotSendAdvisoriesForRemovedOnRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create/delete streams, restart cluster, verify no spurious create/delete advisories on restart. + } + + // ----------------------------------------------------------------------- + // 65. TestJetStreamClusterNoDuplicateOnNodeRestart + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterNoDuplicateOnNodeRestart_ShouldNotDeliverDuplicateMessagesOnRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Subscribe sync, publish, receive, restart node, verify no duplicate delivery. + } + + // ----------------------------------------------------------------------- + // 66. TestJetStreamClusterNoDupePeerSelection + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterNoDupePeerSelection_ShouldNotSelectSamePeerTwiceForConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream, create R=3 consumer, verify consumer cluster has distinct peers. + } + + // ----------------------------------------------------------------------- + // 67. TestJetStreamClusterStreamRemovePeer + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamRemovePeer_ShouldReassignStreamAfterPeerRemoval() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Remove stream peer via $JS.API.STREAM.PEER.REMOVE, verify stream reassigned to new peer. + } + + // ----------------------------------------------------------------------- + // 68. TestJetStreamClusterStreamLeaderStepDown + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamLeaderStepDown_ShouldElectNewStreamLeaderAfterStepDown() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Step down stream leader, verify new leader elected and stream still accessible. + } + + // ----------------------------------------------------------------------- + // 69. TestJetStreamClusterRemoveServer + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterRemoveServer_ShouldRebalanceStreamsAfterServerRemoval() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Remove server, verify streams are rebalanced, no stale peer references remain. + } + + // ----------------------------------------------------------------------- + // 70. TestJetStreamClusterPurgeReplayAfterRestart + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPurgeReplayAfterRestart_ShouldReplayPurgeAfterRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 10, purge, restart cluster, verify stream is still empty. + } + + // ----------------------------------------------------------------------- + // 71. TestJetStreamClusterStreamGetMsg + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamGetMsg_ShouldGetMessageBySequenceFromCluster() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish a msg, get it via $JS.API.STREAM.MSG.GET, verify data matches. + } + + // ----------------------------------------------------------------------- + // 72. TestJetStreamClusterStreamDirectGetMsg + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamDirectGetMsg_ShouldSupportDirectGetFromReplica() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create R=3 stream with AllowDirect=true, publish, direct-get from replica. + } + + // ----------------------------------------------------------------------- + // 73. TestJetStreamClusterStreamPerf + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamPerf_ShouldPublishAndReceiveAllMessagesWithinTimeout() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 5000 msgs to R=3 stream, verify all received by pull consumer. + } + + // ----------------------------------------------------------------------- + // 74. TestJetStreamClusterConsumerPerf + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerPerf_ShouldDeliverAllMessagesToPushConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 5000 msgs to R=3 stream, push-consume all, verify count. + } + + // ----------------------------------------------------------------------- + // 75. TestJetStreamClusterQueueSubConsumer + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterQueueSubConsumer_ShouldDeliverExactlyOnceAcrossQueueGroup() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream, subscribe with queue group, publish 100 msgs, + // verify each msg delivered exactly once across all queue members. + } + + // ----------------------------------------------------------------------- + // 76. TestJetStreamClusterLeaderStepdown + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterLeaderStepdown_ShouldElectNewMetaLeaderAfterStepDown() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Request meta leader stepdown, verify new leader elected. + } + + // ----------------------------------------------------------------------- + // 77. TestJetStreamClusterSourcesFilteringAndUpdating + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterSourcesFilteringAndUpdating_ShouldFilterSourcesBySubjectAndSupportUpdate() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create source stream, create stream with filtered source, verify filtering, + // update source filter, verify updated behavior. + } + + // ----------------------------------------------------------------------- + // 78. TestJetStreamClusterSourcesUpdateOriginError + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterSourcesUpdateOriginError_ShouldReportErrorWhenSourceOriginChanges() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream with source, update to change source origin — should error. + } + + // ----------------------------------------------------------------------- + // 79. TestJetStreamClusterMirrorAndSourcesClusterRestart + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMirrorAndSourcesClusterRestart_ShouldContinueAfterRestart() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create mirror and source streams, publish, restart cluster, verify counts preserved. + } + + // ----------------------------------------------------------------------- + // 80. TestJetStreamClusterMirrorAndSourcesFilteredConsumers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMirrorAndSourcesFilteredConsumers_ShouldWorkWithFilteredConsumers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create mirror with filtered subjects, consume from mirror with consumer filter. + } + + // ----------------------------------------------------------------------- + // 81. TestJetStreamClusterCrossAccountMirrorsAndSources + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterCrossAccountMirrorsAndSources_ShouldMirrorAcrossAccounts() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamClusterWithTemplate(ConfigHelper.JsClusterAccountsTemplate, 3, "R3S"); + c.WaitOnLeader(); + // Create stream in account ONE, mirror in account TWO, verify mirror receives msgs. + } + + // ----------------------------------------------------------------------- + // 82. TestJetStreamClusterFailMirrorsAndSources + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterFailMirrorsAndSources_ShouldFailGracefullyOnInvalidMirrorOrSource() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Attempt to create mirror/source on non-existent stream — should get error response. + } + + // ----------------------------------------------------------------------- + // 83. TestJetStreamClusterConsumerDeliveredSyncReporting + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerDeliveredSyncReporting_ShouldReportDeliveredSequenceAccurately() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Push consumer, publish msgs, verify Delivered.Stream and Delivered.Consumer in sync. + } + + // ----------------------------------------------------------------------- + // 84. TestJetStreamClusterConsumerAckSyncReporting + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerAckSyncReporting_ShouldReportAckFloorAccurately() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Pull consumer, fetch and ack, verify AckFloor.Stream and AckFloor.Consumer in sync. + } + + // ----------------------------------------------------------------------- + // 85. TestJetStreamClusterConsumerDeleteInterestPolicyMultipleConsumers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerDeleteInterestPolicyMultipleConsumers_ShouldNotPurgeMsgsWithOtherActiveConsumers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Interest stream, 2 consumers. Delete one — msgs should remain until other acks too. + } + + // ----------------------------------------------------------------------- + // 86. TestJetStreamClusterConsumerAckNoneInterestPolicyShouldNotRetainAfterDelivery + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerAckNoneInterestPolicyShouldNotRetainAfterDelivery_ShouldRemoveMsgsOnDelivery() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // AckNone consumer on Interest stream: msgs removed on delivery without explicit ack. + } + + // ----------------------------------------------------------------------- + // 87. TestJetStreamClusterConsumerDeleteAckNoneInterestPolicyWithOthers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerDeleteAckNoneInterestPolicyWithOthers_ShouldHandleDeleteWithMultipleConsumers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // AckNone + AckExplicit consumers on Interest stream, delete AckNone consumer, + // verify explicit consumer still sees msgs. + } + + // ----------------------------------------------------------------------- + // 88. TestJetStreamClusterMetaStepdownFromNonSysAccount + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaStepdownFromNonSysAccount_ShouldFailWithPermissionError() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Non-system account attempting meta stepdown should get an error. + } + + // ----------------------------------------------------------------------- + // 89. TestJetStreamClusterMaxDeliveriesOnInterestStreams + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMaxDeliveriesOnInterestStreams_ShouldRespectMaxDeliveriesSetting() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Interest stream, consumer MaxDelivers=3, verify msg removed after 3 delivery attempts. + } + + // ----------------------------------------------------------------------- + // 90. TestJetStreamClusterMetaRecoveryUpdatesDeletesConsumers + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaRecoveryUpdatesDeletesConsumers_ShouldRecoverUpdatedAndDeletedConsumers() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create, update, delete consumer. Restart. Verify correct final state recovered. + } + + // ----------------------------------------------------------------------- + // 91. TestJetStreamClusterMetaRecoveryRecreateFileStreamAsMemory + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaRecoveryRecreateFileStreamAsMemory_ShouldRecoverStreamWithChangedStorageType() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create File stream, delete it, recreate as Memory, restart, verify Memory type recovered. + } + + // ----------------------------------------------------------------------- + // 92. TestJetStreamClusterMetaRecoveryConsumerCreateAndRemove + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaRecoveryConsumerCreateAndRemove_ShouldRecoverAfterConsumerCreateAndDelete() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create consumer, delete it, restart, verify consumer is not present. + } + + // ----------------------------------------------------------------------- + // 93. TestJetStreamClusterMetaRecoveryAddAndUpdateStream + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterMetaRecoveryAddAndUpdateStream_ShouldRecoverUpdatedStreamConfig() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create stream, update MaxMsgs, restart, verify updated config recovered. + } + + // ----------------------------------------------------------------------- + // 94. TestJetStreamClusterConsumerAckOutOfBounds + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerAckOutOfBounds_ShouldHandleOutOfBoundsAckGracefully() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Ack a sequence beyond delivered range — server should not crash, consumer stays healthy. + } + + // ----------------------------------------------------------------------- + // 95. TestJetStreamClusterCatchupLoadNextMsgTooManyDeletes + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterCatchupLoadNextMsgTooManyDeletes_ShouldCatchupWithHighDensityDeletes() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish 1000 msgs, delete 999 of them, kill stream leader, restart, verify catchup. + } + + // ----------------------------------------------------------------------- + // 96. TestJetStreamClusterCatchupMustStallWhenBehindOnApplies + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterCatchupMustStallWhenBehindOnApplies_ShouldNotOverloadCatchupQueue() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Kill stream replica, flood with messages, restart replica, verify catchup without queue overflow. + } + + // ----------------------------------------------------------------------- + // 97. TestJetStreamClusterConsumerInfoAfterCreate + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerInfoAfterCreate_ShouldReturnConsumerInfoImmediatelyAfterCreate() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create consumer, immediately request ConsumerInfo, verify info is available. + } + + // ----------------------------------------------------------------------- + // 98. TestJetStreamClusterStreamUpscalePeersAfterDownscale + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamUpscalePeersAfterDownscale_ShouldRestoreAllPeersOnUpscale() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Scale stream from R=3 to R=1, then back to R=3, verify 3 distinct current peers. + } + + // ----------------------------------------------------------------------- + // 99. TestJetStreamClusterClearAllPreAcksOnRemoveMsg + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterClearAllPreAcksOnRemoveMsg_ShouldClearPreAcksWhenMessageRemoved() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Interest stream, pre-ack msg, then delete it via DeleteMsg, verify pre-ack state cleared. + } + + // ----------------------------------------------------------------------- + // 100. TestJetStreamClusterStreamHealthCheckMustNotRecreate + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamHealthCheckMustNotRecreate_ShouldNotRecreateExistingStream() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Health check on existing stream must not trigger recreation or data loss. + } + + // ----------------------------------------------------------------------- + // 101. TestJetStreamClusterStreamHealthCheckMustNotDeleteEarly + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamHealthCheckMustNotDeleteEarly_ShouldNotDeleteStreamDuringHealthCheck() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Health check on stream with messages must not trigger premature deletion. + } + + // ----------------------------------------------------------------------- + // 102. TestJetStreamClusterStreamHealthCheckOnlyReportsSkew + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamHealthCheckOnlyReportsSkew_ShouldOnlyReportSkewNotForceRecovery() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Force skew in stream replica, health check should report skew, not force recreation. + } + + // ----------------------------------------------------------------------- + // 103. TestJetStreamClusterStreamHealthCheckStreamCatchup + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStreamHealthCheckStreamCatchup_ShouldTriggerCatchupOnHealthCheckFailure() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Simulate replica behind, health check should trigger catchup. + } + + // ----------------------------------------------------------------------- + // 104. TestJetStreamClusterConsumerHealthCheckMustNotRecreate + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerHealthCheckMustNotRecreate_ShouldNotRecreateExistingConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Health check on existing consumer must not trigger recreation. + } + + // ----------------------------------------------------------------------- + // 105. TestJetStreamClusterConsumerHealthCheckMustNotDeleteEarly + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerHealthCheckMustNotDeleteEarly_ShouldNotDeleteActiveConsumer() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Active consumer must not be prematurely deleted by health check. + } + + // ----------------------------------------------------------------------- + // 106. TestJetStreamClusterConsumerHealthCheckOnlyReportsSkew + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerHealthCheckOnlyReportsSkew_ShouldNotForceRecreateOnSkew() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Skewed consumer state should be reported, not cause forced recreation. + } + + // ----------------------------------------------------------------------- + // 107. TestJetStreamClusterConsumerHealthCheckDeleted + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerHealthCheckDeleted_ShouldCleanUpDeletedConsumerOnHealthCheck() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Deleted consumer should be cleaned up gracefully during health check. + } + + // ----------------------------------------------------------------------- + // 108. TestJetStreamClusterRespectConsumerStartSeq + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterRespectConsumerStartSeq_ShouldStartDeliveryFromConfiguredSequence() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create consumer with DeliverByStartSequence=5, verify first delivered msg is seq 5. + } + + // ----------------------------------------------------------------------- + // 109. TestJetStreamClusterPeerRemoveStreamConsumerDesync + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPeerRemoveStreamConsumerDesync_ShouldNotDesyncConsumerAfterPeerRemoval() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(5, "R5S"); + c.WaitOnLeader(); + // Remove a peer from stream, verify consumer state remains in sync with stream. + } + + // ----------------------------------------------------------------------- + // 110. TestJetStreamClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterStuckConsumerAfterLeaderChangeWithUnknownDeliveries_ShouldRecoverFromStuckState() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Consumer with in-flight msgs, leader change — consumer should recover without getting stuck. + } + + // ----------------------------------------------------------------------- + // 111. TestJetStreamClusterAccountStatsForReplicatedStreams + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterAccountStatsForReplicatedStreams_ShouldCountStorageOnceNotPerReplica() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Publish to R=3 stream, verify account stats count storage once (logical), not 3x. + } + + // ----------------------------------------------------------------------- + // 112. TestJetStreamClusterRecreateConsumerFromMetaSnapshot + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterRecreateConsumerFromMetaSnapshot_ShouldRecreateConsumerFromSnapshot() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Create consumer, snapshot meta, shut server, restore from snapshot, verify consumer exists. + } + + // ----------------------------------------------------------------------- + // 113. TestJetStreamClusterUpgradeStreamVersioning + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterUpgradeStreamVersioning_ShouldHandleStreamVersionUpgrade() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Simulate stream version upgrade scenario, verify stream accessible after upgrade. + } + + // ----------------------------------------------------------------------- + // 114. TestJetStreamClusterUpgradeConsumerVersioning + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterUpgradeConsumerVersioning_ShouldHandleConsumerVersionUpgrade() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Simulate consumer version upgrade, verify consumer accessible after upgrade. + } + + // ----------------------------------------------------------------------- + // 115. TestJetStreamClusterInterestPolicyAckAll + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterInterestPolicyAckAll_ShouldRemoveMsgOnlyAfterAllConsumersAckAll() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Interest stream, AckAll consumer: msg removed only after all consumers AckAll. + } + + // ----------------------------------------------------------------------- + // 116. TestJetStreamClusterPreserveRedeliveredWithLaggingStream + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterPreserveRedeliveredWithLaggingStream_ShouldPreserveRedeliveredFlagDuringLag() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Consumer with lagging stream: redelivered flag must be preserved across leader changes. + } + + // ----------------------------------------------------------------------- + // 117. TestJetStreamClusterInvalidJSACKOverRoute + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterInvalidJsAckOverRoute_ShouldHandleInvalidAckGracefully() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // Invalid JSACK sent over a route should not crash the server. + } + + // ----------------------------------------------------------------------- + // 118. TestJetStreamClusterConsumerOnlyDeliverMsgAfterQuorum + // ----------------------------------------------------------------------- + [SkippableFact] + public void ClusterConsumerOnlyDeliverMsgAfterQuorum_ShouldNotDeliverBeforeQuorumAchieved() + { + Skip.If(ShouldSkip(), "Cluster integration tests are not enabled."); + + using var c = TestCluster.CreateJetStreamCluster(3, "R3S"); + c.WaitOnLeader(); + // R=3 consumer must not deliver msg until quorum (2 of 3) of replicas have acknowledged the entry. + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ZB.MOM.NatsNet.Server.IntegrationTests.csproj b/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ZB.MOM.NatsNet.Server.IntegrationTests.csproj index e48edff..2777913 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ZB.MOM.NatsNet.Server.IntegrationTests.csproj +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ZB.MOM.NatsNet.Server.IntegrationTests.csproj @@ -17,7 +17,9 @@ + + @@ -25,6 +27,8 @@ + + diff --git a/reports/current.md b/reports/current.md index a163a75..dab6885 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-03-01 16:08:14 UTC +Generated: 2026-03-01 17:16:29 UTC ## Modules (12 total)