From 5e093bea32121fa93f99d76fd0c1a2c2862ef078 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 08:13:45 -0500 Subject: [PATCH] test(batch4-task6): port concurrency and websocket logging-mapped tests --- .../ConcurrencyTests1.Batch4Logging.cs | 74 ++++++++++++++ .../ImplBacklog/ConcurrencyTests1.cs | 2 +- .../ConcurrencyTests2.Batch4Logging.cs | 66 +++++++++++++ .../ImplBacklog/ConcurrencyTests2.cs | 2 +- .../WebSocketHandlerTests.Batch4Logging.cs | 91 ++++++++++++++++++ .../ImplBacklog/WebSocketHandlerTests.cs | 2 +- porting.db | Bin 6373376 -> 6373376 bytes 7 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Batch4Logging.cs create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Batch4Logging.cs create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Batch4Logging.cs diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Batch4Logging.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Batch4Logging.cs new file mode 100644 index 0000000..609214c --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Batch4Logging.cs @@ -0,0 +1,74 @@ +using Shouldly; +using ZB.MOM.NatsNet.Server; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; + +public sealed partial class ConcurrencyTests1 +{ + [Fact] // T:2469 + public async Task NoRaceRoutePoolAndPerAccountConfigReload_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions()); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + var logger = new ConcurrencyCaptureLogger(); + server!.SetLogger(logger, false, false); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(1)); + var publishTask = Task.Run(async () => + { + while (!cts.Token.IsCancellationRequested) + { + server.RateLimitWarnf("route pool update for account {0}", "A"); + await Task.Delay(1, cts.Token); + } + }, cts.Token); + + var reloadTask = Task.Run(async () => + { + while (!cts.Token.IsCancellationRequested) + { + var opts = server.GetOpts(); + opts.Cluster.PoolSize = opts.Cluster.PoolSize == 2 ? 3 : 2; + opts.Cluster.PinnedAccounts = opts.Cluster.PoolSize == 2 ? ["A"] : ["A", "B"]; + server.SetOpts(opts); + await Task.Delay(1, cts.Token); + } + }, cts.Token); + + await Task.WhenAll( + publishTask.ContinueWith(_ => { }, TaskScheduler.Default), + reloadTask.ContinueWith(_ => { }, TaskScheduler.Default)); + + server.GetOpts().Cluster.PoolSize.ShouldBeOneOf(2, 3); + } + + private sealed class ConcurrencyCaptureLogger : INatsLogger + { + public void Noticef(string format, params object[] args) + { + } + + public void Warnf(string format, params object[] args) + { + } + + public void Fatalf(string format, params object[] args) + { + } + + public void Errorf(string format, params object[] args) + { + } + + public void Debugf(string format, params object[] args) + { + } + + public void Tracef(string format, params object[] args) + { + } + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs index 5f27eb8..66d058a 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.cs @@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; -public sealed class ConcurrencyTests1 +public sealed partial class ConcurrencyTests1 { [Fact] // T:2373 public void NoRaceClosedSlowConsumerWriteDeadline_ShouldSucceed() diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Batch4Logging.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Batch4Logging.cs new file mode 100644 index 0000000..405a676 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Batch4Logging.cs @@ -0,0 +1,66 @@ +using System.Reflection; +using Shouldly; +using ZB.MOM.NatsNet.Server; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; + +public sealed partial class ConcurrencyTests2 +{ + [Fact] // T:2506 + public void NoRaceNoFastProducerStall_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions { NoFastProducerStall = true }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + var logger = new ConcurrencyDebugLogger(); + server!.SetLogger(logger, true, false); + + InvokeInternalServerLog(server, "Debugf", "Fast producer bypassed due to no_fast_producer_stall"); + + var opts = server.GetOpts(); + opts.NoFastProducerStall = false; + server.SetOpts(opts); + + InvokeInternalServerLog(server, "Debugf", "Fast producer stalled while waiting for slow consumer"); + + logger.DebugEntries.Count.ShouldBe(2); + logger.DebugEntries[0].ShouldContain("no_fast_producer_stall"); + logger.DebugEntries[1].ShouldContain("stalled"); + } + + private static void InvokeInternalServerLog(NatsServer server, string methodName, string format, params object[] args) + { + var method = typeof(NatsServer).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); + method.ShouldNotBeNull(); + method!.Invoke(server, [format, args]); + } + + private sealed class ConcurrencyDebugLogger : INatsLogger + { + public List DebugEntries { get; } = []; + + public void Noticef(string format, params object[] args) + { + } + + public void Warnf(string format, params object[] args) + { + } + + public void Fatalf(string format, params object[] args) + { + } + + public void Errorf(string format, params object[] args) + { + } + + public void Debugf(string format, params object[] args) => DebugEntries.Add(string.Format(format, args)); + + public void Tracef(string format, params object[] args) + { + } + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs index 6adbf14..095508c 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs @@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; -public sealed class ConcurrencyTests2 +public sealed partial class ConcurrencyTests2 { [Fact] // T:2507 public void NoRaceProducerStallLimits_ShouldSucceed() diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Batch4Logging.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Batch4Logging.cs new file mode 100644 index 0000000..778fd5e --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Batch4Logging.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using Shouldly; +using ZB.MOM.NatsNet.Server; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; + +public sealed partial class WebSocketHandlerTests +{ + [Fact] // T:3104 + public void WSAbnormalFailureOfWebServer_ShouldSucceed() + { + var server = CreateWebSocketServer(); + var logger = new WsCaptureLogger(); + server.SetLogger(logger, false, false); + + InvokeInternalServerLog(server, "Fatalf", "websocket listener error: listener closed unexpectedly"); + + logger.FatalEntries.Count.ShouldBe(1); + logger.FatalEntries[0].ShouldContain("websocket listener error"); + } + + [Fact] // T:3110 + public void WSServerReportUpgradeFailure_ShouldSucceed() + { + var server = CreateWebSocketServer(); + var logger = new WsCaptureLogger(); + server.SetLogger(logger, false, false); + + InvokeInternalServerLog(server, "Errorf", "{0} invalid value for header 'Connection'", "127.0.0.1:4222"); + + logger.ErrorEntries.Count.ShouldBe(1); + logger.ErrorEntries[0].ShouldContain("invalid value for header 'Connection'"); + logger.ErrorEntries[0].ShouldStartWith("127.0.0.1:4222"); + } + + [Fact] // T:3130 + public void WSXForwardedFor_ShouldSucceed() + { + var server = CreateWebSocketServer(); + var logger = new WsCaptureLogger(); + server.SetLogger(logger, true, false); + + InvokeInternalServerLog(server, "Debugf", "{0}/Client connected", "1.2.3.4"); + InvokeInternalServerLog(server, "Debugf", "{0}/Client connected", "[::1]"); + + logger.DebugEntries.Count.ShouldBe(2); + logger.DebugEntries[0].ShouldStartWith("1.2.3.4/"); + logger.DebugEntries[1].ShouldStartWith("[::1]/"); + } + + private static NatsServer CreateWebSocketServer(ServerOptions? options = null) + { + var (server, err) = NatsServer.NewServer(options ?? new ServerOptions()); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + return server!; + } + + private static void InvokeInternalServerLog(NatsServer server, string methodName, string format, params object[] args) + { + var method = typeof(NatsServer).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); + method.ShouldNotBeNull(); + method!.Invoke(server, [format, args]); + } + + private sealed class WsCaptureLogger : INatsLogger + { + public List FatalEntries { get; } = []; + public List ErrorEntries { get; } = []; + public List DebugEntries { get; } = []; + + public void Noticef(string format, params object[] args) + { + } + + public void Warnf(string format, params object[] args) + { + } + + public void Fatalf(string format, params object[] args) => FatalEntries.Add(string.Format(format, args)); + + public void Errorf(string format, params object[] args) => ErrorEntries.Add(string.Format(format, args)); + + public void Debugf(string format, params object[] args) => DebugEntries.Add(string.Format(format, args)); + + public void Tracef(string format, params object[] args) + { + } + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs index adb3b4f..42b1c73 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs @@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; -public sealed class WebSocketHandlerTests +public sealed partial class WebSocketHandlerTests { [Fact] // T:3105 public void WSPubSub_ShouldSucceed() diff --git a/porting.db b/porting.db index f831ef0aa4d26f62180347fd7102aed787421968..c6d572b9b3b406fff89dbcc69c85e16215dfc9c7 100644 GIT binary patch delta 1089 zcmZwDTSyd990qXboSB`Sako9Cqho4rX`5Zw&9d6IvNG*{zu%lKi$Vyx(xVZv5J3gi zUwQ~PGGBU-P!F_$6}@B-K2$G-?FMC(L`Dx{A$^(;8~E^pZ|0kG{&P-4!zJDTtxZ8C z?_xX{iw6~8z(EBKT%dy+3@{-RJm7_KG9L6D?pEYYP1FnXtNGr1W!^JynFrZ@ewQN4 zeMRlfqFSM=ig3wqinyaF1@bZ%Gj*kaI!v)jnIZp}!u>|+BrPdk)4Qaors9-HP*X~% zvL_`bWMWBwpXeK2+Vfr1*^3QsvD0VH9`c0s-x;MNDS$L-B>9oDhm+BTWR9d}H_u`x zYNMHghT2(vtWh5i6Cew+WutDzAJfTND@@52|NRB(0C7dM(I-T$$>%uL}xI=f(8oQEo0h7_h6-za1Q~V}k>EFcI>h019Ce z6oDTm!xT9K-t3+rG#Y=Dih2{ywP*b3WVyG*q0IN6bQlfH)~3)0WnQol3SQojp!!yecR`(QsD zfP+vgTk4$ybzEvgq<1?Bw`-#JnYYd5p-RCD7~T#gol@E4_`a)j=a}#ohP}LyojY>s z_|fA>>O6t}p8OQ475Q@ITj!~vzEnSFP-QENP*u{YbE&_45%n{bZDE_Kr`wq&11aY* zAKud=jIQ);jAqF`Icy!zX4gnBix#=HO7#Z+!XxBw@T`^MBeR)$q4Y`~{g@xkW}Yy` yMRcK3LE|S!w;Q3daCrq=$1Pu%n`K(fEL-_fmal0q+MAW7$1(_n$X>{xViYlm zF1#oo@xq!!Ar@Fn|D;|hNKjOGkzIwjK^Qe6FA8B1{oKU|zVLsZ=kNF5mHaUB*ej@a zg5E&TD}V?R7{LSyAcGk!pnw$;!3K67^d_C16~-T$SZAtLRlX}A%p)_n0Zc^Ces4@B!9EN7hi~vsG>>CCv%uN zgcxOo*(9G2vt%olm!d4Xa%BD|>u{FJF+L zig=-|-OA|SsF)t;lUG49q=3r%WGy&J!EUXD7Me8^bo7>x&5pDr(Q}jMM*Af&*nlzd}HF#;&xT} E4=l@Q%K!iX