using ClaudeDo.Worker.Hub; using ClaudeDo.Worker.Tests.Infrastructure; using Xunit; namespace ClaudeDo.Worker.Tests.Hub; /// The PowerUse window and handoff threshold are edited on the usage monitor, but Settings→General /// rebuilds the whole DTO positionally on save. So the contract that matters is: whatever /// GetAppSettings hands out, feeding it straight back must change nothing. public sealed class PowerUseSettingsHubTests : IDisposable { private readonly DbFixture _db = new(); public void Dispose() => _db.Dispose(); private WorkerHub CreateHub() { var broadcaster = new HubBroadcaster(new CapturingHubContext()); var hub = new WorkerHub( null!, null!, null!, null!, broadcaster, _db.CreateFactory(), null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, null!, new ClaudeDo.Worker.Online.OnlineInboxConfig(), new ClaudeDo.Worker.OnlineRefreshTokenStore(new ClaudeDo.Worker.DpapiTokenStore("unused.token")), new ClaudeDo.Worker.Runner.PendingQuestionRegistry(), null!); hub.Clients = new FakeHubCallerClients(new RecordingClientProxy()); hub.Context = new FakeHubCallerContext(); return hub; } [Fact] public async Task GetAppSettings_Defaults_HaveNoWindowAnd95Percent() { var settings = await CreateHub().GetAppSettings(); Assert.Null(settings.PowerUseWindowStart); Assert.Null(settings.PowerUseWindowEnd); Assert.Equal(95, settings.PowerUseHandoffPct); } [Fact] public async Task UpdateAppSettings_RoundTripsTheWindowAndThreshold() { var hub = CreateHub(); var before = await hub.GetAppSettings(); await hub.UpdateAppSettings(before with { PowerUseEnabled = true, PowerUseWindowStart = "22:00", PowerUseWindowEnd = "07:00", PowerUseHandoffPct = 88, }); var after = await hub.GetAppSettings(); Assert.True(after.PowerUseEnabled); Assert.Equal("22:00", after.PowerUseWindowStart); Assert.Equal("07:00", after.PowerUseWindowEnd); Assert.Equal(88, after.PowerUseHandoffPct); } /// The Settings→General case: a save that touches none of the PowerUse fields must leave them /// exactly as they were. [Fact] public async Task UpdateAppSettings_UnchangedDto_LeavesPowerUseFieldsAlone() { var hub = CreateHub(); await hub.UpdateAppSettings((await hub.GetAppSettings()) with { PowerUseEnabled = true, PowerUseWindowStart = "22:00", PowerUseWindowEnd = "07:00", PowerUseHandoffPct = 88, }); var stored = await hub.GetAppSettings(); await hub.UpdateAppSettings(stored); var after = await hub.GetAppSettings(); Assert.Equal(stored.PowerUseEnabled, after.PowerUseEnabled); Assert.Equal(stored.PowerUseWindowStart, after.PowerUseWindowStart); Assert.Equal(stored.PowerUseWindowEnd, after.PowerUseWindowEnd); Assert.Equal(stored.PowerUseHandoffPct, after.PowerUseHandoffPct); } [Fact] public async Task UpdateAppSettings_InvalidTimeAndOutOfRangePct_AreNormalized() { var hub = CreateHub(); await hub.UpdateAppSettings((await hub.GetAppSettings()) with { PowerUseWindowStart = "25:99", PowerUseWindowEnd = "07:00", PowerUseHandoffPct = 120, }); var after = await hub.GetAppSettings(); Assert.Null(after.PowerUseWindowStart); Assert.Equal("07:00", after.PowerUseWindowEnd); Assert.Equal(99, after.PowerUseHandoffPct); } }