Skip to content

Declarative TimeProvider override extension for time-travel testing #230

Description

@jeremydmiller

Summary

Add a first-class, declarative way to override the application's TimeProvider with a controllable one when building an AlbaHost, so time-sensitive endpoints can be tested deterministically (freeze "now", advance the clock, etc.).

Motivation

TimeProvider (net8+) is now the standard clock seam, and Microsoft.Extensions.TimeProvider.Testing.FakeTimeProvider is the standard controllable implementation. Lots of integration tests need to pin or advance time — token/session expiry, scheduling, cache TTLs, event-sourced aggregates, "valid until" business rules. Today an Alba user has to hand-roll the DI override and reach into the container to advance the clock.

This came out of the Bobcat spec-runner design (relative date/time tokens like TODAY/NOW and clock-control grammars that advance/freeze the clock between steps). Bobcat wants to optionally share one controllable TimeProvider between the spec and the app-under-test so freezing time makes both agree. That sharing is cleanest if Alba exposes the override natively rather than each consumer re-implementing it — and it's broadly useful to Alba users beyond Bobcat.

Proposed shape

An IAlbaExtension (consistent with the existing Configure(IHostBuilder) seam) that registers a controllable TimeProvider into the app DI and exposes control:

// register a controllable clock, optionally pinned to a start instant
var clock = new TestTimeProvider(startUtc: new DateTimeOffset(2026, 6, 5, 0, 0, 0, TimeSpan.Zero));
await using var host = await AlbaHost.For<Program>(clock);

// drive it from the test
clock.Advance(TimeSpan.FromDays(3));
clock.FreezeAt(someInstant);
  • Internally Configure(IHostBuilder) does services.AddSingleton<TimeProvider>(theControllableProvider) (replacing the default).
  • Could wrap FakeTimeProvider directly, or ship a thin Alba-owned controllable provider so consumers don't need the testing package transitively — open to either.
  • Bonus: expose the active TimeProvider off IAlbaHost for assertions.

Notes / open questions

  • Whether to depend on Microsoft.Extensions.TimeProvider.Testing vs. a small Alba-internal controllable provider.
  • Reset semantics between scenarios are the consumer's concern (Bobcat resets per scenario), but a Reset()/FreezeAt() surface helps.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions