Description
Config.Test.Time was introduced in #754 (following #751) to let tests provide a synthetic rivertype.TimeGenerator. Scheduled-job eligibility uses that configured time source, but JobSnooze calculates its next absolute timestamp from the process wall clock:
nextAttemptScheduledAt := time.Now().Add(snoozeErr.Duration)
The executor then compares that wall-clock-derived timestamp with e.Time.Now(). When Config.Test.Time is intentionally ahead of or behind wall time, the two values belong to different timelines.
This is present in v0.39.0 and still appears on master.
Example
Assume:
- process wall clock:
2026-07-22 12:00 UTC
Config.Test.Time: 2026-06-10 12:00 UTC
- worker returns
river.JobSnooze(8 * time.Hour)
The current implementation stores approximately:
scheduled_at = 2026-07-22 20:00 UTC
The expected synthetic-time value is:
scheduled_at = 2026-06-10 20:00 UTC
Advancing the synthetic clock by eight hours therefore does not make the job eligible; it remains scheduled until the wall-clock-derived timestamp. If the synthetic clock is ahead of wall time, the inverse can happen and the snoozed job may become immediately eligible.
This makes Config.Test.Time unreliable for behavioral or manual simulation tests that exercise workers which repeatedly snooze themselves.
Suggested change
Use the executor's configured time generator when calculating the snooze target:
- nextAttemptScheduledAt := time.Now().Add(snoozeErr.Duration)
+ nextAttemptScheduledAt := e.Time.Now().Add(snoozeErr.Duration)
This is also consistent with the comparison immediately below, which already uses e.Time.Now().
When Config.Test.Time is nil, River's default time generator still delegates to time.Now(), so ordinary runtime behavior should remain unchanged.
Suggested regression test
Create a client/worker with Config.Test.Time fixed to an instant that differs materially from wall time, return JobSnooze(duration), and assert that the resulting job's ScheduledAt equals the synthetic instant plus the snooze duration.
Description
Config.Test.Timewas introduced in #754 (following #751) to let tests provide a syntheticrivertype.TimeGenerator. Scheduled-job eligibility uses that configured time source, butJobSnoozecalculates its next absolute timestamp from the process wall clock:The executor then compares that wall-clock-derived timestamp with
e.Time.Now(). WhenConfig.Test.Timeis intentionally ahead of or behind wall time, the two values belong to different timelines.This is present in v0.39.0 and still appears on
master.Example
Assume:
2026-07-22 12:00 UTCConfig.Test.Time:2026-06-10 12:00 UTCriver.JobSnooze(8 * time.Hour)The current implementation stores approximately:
The expected synthetic-time value is:
Advancing the synthetic clock by eight hours therefore does not make the job eligible; it remains scheduled until the wall-clock-derived timestamp. If the synthetic clock is ahead of wall time, the inverse can happen and the snoozed job may become immediately eligible.
This makes
Config.Test.Timeunreliable for behavioral or manual simulation tests that exercise workers which repeatedly snooze themselves.Suggested change
Use the executor's configured time generator when calculating the snooze target:
This is also consistent with the comparison immediately below, which already uses
e.Time.Now().When
Config.Test.Timeis nil, River's default time generator still delegates totime.Now(), so ordinary runtime behavior should remain unchanged.Suggested regression test
Create a client/worker with
Config.Test.Timefixed to an instant that differs materially from wall time, returnJobSnooze(duration), and assert that the resulting job'sScheduledAtequals the synthetic instant plus the snooze duration.