diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs index 96e68f860..b16d59821 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs @@ -57,6 +57,11 @@ public enum CheckpointType private const int NumEventsToFallBehind = 3 * 32; + // Delay to wait for max age expiration in tests. Set to 2500ms (2.5 seconds) to account for + // timing variations, especially on Windows where expiration checks may not be perfectly synchronized. + // This reduces test flakiness by ensuring events are fully expired before verification. + private const int ExpirationDelayMs = 2500; + public static object[] TestCases = { // NON-EXISTENT STREAM CreateTestData( @@ -687,8 +692,15 @@ private Task WriteMetadata(string metadata) private Task MaxCount(long maxCount) => WriteMetadata(@$"{{""$maxCount"":{maxCount}}}"); private async Task ExpiredMaxAge() { + // Set max age to 1 second - events older than this will be expired/truncated. + // Note: A max age of zero doesn't do anything, so we're forced to use at least 1 second. await WriteMetadata(@"{""$maxAge"": 1 }"); // seconds - await Task.Delay(TimeSpan.FromMilliseconds(2000)); + + // Wait for events to expire. Using ExpirationDelayMs (2500ms) instead of the minimum 1001ms + // to account for timing variations, especially on Windows where expiration checks may + // not be perfectly synchronized. This reduces test flakiness by ensuring events are + // fully expired before the test proceeds to verify they're gone. + await Task.Delay(TimeSpan.FromMilliseconds(ExpirationDelayMs)); } private Task ApplyTruncation() @@ -890,6 +902,8 @@ public async Task enumeration_is_correct() return; } + // Verify subscription confirmation message. Store in variable to enable better error messages + // that include the test case name and actual type received, making debugging easier when tests fail. var current = await sub.GetNext(); Assert.True( current is SubscriptionConfirmation, @@ -899,6 +913,8 @@ public async Task enumeration_is_correct() _nextEventNumber = await ReadExpectedEvents(sub, _nextEventNumber, LastEventNumber); + // Verify caught-up message with descriptive error message for easier debugging. + // This helps identify which test case failed and what message type was received instead. current = await sub.GetNext(); Assert.True( current is CaughtUp, diff --git a/src/EventStore.Core.Tests/TransactionLog/versioned_pattern_filenaming_strategy.cs b/src/EventStore.Core.Tests/TransactionLog/versioned_pattern_filenaming_strategy.cs index 3b36dcbed..2ada28d45 100644 --- a/src/EventStore.Core.Tests/TransactionLog/versioned_pattern_filenaming_strategy.cs +++ b/src/EventStore.Core.Tests/TransactionLog/versioned_pattern_filenaming_strategy.cs @@ -192,7 +192,8 @@ public void throws_for_incorrectly_formatted_filename() } [Test] - public void returns_correct_prefix_with_get_prefix_for() { + public void returns_correct_prefix_with_get_prefix_for() + { var strategy = new VersionedPatternFileNamingStrategy(PathName, "chunk-"); Assert.AreEqual("chunk-", strategy.GetPrefixFor(null, null)); Assert.AreEqual("chunk-000000.", strategy.GetPrefixFor(0, null)); diff --git a/src/EventStore.Core.Tests/TransactionLog/when_chasing_a_chunked_transaction_log.cs b/src/EventStore.Core.Tests/TransactionLog/when_chasing_a_chunked_transaction_log.cs index 83c429d92..9e3f7222e 100644 --- a/src/EventStore.Core.Tests/TransactionLog/when_chasing_a_chunked_transaction_log.cs +++ b/src/EventStore.Core.Tests/TransactionLog/when_chasing_a_chunked_transaction_log.cs @@ -20,13 +20,16 @@ public static class LogRecordExtensions public static void WriteWithLengthPrefixAndSuffixTo(this ILogRecord record, BinaryWriter writer) { var localWriter = new BufferWriterSlim(); - try { + try + { record.WriteTo(ref localWriter); writer.Write(localWriter.WrittenCount); writer.Write(localWriter.WrittenSpan); writer.Write(localWriter.WrittenCount); - } finally { + } + finally + { localWriter.Dispose(); } } diff --git a/src/EventStore.Core.Tests/TransactionLog/when_reading_physical_bytes_bulk_from_a_chunk.cs b/src/EventStore.Core.Tests/TransactionLog/when_reading_physical_bytes_bulk_from_a_chunk.cs index c36028a36..4ef942b92 100644 --- a/src/EventStore.Core.Tests/TransactionLog/when_reading_physical_bytes_bulk_from_a_chunk.cs +++ b/src/EventStore.Core.Tests/TransactionLog/when_reading_physical_bytes_bulk_from_a_chunk.cs @@ -38,39 +38,39 @@ public async Task a_read_on_new_file_can_be_performed() chunk.MarkForDeletion(); chunk.WaitForDestroy(5000); } -/* - [Test] - public void a_read_on_scavenged_chunk_includes_map() - { - var chunk = TFChunk.CreateNew(GetFilePathFor("afile"), 200, 0, 0, isScavenged: true, inMem: false, unbuffered: false, writethrough: false); - chunk.CompleteScavenge(new [] {new PosMap(0, 0), new PosMap(1,1) }, false); - using (var reader = chunk.AcquireRawReader()) - { - var buffer = new byte[1024]; - var result = reader.ReadNextBytes(1024, buffer); - Assert.IsFalse(result.IsEOF); - Assert.AreEqual(ChunkHeader.Size + ChunkHeader.Size + 2 * PosMap.FullSize, result.BytesRead); - } - chunk.MarkForDeletion(); - chunk.WaitForDestroy(5000); - } + /* + [Test] + public void a_read_on_scavenged_chunk_includes_map() + { + var chunk = TFChunk.CreateNew(GetFilePathFor("afile"), 200, 0, 0, isScavenged: true, inMem: false, unbuffered: false, writethrough: false); + chunk.CompleteScavenge(new [] {new PosMap(0, 0), new PosMap(1,1) }, false); + using (var reader = chunk.AcquireRawReader()) + { + var buffer = new byte[1024]; + var result = reader.ReadNextBytes(1024, buffer); + Assert.IsFalse(result.IsEOF); + Assert.AreEqual(ChunkHeader.Size + ChunkHeader.Size + 2 * PosMap.FullSize, result.BytesRead); + } + chunk.MarkForDeletion(); + chunk.WaitForDestroy(5000); + } - [Test] - public void a_read_past_end_of_completed_chunk_does_include_header_or_footer() - { - var chunk = TFChunk.CreateNew(GetFilePathFor("File1"), 300, 0, 0, isScavenged: false, inMem: false, unbuffered: false, writethrough: false); - chunk.Complete(); - using (var reader = chunk.AcquireRawReader()) - { - var buffer = new byte[1024]; - var result = reader.ReadNextBytes(1024, buffer); - Assert.IsTrue(result.IsEOF); - Assert.AreEqual(ChunkHeader.Size + ChunkFooter.Size, result.BytesRead); //just header + footer = 256 - } - chunk.MarkForDeletion(); - chunk.WaitForDestroy(5000); - } -*/ + [Test] + public void a_read_past_end_of_completed_chunk_does_include_header_or_footer() + { + var chunk = TFChunk.CreateNew(GetFilePathFor("File1"), 300, 0, 0, isScavenged: false, inMem: false, unbuffered: false, writethrough: false); + chunk.Complete(); + using (var reader = chunk.AcquireRawReader()) + { + var buffer = new byte[1024]; + var result = reader.ReadNextBytes(1024, buffer); + Assert.IsTrue(result.IsEOF); + Assert.AreEqual(ChunkHeader.Size + ChunkFooter.Size, result.BytesRead); //just header + footer = 256 + } + chunk.MarkForDeletion(); + chunk.WaitForDestroy(5000); + } + */ [Test] public async Task if_asked_for_more_than_buffer_size_will_only_read_buffer_size() diff --git a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum.cs b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum.cs index 8cc19b53e..89724e853 100644 --- a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum.cs +++ b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum.cs @@ -68,7 +68,9 @@ public async Task a_record_can_be_written() await using var filestream = File.Open(filename, new FileStreamOptions { - Mode = FileMode.Open, Access = FileAccess.Read, Options = FileOptions.Asynchronous + Mode = FileMode.Open, + Access = FileAccess.Read, + Options = FileOptions.Asynchronous }); filestream.Seek(ChunkHeader.Size + 137 + sizeof(int), SeekOrigin.Begin); var recordLength = filestream.Length - filestream.Position; diff --git a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum_and_data_bigger_than_buffer.cs b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum_and_data_bigger_than_buffer.cs index 822120dd9..fd2210590 100644 --- a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum_and_data_bigger_than_buffer.cs +++ b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_checksum_and_data_bigger_than_buffer.cs @@ -73,7 +73,9 @@ public async Task a_record_can_be_written() await using var filestream = File.Open(filename, new FileStreamOptions { - Mode = FileMode.Open, Access = FileAccess.Read, Options = FileOptions.Asynchronous + Mode = FileMode.Open, + Access = FileAccess.Read, + Options = FileOptions.Asynchronous }); filestream.Seek(ChunkHeader.Size + 137 + sizeof(int), SeekOrigin.Begin); var recordLength = filestream.Length - filestream.Position; diff --git a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_not_enough_space_in_chunk.cs b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_not_enough_space_in_chunk.cs index 0620b6889..7da04b6c5 100644 --- a/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_not_enough_space_in_chunk.cs +++ b/src/EventStore.Core.Tests/TransactionLog/when_writing_an_existing_chunked_transaction_file_with_not_enough_space_in_chunk.cs @@ -104,7 +104,9 @@ public async Task a_record_is_not_written_at_first_but_written_on_second_try() await using var filestream = File.Open(filename2, new FileStreamOptions { - Mode = FileMode.Open, Access = FileAccess.Read, Options = FileOptions.Asynchronous + Mode = FileMode.Open, + Access = FileAccess.Read, + Options = FileOptions.Asynchronous }); filestream.Seek(ChunkHeader.Size + sizeof(int), SeekOrigin.Begin); var recordLength = filestream.Length - filestream.Position; diff --git a/src/EventStore.Core.Tests/TransactionLog/with_tfchunk_enumerator.cs b/src/EventStore.Core.Tests/TransactionLog/with_tfchunk_enumerator.cs index 09204b2f7..2aad3074e 100644 --- a/src/EventStore.Core.Tests/TransactionLog/with_tfchunk_enumerator.cs +++ b/src/EventStore.Core.Tests/TransactionLog/with_tfchunk_enumerator.cs @@ -38,8 +38,10 @@ public async Task iterates_chunks_with_correct_callback_order() var strategy = new VersionedPatternFileNamingStrategy(PathName, "chunk-"); var chunkEnumerator = new TFChunkEnumerator(strategy); var result = new List(); - ValueTask GetNextFileNumber(string chunk, int chunkNumber, int chunkVersion, CancellationToken token) { - return Path.GetFileName(chunk) switch { + ValueTask GetNextFileNumber(string chunk, int chunkNumber, int chunkVersion, CancellationToken token) + { + return Path.GetFileName(chunk) switch + { "chunk-000001.000000" => new(2), "chunk-000002.000001" => new(3), "chunk-000005.000000" => new(6), diff --git a/src/EventStore.Core.Tests/Transforms/BitFlip/BitFlipChunkWriteTransform.cs b/src/EventStore.Core.Tests/Transforms/BitFlip/BitFlipChunkWriteTransform.cs index 9cd4801d4..a42eee305 100644 --- a/src/EventStore.Core.Tests/Transforms/BitFlip/BitFlipChunkWriteTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/BitFlip/BitFlipChunkWriteTransform.cs @@ -34,7 +34,8 @@ public async ValueTask WriteFooter(ReadOnlyMemory footer, Cancellatio private static int GetAlignedSize(int size, int alignmentSize) { - if (size % alignmentSize == 0) return size; + if (size % alignmentSize == 0) + return size; return (size / alignmentSize + 1) * alignmentSize; } } diff --git a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkReadTransform.cs b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkReadTransform.cs index de03e703a..6108f508a 100644 --- a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkReadTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkReadTransform.cs @@ -1,6 +1,7 @@ using EventStore.Plugins.Transforms; namespace EventStore.Core.Tests.Transforms.ByteDup; + public class ByteDupChunkReadTransform : IChunkReadTransform { public ChunkDataReadStream TransformData(ChunkDataReadStream dataStream) => diff --git a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkTransform.cs b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkTransform.cs index cb3cebe16..d6769969f 100644 --- a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkTransform.cs @@ -1,6 +1,7 @@ using EventStore.Plugins.Transforms; namespace EventStore.Core.Tests.Transforms.ByteDup; + public class ByteDupChunkTransform : IChunkTransform { public IChunkReadTransform Read { get; } = new ByteDupChunkReadTransform(); diff --git a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkWriteTransform.cs b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkWriteTransform.cs index f00f08eca..7f4cde3f8 100644 --- a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkWriteTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupChunkWriteTransform.cs @@ -37,7 +37,8 @@ public async ValueTask WriteFooter(ReadOnlyMemory footer, Cancellatio private static int GetAlignedSize(int size, int alignmentSize) { - if (size % alignmentSize == 0) return size; + if (size % alignmentSize == 0) + return size; return (size / alignmentSize + 1) * alignmentSize; } } diff --git a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupDbTransform.cs b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupDbTransform.cs index c52625c7c..b80e55986 100644 --- a/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupDbTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/ByteDup/ByteDupDbTransform.cs @@ -1,6 +1,7 @@ using EventStore.Plugins.Transforms; namespace EventStore.Core.Tests.Transforms.ByteDup; + public class ByteDupDbTransform : IDbTransform { public string Name => "bytedup"; diff --git a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkReadTransform.cs b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkReadTransform.cs index 26bc83c58..53bdd6e93 100644 --- a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkReadTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkReadTransform.cs @@ -1,6 +1,7 @@ using EventStore.Plugins.Transforms; namespace EventStore.Core.Tests.Transforms.WithHeader; + public class WithHeaderChunkReadTransform(int transformHeaderSize) : IChunkReadTransform { public ChunkDataReadStream TransformData(ChunkDataReadStream dataStream) => diff --git a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkWriteTransform.cs b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkWriteTransform.cs index 88a600b6b..7db67f70a 100644 --- a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkWriteTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderChunkWriteTransform.cs @@ -36,7 +36,8 @@ public async ValueTask WriteFooter(ReadOnlyMemory footer, Cancellatio private static int GetAlignedSize(int size, int alignmentSize) { - if (size % alignmentSize == 0) return size; + if (size % alignmentSize == 0) + return size; return (size / alignmentSize + 1) * alignmentSize; } } diff --git a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderDbTransform.cs b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderDbTransform.cs index fe2e5ffea..fecca1cf5 100644 --- a/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderDbTransform.cs +++ b/src/EventStore.Core.Tests/Transforms/WithHeader/WithHeaderDbTransform.cs @@ -1,6 +1,7 @@ using EventStore.Plugins.Transforms; namespace EventStore.Core.Tests.Transforms.WithHeader; + public class WithHeaderDbTransform : IDbTransform { public string Name => "withheader";