From 2cce3352cf8487635a8d296d1aa25b20ce34e580 Mon Sep 17 00:00:00 2001 From: Chris Wolfgang <210299580+Chris-Wolfgang@users.noreply.github.com> Date: Tue, 18 Aug 2026 21:26:07 -0400 Subject: [PATCH] chore: remove redundant null-forgiving operators (S8969) + suppress test-gate VSTHRD003 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SonarAnalyzer 10.32 bump (#416) added rule S8969 ("Null-forgiving operators should not be redundant"), which flagged 20 redundant `x!.member` uses across the test/contract-base code (e.g. `final!.ErrorItemCount`, `PeekStartedAt!.Value` — the `!` has no effect where the value is already non-null). Removed the redundant `!` at each site. Release (TWAE) build stays clean, which proves each was genuinely redundant (a needed `!` would surface a CS86xx nullable warning). Also suppressed one VSTHRD003 (await of a foreign task) in GatedSourceAsync — awaiting a TaskCompletionSource used to gate the test is intentional — with a narrow inline #pragma rather than a per-instance dismissal, so it survives line shifts. No behaviour or public-API change. Co-Authored-By: Claude Opus 4.8 --- src/Wolfgang.Etl.TestKit.Xunit/EtlPipelineContractTests.cs | 2 +- src/Wolfgang.Etl.TestKit.Xunit/EtlScenarioOfT.cs | 2 +- .../IdempotentLoaderContractTests.cs | 2 +- .../IdempotentTransformerContractTests.cs | 2 +- src/Wolfgang.Etl.TestKit/TestExtractor.cs | 2 +- .../EtlPipelineFuzzTests.cs | 2 +- .../Wolfgang.Etl.Abstractions.Tests.Fuzz/ReportFuzzTests.cs | 2 +- .../BaseClassTests/ClockSeamTests.cs | 2 +- .../BaseClassTests/TimerSeamTests.cs | 6 +++--- .../Wolfgang.Etl.TestKit.Tests.Unit/FaultyExtractorTests.cs | 2 +- .../FaultyLoaderMutationTests.cs | 2 ++ tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderTests.cs | 2 +- .../FaultyTransformerTests.cs | 2 +- tests/Wolfgang.Etl.TestKit.Tests.Unit/TestExtractorTests.cs | 2 +- tests/Wolfgang.Etl.TestKit.Tests.Unit/TestLoaderTests.cs | 2 +- .../Wolfgang.Etl.TestKit.Tests.Unit/TestTransformerTests.cs | 2 +- .../EtlPipelineErrorAggregationTests.cs | 4 ++-- .../EtlPipelineProgressTests.cs | 2 +- 18 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/Wolfgang.Etl.TestKit.Xunit/EtlPipelineContractTests.cs b/src/Wolfgang.Etl.TestKit.Xunit/EtlPipelineContractTests.cs index 1067ae89..cc35d638 100644 --- a/src/Wolfgang.Etl.TestKit.Xunit/EtlPipelineContractTests.cs +++ b/src/Wolfgang.Etl.TestKit.Xunit/EtlPipelineContractTests.cs @@ -101,7 +101,7 @@ public async Task Pipeline_progress_counts_all_records_extracted_and_loaded_Asyn var final = capture.FinalReport; Assert.NotNull(final); - Assert.Equal(source.Count, final!.ExtractedItemCount); + Assert.Equal(source.Count, final.ExtractedItemCount); Assert.Equal(source.Count, final.LoadedItemCount); Assert.Equal(0, final.ErrorItemCount); } diff --git a/src/Wolfgang.Etl.TestKit.Xunit/EtlScenarioOfT.cs b/src/Wolfgang.Etl.TestKit.Xunit/EtlScenarioOfT.cs index 5aa968cb..ed37d05b 100644 --- a/src/Wolfgang.Etl.TestKit.Xunit/EtlScenarioOfT.cs +++ b/src/Wolfgang.Etl.TestKit.Xunit/EtlScenarioOfT.cs @@ -94,7 +94,7 @@ public async Task RunAndAssertAsync(IReadOnlyList expectedLoaded, int expecte Assert.Equal(expectedLoaded, loader.GetCollectedItems()); Assert.NotNull(capture.FinalReport); - Assert.Equal(expectedErrors, capture.FinalReport!.ErrorItemCount); + Assert.Equal(expectedErrors, capture.FinalReport.ErrorItemCount); } diff --git a/src/Wolfgang.Etl.TestKit.Xunit/IdempotentLoaderContractTests.cs b/src/Wolfgang.Etl.TestKit.Xunit/IdempotentLoaderContractTests.cs index 7521a8f4..f571865c 100644 --- a/src/Wolfgang.Etl.TestKit.Xunit/IdempotentLoaderContractTests.cs +++ b/src/Wolfgang.Etl.TestKit.Xunit/IdempotentLoaderContractTests.cs @@ -95,7 +95,7 @@ public abstract class IdempotentLoaderContractTests /// /// The system under test, after a load has completed. protected virtual int GetCurrentItemCount(TSut sut) => - ((LoaderBase)(object)sut!).CurrentItemCount; + ((LoaderBase)(object)sut).CurrentItemCount; diff --git a/src/Wolfgang.Etl.TestKit.Xunit/IdempotentTransformerContractTests.cs b/src/Wolfgang.Etl.TestKit.Xunit/IdempotentTransformerContractTests.cs index 4ed168a8..cb884af2 100644 --- a/src/Wolfgang.Etl.TestKit.Xunit/IdempotentTransformerContractTests.cs +++ b/src/Wolfgang.Etl.TestKit.Xunit/IdempotentTransformerContractTests.cs @@ -81,7 +81,7 @@ public abstract class IdempotentTransformerContractTests /// /// The system under test, after a transform has completed. protected virtual int GetCurrentItemCount(TSut sut) => - ((TransformerBase)(object)sut!).CurrentItemCount; + ((TransformerBase)(object)sut).CurrentItemCount; diff --git a/src/Wolfgang.Etl.TestKit/TestExtractor.cs b/src/Wolfgang.Etl.TestKit/TestExtractor.cs index d60cb17e..485e76fe 100644 --- a/src/Wolfgang.Etl.TestKit/TestExtractor.cs +++ b/src/Wolfgang.Etl.TestKit/TestExtractor.cs @@ -542,7 +542,7 @@ [EnumeratorCancellation] CancellationToken token // it changes no observable extraction result (the source is synchronous). await Task.Yield(); - var enumerator = (_enumerator ?? _enumerable!.GetEnumerator())!; + var enumerator = (_enumerator ?? _enumerable!.GetEnumerator()); var ownsEnumerator = _enumerator == null; try diff --git a/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/EtlPipelineFuzzTests.cs b/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/EtlPipelineFuzzTests.cs index 17271393..c8c8ec3a 100644 --- a/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/EtlPipelineFuzzTests.cs +++ b/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/EtlPipelineFuzzTests.cs @@ -50,7 +50,7 @@ public void From_To_delivers_the_source_in_order_and_counts_both_ends() Assert.Equal(items, loader.Loaded); Assert.NotNull(last); - Assert.Equal(items.Length, last!.ExtractedItemCount); + Assert.Equal(items.Length, last.ExtractedItemCount); Assert.Equal(items.Length, last.LoadedItemCount); }, iter: Iterations); diff --git a/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/ReportFuzzTests.cs b/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/ReportFuzzTests.cs index 5902bdb3..d4a5aa69 100644 --- a/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/ReportFuzzTests.cs +++ b/tests/Wolfgang.Etl.Abstractions.Tests.Fuzz/ReportFuzzTests.cs @@ -57,7 +57,7 @@ public void Report_metrics_are_internally_consistent() { // Percentage is always clamped to [0, 100]. Assert.NotNull(report.PercentComplete); - Assert.InRange(report.PercentComplete!.Value, 0d, 100d); + Assert.InRange(report.PercentComplete.Value, 0d, 100d); // Once the count reaches the total, nothing remains. if (current >= total.Value) diff --git a/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/ClockSeamTests.cs b/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/ClockSeamTests.cs index e6cce510..ac4a7e36 100644 --- a/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/ClockSeamTests.cs +++ b/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/ClockSeamTests.cs @@ -80,7 +80,7 @@ public async Task With_no_injected_source_the_real_system_clock_is_used() var after = DateTimeOffset.UtcNow; Assert.NotNull(sut.PeekStartedAt); - Assert.InRange(sut.PeekStartedAt!.Value, before.AddSeconds(-1), after.AddSeconds(1)); + Assert.InRange(sut.PeekStartedAt.Value, before.AddSeconds(-1), after.AddSeconds(1)); Assert.True(sut.PeekElapsed >= TimeSpan.Zero); } diff --git a/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/TimerSeamTests.cs b/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/TimerSeamTests.cs index 7a942e52..556976ba 100644 --- a/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/TimerSeamTests.cs +++ b/tests/Wolfgang.Etl.Abstractions.Tests.Unit/BaseClassTests/TimerSeamTests.cs @@ -123,7 +123,7 @@ public void LoaderBase_CreateProgressTimer_returns_a_started_timer() using var timer = loader.CallCreateProgressTimer(); Assert.NotNull(core); - Assert.Equal(321, core!.LastDueTime); // CreateProgressTimer called Start(ReportingInterval) + Assert.Equal(321, core.LastDueTime); // CreateProgressTimer called Start(ReportingInterval) Assert.Equal(321, core.LastPeriod); } @@ -137,7 +137,7 @@ public void TransformerBase_CreateProgressTimer_returns_a_started_timer() using var timer = transformer.CallCreateProgressTimer(); Assert.NotNull(core); - Assert.Equal(654, core!.LastDueTime); + Assert.Equal(654, core.LastDueTime); Assert.Equal(654, core.LastPeriod); } @@ -151,7 +151,7 @@ public void ExtractorBase_CreateProgressTimer_returns_a_started_timer() using var timer = extractor.CallCreateProgressTimer(); Assert.NotNull(core); - Assert.Equal(111, core!.LastDueTime); + Assert.Equal(111, core.LastDueTime); Assert.Equal(111, core.LastPeriod); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyExtractorTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyExtractorTests.cs index 6ec01e29..989cdc76 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyExtractorTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyExtractorTests.cs @@ -331,7 +331,7 @@ public async Task ExtractAsync_with_progress_and_injected_timer_reports_progress while (await enumerator.MoveNextAsync()) { } Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderMutationTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderMutationTests.cs index 47d4b10b..5ae742a7 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderMutationTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderMutationTests.cs @@ -311,7 +311,9 @@ public async Task LoadAsync_stops_the_injected_timer_in_the_finally() private static async IAsyncEnumerable GatedSourceAsync(TaskCompletionSource gate) { yield return 1; +#pragma warning disable VSTHRD003 // awaiting a test-gate TaskCompletionSource is intentional await gate.Task.ConfigureAwait(false); +#pragma warning restore VSTHRD003 yield return 2; yield return 3; } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderTests.cs index 04ce7a6c..90ca95fe 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyLoaderTests.cs @@ -296,7 +296,7 @@ public async Task LoadAsync_with_progress_and_injected_timer_reports_progress_wh await task; Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyTransformerTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyTransformerTests.cs index 66e0095d..d9f2a203 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyTransformerTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/FaultyTransformerTests.cs @@ -326,7 +326,7 @@ public async Task TransformAsync_with_progress_and_injected_timer_reports_progre while (await enumerator.MoveNextAsync()) { } Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestExtractorTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestExtractorTests.cs index c3fe7512..eeb4d550 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestExtractorTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestExtractorTests.cs @@ -824,7 +824,7 @@ public async Task ExtractAsync_with_progress_and_injected_timer_reports_progress while (await enumerator.MoveNextAsync()) { } Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestLoaderTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestLoaderTests.cs index 38f3532d..4dfcd860 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestLoaderTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestLoaderTests.cs @@ -344,7 +344,7 @@ public async Task LoadAsync_with_progress_and_injected_timer_reports_progress_wh await task; Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestTransformerTests.cs b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestTransformerTests.cs index 0a4373ce..df66dafa 100644 --- a/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestTransformerTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Tests.Unit/TestTransformerTests.cs @@ -199,7 +199,7 @@ public async Task TransformAsync_with_progress_and_injected_timer_reports_progre while (await enumerator.MoveNextAsync()) { } Assert.NotNull(captured); - Assert.True(captured!.CurrentItemCount >= 1); + Assert.True(captured.CurrentItemCount >= 1); } diff --git a/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineErrorAggregationTests.cs b/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineErrorAggregationTests.cs index 71fd0473..6449e250 100644 --- a/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineErrorAggregationTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineErrorAggregationTests.cs @@ -34,7 +34,7 @@ await EtlPipeline var final = capture.FinalReport; Assert.NotNull(final); // One error skipped in the extractor + one in the loader. - Assert.Equal(2, final!.ErrorItemCount); + Assert.Equal(2, final.ErrorItemCount); } @@ -64,6 +64,6 @@ await EtlPipeline var final = capture.FinalReport; Assert.NotNull(final); // One error skipped in each of the extractor, transformer, and loader. - Assert.Equal(3, final!.ErrorItemCount); + Assert.Equal(3, final.ErrorItemCount); } } diff --git a/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineProgressTests.cs b/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineProgressTests.cs index ad9b242d..2fe0437f 100644 --- a/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineProgressTests.cs +++ b/tests/Wolfgang.Etl.TestKit.Xunit.Tests.Unit/EtlPipelineProgressTests.cs @@ -31,7 +31,7 @@ await EtlPipeline var final = capture.FinalReport; Assert.NotNull(final); - Assert.Equal(1, final!.ErrorItemCount); + Assert.Equal(1, final.ErrorItemCount); Assert.Equal(4, final.LoadedItemCount); }