Documentation
¶
Overview ¶
Package clock provides a pluggable Now() time source so the mock's protocol-time outputs (JWT iat/exp, code/OTP TTLs, bearer validation) can be frozen or skewed at runtime via /admin0/clock for deterministic tests.
Wall-clock uses (request logging, middleware latency, http.Server timeouts) deliberately keep using time.Now directly — log timestamps should not lie because protocol time was frozen.
Index ¶
- Variables
- type Clock
- type Controlled
- func (c *Controlled) Advance(d time.Duration) error
- func (c *Controlled) ConfiguredOffset() (time.Duration, bool)
- func (c *Controlled) Freeze(t time.Time)
- func (c *Controlled) Now() time.Time
- func (c *Controlled) Offset(d time.Duration)
- func (c *Controlled) Reset()
- func (c *Controlled) Snapshot() (mode Mode, now time.Time, offset time.Duration, hasOffset bool)
- func (c *Controlled) State() (Mode, time.Time)
- type Mode
- type Real
Constants ¶
This section is empty.
Variables ¶
var ErrAdvanceInRealMode = errors.New("clock: cannot advance while in real mode (Freeze or Offset first)")
ErrAdvanceInRealMode is returned by Advance when the clock is in real mode. There's nothing to advance against; the caller should Freeze or Offset first.
Functions ¶
This section is empty.
Types ¶
type Clock ¶
Clock is the single read-side primitive the mock's handlers depend on. One method keeps the surface trivial to stub.
type Controlled ¶
type Controlled struct {
// contains filtered or unexported fields
}
Controlled is a mutable Clock the admin0 surface and the SDK drive. Safe for concurrent use; all reads take an RLock, all mutations take the write lock.
func NewControlled ¶
func NewControlled() *Controlled
NewControlled returns a Controlled in real mode.
func (*Controlled) Advance ¶
func (c *Controlled) Advance(d time.Duration) error
Advance mutates the held value by d. In frozen mode the pinned instant moves by d; in offset mode the offset grows by d; in real mode the call is a no-op and returns ErrAdvanceInRealMode.
func (*Controlled) ConfiguredOffset ¶
func (c *Controlled) ConfiguredOffset() (time.Duration, bool)
ConfiguredOffset returns the configured offset and true when the clock is in offset mode; (0, false) otherwise. Used by callers that want the raw offset without the resolved Now (Snapshot is the better choice for callers that need both).
func (*Controlled) Freeze ¶
func (c *Controlled) Freeze(t time.Time)
Freeze pins Now to t until the next mode change. Also zeroes the offset field even though Now never reads it in frozen mode — a future method that touches offset without rechecking mode (e.g. a "scale offset by N" helper) shouldn't see leftover state from a prior Offset() call.
func (*Controlled) Now ¶
func (c *Controlled) Now() time.Time
Now returns the current effective time given the mode.
func (*Controlled) Offset ¶
func (c *Controlled) Offset(d time.Duration)
Offset switches to offset mode with the given skew. The wall clock keeps ticking; Now returns time.Now() + d. Also zeroes the pinned field for the same future-safety reason as Freeze.
func (*Controlled) Reset ¶
func (c *Controlled) Reset()
Reset returns the clock to real mode and clears any held state.
func (*Controlled) Snapshot ¶
Snapshot returns (mode, now, offset, hasOffset) under a single RLock so callers get a consistent view. `hasOffset` is true only when `mode == ModeOffset`; in that case `offset` is the configured skew.
Prefer Snapshot over composing State + ConfiguredOffset when both values are needed: a concurrent Freeze/Offset call between the two accessor calls would otherwise produce an inconsistent picture (e.g. mode=frozen but hasOffset=true).