test(batch57): port 53 SuperCluster and LeafNode integration tests
Ports 36 JetStream super-cluster tests from jetstream_super_cluster_test.go, 3 JetStream leaf-node tests from jetstream_leafnode_test.go, and 14 leaf-node tests from leafnode_test.go into the integration test project. Creates the required harness infrastructure (TestSuperCluster, TestCluster, IntegrationTestBase, CheckHelper, ConfigHelper, NatsTestClient, TestServerHelper). All 53 tests are marked [Fact(Skip = "...")] pending full multi-server cluster runtime.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Polling / assertion helpers ported from the Go test framework's checkFor and
|
||||
/// related utilities.
|
||||
/// </summary>
|
||||
public static class CheckHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Polls <paramref name="check"/> every <paramref name="interval"/> until it
|
||||
/// returns null (success) or <paramref name="timeout"/> expires.
|
||||
/// </summary>
|
||||
public static void CheckFor(
|
||||
TimeSpan timeout,
|
||||
TimeSpan interval,
|
||||
Func<string?> check)
|
||||
{
|
||||
var deadline = DateTime.UtcNow + timeout;
|
||||
string? last = null;
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
last = check();
|
||||
if (last is null)
|
||||
return;
|
||||
Thread.Sleep(interval);
|
||||
}
|
||||
throw new Xunit.Sdk.XunitException($"CheckFor timed out after {timeout}: {last}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the supplied server reports exactly <paramref name="expected"/>
|
||||
/// leaf-node connections.
|
||||
/// </summary>
|
||||
public static void CheckLeafNodeConnectedCount(object server, int expected) =>
|
||||
throw new NotImplementedException("Requires a running server instance.");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helpers for generating and writing NATS server configuration files used by
|
||||
/// integration tests.
|
||||
/// </summary>
|
||||
public static class ConfigHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Template for a basic JetStream super-cluster node.
|
||||
/// Placeholders: {ServerName}, {ClusterName}, {StoreDir}, {ClusterPort}, {Routes}
|
||||
/// </summary>
|
||||
public const string JsSuperClusterTemplate = """
|
||||
listen: 127.0.0.1:-1
|
||||
server_name: {ServerName}
|
||||
jetstream: {max_mem_store: 256MB, max_file_store: 2GB, store_dir: '{StoreDir}'}
|
||||
cluster {
|
||||
name: {ClusterName}
|
||||
listen: 127.0.0.1:{ClusterPort}
|
||||
routes = [{Routes}]
|
||||
}
|
||||
accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// Writes <paramref name="content"/> to a temporary file and returns its path.
|
||||
/// The file is deleted when the process exits (via <see cref="Path.GetTempFileName"/>).
|
||||
/// </summary>
|
||||
public static string CreateConfigFile(string content)
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"natsnet_{Guid.NewGuid():N}.conf");
|
||||
File.WriteAllText(path, content);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for integration tests that require a running .NET NatsServer.
|
||||
/// Tests that depend on multi-server cluster infrastructure skip automatically
|
||||
/// until the full server runtime is available.
|
||||
/// </summary>
|
||||
public abstract class IntegrationTestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true when the full server runtime required by cluster / super-cluster
|
||||
/// tests is not yet available.
|
||||
/// </summary>
|
||||
protected static bool ServerRuntimeUnavailable => true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
using NATS.Client.Core;
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helpers for connecting NATS test clients to running servers.
|
||||
/// </summary>
|
||||
public static class NatsTestClient
|
||||
{
|
||||
/// <summary>Connects to the given NATS URL.</summary>
|
||||
public static async Task<NatsConnection> Connect(string url)
|
||||
{
|
||||
var conn = new NatsConnection(new NatsOpts { Url = url });
|
||||
await conn.ConnectAsync();
|
||||
return conn;
|
||||
}
|
||||
|
||||
/// <summary>Connects to the client URL of the supplied server handle.</summary>
|
||||
public static Task<NatsConnection> ConnectToServer(object server) =>
|
||||
throw new NotImplementedException("Requires a running server instance.");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder harness for a single JetStream cluster (multiple servers forming a
|
||||
/// Raft group). The real implementation requires the full NatsServer runtime.
|
||||
/// </summary>
|
||||
public sealed class TestCluster : IDisposable
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public TestCluster(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JetStream cluster. Not yet implemented.
|
||||
/// </summary>
|
||||
public static TestCluster CreateJetStreamCluster(int numServers, string name) =>
|
||||
new(name);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// TODO: shut down all in-process servers when runtime is available.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helpers for starting individual NATS server instances in integration tests.
|
||||
/// </summary>
|
||||
public static class TestServerHelper
|
||||
{
|
||||
/// <summary>Runs a server with programmatically supplied options.</summary>
|
||||
public static IDisposable RunServer(object opts) =>
|
||||
throw new NotImplementedException("Requires full NatsServer runtime.");
|
||||
|
||||
/// <summary>Runs a server from a config file on disk.</summary>
|
||||
public static IDisposable RunServerWithConfig(string configFilePath) =>
|
||||
throw new NotImplementedException("Requires full NatsServer runtime.");
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
namespace ZB.MOM.NatsNet.Server.IntegrationTests.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder harness for a JetStream super-cluster (multiple clusters connected
|
||||
/// by gateways). The real implementation requires the full NatsServer runtime to
|
||||
/// be capable of spawning in-process cluster nodes.
|
||||
/// </summary>
|
||||
public sealed class TestSuperCluster : IDisposable
|
||||
{
|
||||
private readonly List<TestCluster> _clusters;
|
||||
|
||||
private TestSuperCluster(List<TestCluster> clusters)
|
||||
{
|
||||
_clusters = clusters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a super-cluster with <paramref name="numClusters"/> clusters, each
|
||||
/// containing <paramref name="numPerCluster"/> servers.
|
||||
/// Not yet implemented — tests that call this will be skipped.
|
||||
/// </summary>
|
||||
public static TestSuperCluster CreateJetStreamSuperCluster(int numPerCluster, int numClusters) =>
|
||||
new([]);
|
||||
|
||||
/// <summary>Returns the current JetStream meta-leader across all clusters.</summary>
|
||||
public object? Leader() => null;
|
||||
|
||||
/// <summary>Returns a random server from any cluster in the super-cluster.</summary>
|
||||
public object? RandomServer() => null;
|
||||
|
||||
/// <summary>Waits until a meta-leader is elected.</summary>
|
||||
public void WaitOnLeader() { /* placeholder */ }
|
||||
|
||||
/// <summary>Waits until a stream leader is elected in the given account.</summary>
|
||||
public void WaitOnStreamLeader(string account, string stream) { /* placeholder */ }
|
||||
|
||||
/// <summary>Returns the named cluster.</summary>
|
||||
public TestCluster ClusterForName(string name) =>
|
||||
_clusters.Single(c => c.Name == name);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var c in _clusters)
|
||||
c.Dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user