AuroraMeter.Clock.Fixed (Aurora Meter v1.0.0)

View Source

A frozen clock for tests: one Agent holding one instant.

It ships in lib/ rather than test/support/ because host test suites need it, exactly as AuroraMeter.Test does. Drive it through AuroraMeter.Test.with_clock/2 and AuroraMeter.Test.travel/1,2 rather than starting it by hand: the helper serialises against other configuration changes, restores the previous clock: value on the way out (including on a raise) and stops the agent.

It is a single named agent, so it is global to the node: every test that installs it must be async: false.

All four readings come from the one frozen instant, db_now/0 included. That is the point of putting db_now/0 on the behaviour rather than leaving it as a bare query helper: a decision that consults the database's clock is exactly as testable as one that consults the node's, and a test can drive a cooldown or a retry horizon across its boundary in both directions without waiting.

monotonic_ms/0 moves in step with the frozen instant: travelling forward by 90 seconds moves it by exactly 90_000. It is an offset from the real monotonic reading taken when the agent started, not the frozen instant's epoch milliseconds, so it shares a number line with the real monotonic clock and a span measured across the end of a frozen block is still comparable with one measured outside it. A cache TTL, for example, does not come back with an expiry decades in the future.

Because db_now/0 is answered from the frozen instant, a test using this clock stops exercising the real query. AuroraMeter.ClockTest closes that gap deliberately with cases that call AuroraMeter.Clock.System.db_now/0.

Summary

Types

Any unit DateTime.add/3 accepts.

Functions

Moves the pinned instant by amount of unit (negative moves it back, which is how a test reproduces a backwards step deliberately).

Returns a specification to start this module under a supervisor.

Whether the fixed clock agent is running.

Pins the clock to instant.

Starts the agent. :instant pins it immediately; without one every read raises until set/1 is called.

Stops the agent. Safe to call when it is not running.

Types

unit()

@type unit() :: System.time_unit() | :day | :hour | :minute

Any unit DateTime.add/3 accepts.

Functions

advance(amount, unit)

@spec advance(integer(), unit()) :: :ok

Moves the pinned instant by amount of unit (negative moves it back, which is how a test reproduces a backwards step deliberately).

child_spec(arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

running?()

@spec running?() :: boolean()

Whether the fixed clock agent is running.

set(instant)

@spec set(DateTime.t()) :: :ok

Pins the clock to instant.

start_link(opts \\ [])

@spec start_link(keyword()) :: Agent.on_start()

Starts the agent. :instant pins it immediately; without one every read raises until set/1 is called.

stop()

@spec stop() :: :ok

Stops the agent. Safe to call when it is not running.