using NATS.Server.IO; using Shouldly; namespace NATS.Server.Transport.Tests.IO; /// /// Tests for the consecutive short-read counter in AdaptiveReadBuffer. /// Go reference: server/client.go — readLoop buffer sizing with short-read counter. /// public class AdaptiveReadBufferShortReadTests { [Fact] public void Initial_size_is_4096() { var b = new AdaptiveReadBuffer(); b.CurrentSize.ShouldBe(4096); } [Fact] public void Full_read_doubles_size() { var b = new AdaptiveReadBuffer(); b.RecordRead(4096); b.CurrentSize.ShouldBe(8192); } [Fact] public void Single_short_read_does_not_shrink() { // A short read is less than target/4 = 4096/4 = 1024 var b = new AdaptiveReadBuffer(); b.RecordRead(100); b.CurrentSize.ShouldBe(4096); } [Fact] public void Three_short_reads_do_not_shrink() { var b = new AdaptiveReadBuffer(); b.RecordRead(100); b.RecordRead(100); b.RecordRead(100); b.CurrentSize.ShouldBe(4096); } [Fact] public void Four_short_reads_triggers_shrink() { var b = new AdaptiveReadBuffer(); b.RecordRead(100); b.RecordRead(100); b.RecordRead(100); b.RecordRead(100); b.CurrentSize.ShouldBe(2048); } [Fact] public void Short_read_counter_resets_on_full_read() { // 3 short reads, then a full read resets the counter — subsequent short read should not shrink var b = new AdaptiveReadBuffer(); b.RecordRead(100); // short b.RecordRead(100); // short b.RecordRead(100); // short (3 total, not yet at threshold) b.RecordRead(4096); // full read — doubles size and resets counter b.RecordRead(512); // short (relative to new size 8192; 512 < 8192/4=2048) — only 1 consecutive b.CurrentSize.ShouldBe(8192); } [Fact] public void Short_read_counter_resets_on_medium_read() { // A medium read is >= target/4 but < target // For target 4096: medium range is [1024, 4096) var b = new AdaptiveReadBuffer(); b.RecordRead(100); // short — counter = 1 b.RecordRead(100); // short — counter = 2 b.RecordRead(100); // short — counter = 3 b.RecordRead(2000); // medium (>= 4096/4=1024, < 4096) — resets counter b.RecordRead(100); // short — counter = 1, should not shrink b.CurrentSize.ShouldBe(4096); } [Fact] public void Short_read_counter_resets_after_shrink() { // After 4 short reads trigger a shrink, counter resets to 0 var b = new AdaptiveReadBuffer(); b.RecordRead(100); // short — counter = 1 b.RecordRead(100); // short — counter = 2 b.RecordRead(100); // short — counter = 3 b.RecordRead(100); // short — counter = 4 → shrinks to 2048, resets counter to 0 b.ConsecutiveShortReads.ShouldBe(0); // One more short read should be counter = 1 (not triggering another shrink) b.RecordRead(50); // short relative to 2048 (50 < 2048/4=512) — counter = 1 b.ConsecutiveShortReads.ShouldBe(1); b.CurrentSize.ShouldBe(2048); } [Fact] public void Size_never_goes_below_512() { // Force the buffer down to 512 then attempt to shrink further var b = new AdaptiveReadBuffer(); // Drive target down to 512 via repeated shrink cycles for (var i = 0; i < 4; i++) b.RecordRead(1); // target 4096 → 2048 for (var i = 0; i < 4; i++) b.RecordRead(1); // 2048 → 1024 for (var i = 0; i < 4; i++) b.RecordRead(1); // 1024 → 512 b.CurrentSize.ShouldBe(512); // Now try to shrink again — should stay at 512 for (var i = 0; i < 4; i++) b.RecordRead(1); b.CurrentSize.ShouldBe(512); } [Fact] public void ConsecutiveShortReads_property_reflects_count() { var b = new AdaptiveReadBuffer(); b.RecordRead(100); // short — counter = 1 b.RecordRead(100); // short — counter = 2 b.ConsecutiveShortReads.ShouldBe(2); } }