fix(review): remediate re-review findings — DCL-029/InboundAPI-031/SiteRuntime-032/StoreAndForward-028 + Low doc/test
Fixes the 8 findings from the 2026-06-24 re-review (commit c42bb485), with a
regression test per Medium finding:
- DataConnectionLayer-029 (Med): HandleAlarmSubscribeCompleted now mirrors the
tag-path re-check — if a feed is already stored for the source, release the
redundant just-created subscription instead of overwriting + leaking the first
one (the double-subscribe window DCL-023 reopened). +regression test.
- InboundAPI-031 (Med): remove WaitForAttribute's local 5s grace backstop (tighter
than the CommunicationService Ask's timeout+IntegrationTimeout round-trip budget,
so a slow-but-valid timed-out 'false' got cancelled into a 500). Link only the
client-abort + explicit caller tokens; the lower layer owns the backstop. +test.
- SiteRuntime-032 (Med): derive the deployed count from an authoritative set of
deployed config names (HashSet) instead of a map-presence-gated int, so deleting
a DISABLED instance decrements correctly (SiteRuntime-029's gate leaked it).
+deploy->disable->delete regression test.
- StoreAndForward-028 (Med): reset _bufferedCount in StopAsync alongside the
register-guard so a same-instance Stop->Start re-seeds from a clean base (no ~2N
gauge double-count). +restart regression test.
- AuditLog-017 (Low): test the OnIngestAsync scope-resolution guard (actor survives,
replies empty, counts the failure) — no longer unpinned.
- CentralUI-037 / ScriptAnalysis-009 / SiteRuntime-033 (Low): doc-comment + spec
fixes (Database-throws in the inbound sandbox; baseReferences param wording;
native-alarm cap return-to-normal + per-condition NativeAlarmDropped eviction).
Targeted suites green: SiteRuntime 5, StoreAndForward 6, InboundAPI 31,
DataConnectionLayer 10, AuditLog 5, ScriptAnalysis 40, CentralUI ScriptAnalysis 52.
This commit is contained in:
@@ -209,6 +209,50 @@ public class QueueDepthGaugeTests : IAsyncLifetime, IDisposable
|
||||
Assert.Equal(0, ReadQueueDepthGauge());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// StoreAndForward-028: a same-instance Stop→Start must re-seed the cached depth from a
|
||||
/// clean base. <see cref="StoreAndForwardService.StopAsync"/> resets the one-time
|
||||
/// registration guard so a later <see cref="StoreAndForwardService.StartAsync"/>
|
||||
/// re-registers and re-seeds <c>_bufferedCount</c> from the durable Pending count; if
|
||||
/// StopAsync did not also reset <c>_bufferedCount</c>, the restart would ADD the re-read
|
||||
/// count on top of the leftover in-memory value, double-counting the gauge to ~2N.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task StartAsync_AfterStop_ReseedsFromCleanBase_NoDoubleCount()
|
||||
{
|
||||
// One durable Pending row that survives the stop/restart in SQLite.
|
||||
await _storage.EnqueueAsync(new StoreAndForwardMessage
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
Category = StoreAndForwardCategory.ExternalSystem,
|
||||
Target = "api",
|
||||
PayloadJson = "{}",
|
||||
Status = StoreAndForwardMessageStatus.Pending,
|
||||
CreatedAt = DateTimeOffset.UtcNow,
|
||||
MaxRetries = 3
|
||||
});
|
||||
|
||||
var svc = new StoreAndForwardService(
|
||||
_storage,
|
||||
new StoreAndForwardOptions { RetryTimerInterval = TimeSpan.FromMinutes(10) },
|
||||
NullLogger<StoreAndForwardService>.Instance);
|
||||
|
||||
// First start seeds the cached count from the durable store → 1.
|
||||
await svc.StartAsync();
|
||||
Assert.Equal(1, ReadQueueDepthGauge());
|
||||
|
||||
// Graceful stop resets the registration guard AND the cached count (the fix).
|
||||
await svc.StopAsync();
|
||||
|
||||
// Restart the SAME instance: the guard was reset so StartAsync re-seeds from the
|
||||
// store (still 1 Pending). With the _bufferedCount reset the gauge reports 1, not 2;
|
||||
// without it the seed would ADD onto the leftover 1 → 2 (the double-count bug).
|
||||
await svc.StartAsync();
|
||||
Assert.Equal(1, ReadQueueDepthGauge());
|
||||
|
||||
await svc.StopAsync();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gauge_SeedsFromExistingPendingRows_OnStart()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user