Skip to content

Commit 77c0417

Browse files
committed
fix: do not report a spent reminder as armed
A one-shot keeps its row after it fires, as completed, and the view returned it. A next-run lookup reported an old time rather than nothing, and an existence check refused to re-arm an alarm that could never fire again. Found by auditing against the JavaScript port, where the same defect was reported. Both sides drop completed rows now.
1 parent 0b9af48 commit 77c0417

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
- Leave a one-shot reminder that already fired out of `reminder` and
6+
`reminders`. Its row stays as `completed`, so a next-run lookup reported an
7+
old time rather than nothing, and an existence check refused to re-arm an
8+
alarm that could never fire again.
59
- Refuse an unknown operation in `reminder`, `reminders`, `unschedule`, and
610
`unschedule_all`. `schedule`
711
already raised `UnknownMessage` for one, so a typo cancelled nothing quietly

‎lib/solid_objects/actor.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def reminder_view
344344
def committed_reminders
345345
return {} unless instance_id
346346

347-
Reminder.where(instance_id:).each_with_object({}) do |row, view|
347+
Reminder.where(instance_id:).where.not(status: "completed").each_with_object({}) do |row, view|
348348
view[row.name] = reminder_status(
349349
name: row.name,
350350
operation: row.operation,

‎test/integration/reminder_cancellation_test.rb‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ def arm
137137
schedule(at: Time.utc(2030, 1, 1), every: 90).ping
138138
end
139139

140+
def arm_due
141+
schedule(at: 1.second.ago).ping
142+
end
143+
144+
def read_name
145+
self.seen = { "name" => reminder(:ping)&.name }
146+
end
147+
140148
def read_next_run
141149
found = reminder(:ping)
142150
self.seen = found && {
@@ -412,6 +420,26 @@ def state_of(actor_type)
412420
scheduler&.stop
413421
end
414422

423+
test "inspection does not report a one-shot that already fired" do
424+
reference = InspectorActor.ref("one")
425+
reference.async.arm_due
426+
drain
427+
reference.async.read_name
428+
drain
429+
430+
assert_equal "ping", state_of("cancel-inspector").fetch("seen").fetch("name")
431+
432+
scheduler = SolidObjects::ReminderScheduler.new
433+
scheduler.run_once
434+
drain
435+
reference.async.read_name
436+
drain
437+
438+
assert_nil state_of("cancel-inspector").fetch("seen").fetch("name")
439+
ensure
440+
scheduler&.stop
441+
end
442+
415443
test "inspection reports the next run time and interval" do
416444
reference = InspectorActor.ref("one")
417445
reference.async.arm

0 commit comments

Comments
 (0)