fix(notifications): park corrupt buffered notification payloads instead of silently discarding as delivered (supersedes StoreAndForward-018)
This commit is contained in:
@@ -199,16 +199,13 @@ public class NotificationForwarderTests : TestKit
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Deliver_CorruptJsonPayload_ReturnsTrue_AndDoesNotForwardAnything()
|
||||
public async Task Deliver_CorruptJsonPayload_ReturnsFalse_ParksForForensics_AndDoesNotForward()
|
||||
{
|
||||
// Regression test for StoreAndForward-018. The design doc forbids parking
|
||||
// notifications ("notifications do not park — they are retried at the fixed
|
||||
// forward interval until central acks"; Component-StoreAndForward.md). The
|
||||
// previous implementation returned false on a corrupt payload, which the S&F
|
||||
// engine interprets as a permanent failure and parks the row — contradicting
|
||||
// the invariant. The fix: discard a corrupt buffered notification by
|
||||
// returning true (engine clears the buffer via its normal success path),
|
||||
// with a Warning log line carrying the row id and a payload preview.
|
||||
// Supersedes StoreAndForward-018 (discard-on-corrupt). Returning false is the
|
||||
// handler contract's permanent-failure signal: the engine parks the row, which
|
||||
// preserves the payload for operator forensics. Retrying a corrupt payload is
|
||||
// pointless; deleting it silently (and reporting Delivered) loses data with no
|
||||
// parked row, no central Notifications row, and no audit trail.
|
||||
var centralProbe = CreateTestProbe();
|
||||
var forwarder = new NotificationForwarder(
|
||||
centralProbe.Ref, "site-7", ForwardTimeout);
|
||||
@@ -222,7 +219,7 @@ public class NotificationForwarderTests : TestKit
|
||||
OriginInstanceName = "Plant.Pump3",
|
||||
};
|
||||
|
||||
Assert.True(await forwarder.DeliverAsync(corrupt));
|
||||
Assert.False(await forwarder.DeliverAsync(corrupt));
|
||||
|
||||
// The corrupt-payload path must NOT round-trip to central — no
|
||||
// NotificationSubmit / no Ask. ExpectNoMsg confirms nothing was forwarded.
|
||||
@@ -230,11 +227,11 @@ public class NotificationForwarderTests : TestKit
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Deliver_NullDeserializedPayload_ReturnsTrue_AndDoesNotForwardAnything()
|
||||
public async Task Deliver_NullDeserializedPayload_ReturnsFalse_ParksForForensics_AndDoesNotForward()
|
||||
{
|
||||
// The companion case to corrupt JSON: the payload is valid JSON but
|
||||
// deserialises to null (e.g. "null"). Same treatment per StoreAndForward-018
|
||||
// — discard rather than park.
|
||||
// deserialises to null (e.g. "null"). Same treatment — park (return false)
|
||||
// rather than silently discard-as-delivered. Supersedes StoreAndForward-018.
|
||||
var centralProbe = CreateTestProbe();
|
||||
var forwarder = new NotificationForwarder(
|
||||
centralProbe.Ref, "site-7", ForwardTimeout);
|
||||
@@ -248,7 +245,7 @@ public class NotificationForwarderTests : TestKit
|
||||
OriginInstanceName = "Plant.Pump3",
|
||||
};
|
||||
|
||||
Assert.True(await forwarder.DeliverAsync(nullPayload));
|
||||
Assert.False(await forwarder.DeliverAsync(nullPayload));
|
||||
centralProbe.ExpectNoMsg(TimeSpan.FromMilliseconds(200));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user