mbproxy: Wave 3 cleanups, docs, and test gaps from 2026-05-14 review
Closes the Wave 3 (cleanup) tier of codereviews/2026-05-14/RemediationPlan.md.
Tests: 378 pass / 0 fail (baseline 370 + 8 new W3 regression tests).
Code cleanups:
* PlcMultiplexer: removed dead `elapsedMs` calculation (the actual EWMA
conversion uses Stopwatch ticks two lines below).
* UpstreamPipe.FillAsync: dropped the meaningless `firstRead && remaining
== count ? false : false` ternary; both branches were `false`.
* InFlightByKeyMap.TryAttachOrCreate (always returned `true`) renamed to
`AttachOrCreate` and made `void`. Test sites updated to drop the dead
`bool ok = ...; ok.ShouldBeTrue();` assertions.
* BcdCodec.HasBadNibble promoted from private to internal; the duplicate
copy in BcdPduPipeline removed and the call sites updated to
`BcdCodec.HasBadNibble`.
* PlcMultiplexer watchdog comment fixed: said "1-second floor", code uses
100 ms. Now both agree.
* StatusSnapshotBuilder: simplified the unreachable
`RemoteEp?.ToString() ?? RemoteEp?.Address.ToString() ?? "?"` to
`RemoteEp?.ToString() ?? "?"`.
* Mbproxy.csproj: stale "deferred" Polly comment replaced with a real
description of where Polly is used (BackendConnect + ListenerRecovery).
Doc updates:
* README: added a callout about the unconventional 32-bit BCD wire format
("two base-10000 digits in CDAB", not standard binary CDAB Int32) so
integrators using off-the-shelf clients learn about the silent-corruption
hazard before configuring writes.
* docs/design.md: clarified `cacheMissCount` and `coalescedMissCount`
semantics — "miss" means "did not find a fresh entry / did not coalesce",
NOT "produced a backend round-trip". Operators wanting actual backend
traffic should compute `miss − coalescedHit − exception04`.
* docs/Architecture/ResponseCache.md: documented the structural
"skip invalidation while recovering" gating (no backend reader during
recovery → no FC06/FC16 response → no invalidation).
* docs/Operations/Configuration.md: noted that the Event Log sink is the
custom EventLogBridge, not Serilog.Sinks.EventLog (W2.23 cached check).
* docs/plan/README.md: added a Phase 12 row pointing at the remediation
plan and linking out to codereviews/2026-05-14/.
Test additions (W3 high-value gaps):
* BcdPduPipelineTests:
- FC16_WriteStartsOnHighWord_Of32BitPair_PassesThroughRaw_WithPartialWarning
(symmetric inverse of the existing low-side partial-overlap test).
- FC03_Mixed_16Bit_32Bit_AndNonBcd_InOneRead_OnlyConfiguredSlotsRewritten
(mixed-slot routing in a single FC03 read).
- FC16_Response_PassesThroughUnchanged_RegardlessOfTagMap (FC16 response
carries no register data; rewriter must pass through).
* AdminEndpointTests:
- NonGetMethod_AgainstAdminRoutes_Returns405 (Theory: POST/PUT/DELETE/
PATCH against `/` and `/status.json` must return 405; guards against
an accidental MapPost being added later).
* HotReloadE2ETests:
- E2E_TagListReload_OnCacheablePlc_EmitsCacheFlushedEvent (validates the
W2.8 cache.flushed wiring end-to-end via the real FileSystemWatcher
reload path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,13 +53,12 @@ public sealed class InFlightByKeyMapTests
|
||||
var party = new InterestedParty(pipe, OriginalTxId: 0x1234);
|
||||
|
||||
int factoryCalls = 0;
|
||||
bool ok = map.TryAttachOrCreate(
|
||||
map.AttachOrCreate(
|
||||
key, party,
|
||||
factory: () => { factoryCalls++; return MakeRequest(party); },
|
||||
maxParties: 32,
|
||||
out var req, out bool wasNew);
|
||||
|
||||
ok.ShouldBeTrue();
|
||||
wasNew.ShouldBeTrue("a brand-new key must take the create branch");
|
||||
factoryCalls.ShouldBe(1, "the factory must be called exactly once");
|
||||
req.ShouldNotBeNull();
|
||||
@@ -86,15 +85,14 @@ public sealed class InFlightByKeyMapTests
|
||||
var partyB = new InterestedParty(pipeB, OriginalTxId: 0x2222);
|
||||
|
||||
int factoryCalls = 0;
|
||||
map.TryAttachOrCreate(key, partyA,
|
||||
map.AttachOrCreate(key, partyA,
|
||||
factory: () => { factoryCalls++; return MakeRequest(partyA); },
|
||||
maxParties: 32, out var first, out bool firstWasNew);
|
||||
|
||||
bool ok = map.TryAttachOrCreate(key, partyB,
|
||||
map.AttachOrCreate(key, partyB,
|
||||
factory: () => { factoryCalls++; return MakeRequest(partyB); },
|
||||
maxParties: 32, out var second, out bool secondWasNew);
|
||||
|
||||
ok.ShouldBeTrue();
|
||||
firstWasNew.ShouldBeTrue();
|
||||
secondWasNew.ShouldBeFalse("the second attach must coalesce onto the first");
|
||||
factoryCalls.ShouldBe(1, "the factory must only fire on the create branch");
|
||||
@@ -126,20 +124,19 @@ public sealed class InFlightByKeyMapTests
|
||||
var partyC = new InterestedParty(pipeC, OriginalTxId: 0xCCCC);
|
||||
|
||||
// MaxParties = 2 — first attach creates, second appends, third overflows.
|
||||
map.TryAttachOrCreate(key, partyA,
|
||||
map.AttachOrCreate(key, partyA,
|
||||
factory: () => MakeRequest(partyA), maxParties: 2,
|
||||
out var first, out _);
|
||||
map.TryAttachOrCreate(key, partyB,
|
||||
map.AttachOrCreate(key, partyB,
|
||||
factory: () => MakeRequest(partyB), maxParties: 2,
|
||||
out var second, out _);
|
||||
|
||||
int factoryCalls = 0;
|
||||
bool ok = map.TryAttachOrCreate(key, partyC,
|
||||
map.AttachOrCreate(key, partyC,
|
||||
factory: () => { factoryCalls++; return MakeRequest(partyC); },
|
||||
maxParties: 2,
|
||||
out var third, out bool thirdWasNew);
|
||||
|
||||
ok.ShouldBeTrue();
|
||||
thirdWasNew.ShouldBeTrue("the third attach must overflow into a fresh entry");
|
||||
factoryCalls.ShouldBe(1, "the factory must fire to create the overflow entry");
|
||||
third.ShouldNotBeSameAs(first, "the overflow must be a distinct InFlightRequest");
|
||||
@@ -167,8 +164,8 @@ public sealed class InFlightByKeyMapTests
|
||||
var partyA = new InterestedParty(pipeA, 1);
|
||||
var partyB = new InterestedParty(pipeB, 2);
|
||||
|
||||
map.TryAttachOrCreate(key, partyA, () => MakeRequest(partyA), 32, out _, out _);
|
||||
map.TryAttachOrCreate(key, partyB, () => MakeRequest(partyB), 32, out _, out _);
|
||||
map.AttachOrCreate(key, partyA, () => MakeRequest(partyA), 32, out _, out _);
|
||||
map.AttachOrCreate(key, partyB, () => MakeRequest(partyB), 32, out _, out _);
|
||||
|
||||
bool removed = map.TryRemove(key, out var req);
|
||||
|
||||
@@ -228,7 +225,7 @@ public sealed class InFlightByKeyMapTests
|
||||
{
|
||||
if (workCt.IsCancellationRequested) return;
|
||||
var party = new InterestedParty(pipe, (ushort)i);
|
||||
map.TryAttachOrCreate(
|
||||
map.AttachOrCreate(
|
||||
key, party,
|
||||
factory: () => MakeRequest(party),
|
||||
maxParties: MaxParties,
|
||||
|
||||
Reference in New Issue
Block a user