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
Summary
Add a first-class, declarative way to override the application's
TimeProviderwith a controllable one when building anAlbaHost, so time-sensitive endpoints can be tested deterministically (freeze "now", advance the clock, etc.).Motivation
TimeProvider(net8+) is now the standard clock seam, andMicrosoft.Extensions.TimeProvider.Testing.FakeTimeProvideris 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/NOWand clock-control grammars that advance/freeze the clock between steps). Bobcat wants to optionally share one controllableTimeProviderbetween 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 existingConfigure(IHostBuilder)seam) that registers a controllableTimeProviderinto the app DI and exposes control:Configure(IHostBuilder)doesservices.AddSingleton<TimeProvider>(theControllableProvider)(replacing the default).FakeTimeProviderdirectly, or ship a thin Alba-owned controllable provider so consumers don't need the testing package transitively — open to either.TimeProvideroffIAlbaHostfor assertions.Notes / open questions
Microsoft.Extensions.TimeProvider.Testingvs. a small Alba-internal controllable provider.Reset()/FreezeAt()surface helps.🤖 Generated with Claude Code