using ClaudeDo.Worker.Usage.TokenTracker; namespace ClaudeDo.Worker.Tests.Usage.TokenTracker; public sealed class TokenTrackerStateTests { private static TokenTrackerExport Export(DateTime fetchedAt) => new(11, [], fetchedAt); [Fact] public void FreshState_IsEmpty() { var state = new TokenTrackerState(); Assert.Null(state.Export); Assert.Null(state.LastAttemptUtc); Assert.Null(state.LastError); } [Fact] public void ReportSuccess_StoresExportAndClearsError() { var state = new TokenTrackerState(); var at = new DateTime(2026, 8, 24, 9, 0, 0, DateTimeKind.Utc); state.ReportFailure("boom", at.AddMinutes(-1)); state.ReportSuccess(Export(at)); Assert.NotNull(state.Export); Assert.Equal(at, state.LastAttemptUtc); Assert.Null(state.LastError); } [Fact] public void ReportFailure_KeepsPreviousExport() { var state = new TokenTrackerState(); var good = new DateTime(2026, 8, 24, 9, 0, 0, DateTimeKind.Utc); state.ReportSuccess(Export(good)); state.ReportFailure("tokentracker exited with code 1", good.AddMinutes(30)); Assert.NotNull(state.Export); Assert.Equal(good, state.Export!.FetchedAtUtc); Assert.Equal("tokentracker exited with code 1", state.LastError); Assert.Equal(good.AddMinutes(30), state.LastAttemptUtc); } [Fact] public void IsOlderThan_NoExport_IsTrue() { Assert.True(new TokenTrackerState().IsOlderThan(TimeSpan.FromMinutes(15), new DateTime(2026, 8, 24, 9, 0, 0, DateTimeKind.Utc))); } [Fact] public void IsOlderThan_ComparesAgainstFetchTime() { var state = new TokenTrackerState(); var fetched = new DateTime(2026, 8, 24, 9, 0, 0, DateTimeKind.Utc); state.ReportSuccess(Export(fetched)); Assert.False(state.IsOlderThan(TimeSpan.FromMinutes(15), fetched.AddMinutes(14))); Assert.True(state.IsOlderThan(TimeSpan.FromMinutes(15), fetched.AddMinutes(16))); } }