logos-delivery/tests/messaging/test_delivery_task_reaping.nim
stubbsta 5a9f518bdd
fix: charge admission once per task and exempt budget-parked tasks from the reaper
Two defects the send-service seam had once admission actually enforces,
both fixed by a single DeliveryTask.firstAdmittedTime field:

- The retry loop gated admission on firstPropagatedTime.isNone(), so a
  task that failed to propagate (e.g. no peers) was re-admitted on every
  1s tick, re-charging the epoch budget and — with the shipped default of
  one message per epoch — starving all other traffic. Admission is now
  gated on firstAdmittedTime.isNone(): a task charges one slot / draws one
  nonce for its lifetime, and retries reuse it.

- reportTaskResult reaped any never-propagated task older than
  MaxTimeInCache (60s) measured from message creation, so an over-budget
  task parked for a 600s epoch was hard-failed with a misleading "Unable
  to send within retry time window" long before the epoch could roll
  (issue #4049). The reaper now runs only for admitted tasks and measures
  from admission, so a task waiting for epoch budget is exempt and gets a
  full delivery window once it is finally admitted.

The RLN-rejection branch in both processors resets firstAdmittedTime
along with clearing the proof, so the regenerated proof's fresh nonce is
re-admitted rather than sent uncharged.

The reap decision is extracted to DeliveryTask.isDeliveryTimedOut and
unit-tested (tests/messaging/test_delivery_task_reaping.nim); a
scheduler-level integration test of park-and-release is a follow-up.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 12:30:49 +02:00

40 lines
1.7 KiB
Nim

{.used.}
import results, chronos, testutils/unittests
import logos_delivery/messaging/delivery_service/send_service/delivery_task
const MaxTime = chronos.minutes(1)
proc taskWith(admitted, propagated: Opt[Moment]): DeliveryTask =
## Builds a DeliveryTask directly (bypassing `new`, which needs a broker) with
## only the fields the reaping predicate reads.
DeliveryTask(firstAdmittedTime: admitted, firstPropagatedTime: propagated)
suite "DeliveryTask - delivery-timeout reaping":
test "a task parked for budget (never admitted) is exempt, however old":
## The #4049 fix: budget-parked tasks must survive to the epoch roll instead
## of being aged out with a misleading failure.
let task = taskWith(Opt.none(Moment), Opt.none(Moment))
check not task.isDeliveryTimedOut(MaxTime)
test "an admitted, never-propagated task past the window times out":
let task = taskWith(Opt.some(Moment.now() - chronos.minutes(2)), Opt.none(Moment))
check task.isDeliveryTimedOut(MaxTime)
test "an admitted task still within the window does not time out":
let task = taskWith(Opt.some(Moment.now()), Opt.none(Moment))
check not task.isDeliveryTimedOut(MaxTime)
test "a propagated task is never timed out here (store validation owns it)":
let task =
taskWith(Opt.some(Moment.now() - chronos.minutes(2)), Opt.some(Moment.now()))
check not task.isDeliveryTimedOut(MaxTime)
test "the timeout clock runs from admission, not message creation":
## A task that waited a long time for budget then just got admitted has a
## fresh clock — it is not reaped immediately on admission.
let task = taskWith(Opt.some(Moment.now()), Opt.none(Moment))
check task.admissionAge() < MaxTime
check not task.isDeliveryTimedOut(MaxTime)