Schalter, Zeitfenster und Handoff-Prozent lagen im Usage-Monitor und mussten in SettingsModalViewModel nur load->save durchgeschleift werden, damit ein Settings-Speichern sie nicht zurueckwirft. Jetzt gehoeren sie dem General-Tab, der Monitor ist wieder nur Anzeige + Threshold-Drag, und "Jetzt aktualisieren" sitzt als Icon in der Titelleiste (neuer ModalShell.HeaderActions-Slot).
101 lines
3.6 KiB
C#
101 lines
3.6 KiB
C#
using ClaudeDo.Worker.Hub;
|
|
using ClaudeDo.Worker.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace ClaudeDo.Worker.Tests.Hub;
|
|
|
|
/// Settings rebuilds the whole AppSettings 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);
|
|
}
|
|
}
|