Files
ClaudeDo/tests/ClaudeDo.Ui.Tests/ViewModels/UsagePillViewModelTests.cs
T
mika kuns 677a4c1853 fix(ui-tests): install a real localizer in UsagePillViewModelTests
The pill tests asserted on real localized strings but never set Loc.Current,
which defaults to a key-echo localizer. They only passed because an unrelated
test class happened to install a real Localizer first; adding the usage-monitor
modal tests changed the ordering and the assertions started seeing raw keys.

Initialize Loc.Current in the constructor, matching every other UI test class.
2026-08-05 14:06:12 +02:00

144 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IO;
using ClaudeDo.Localization;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels;
using Xunit;
namespace ClaudeDo.Ui.Tests.ViewModels;
public class UsagePillViewModelTests
{
// Loc.Current is ambient static state defaulting to a key-echo localizer, so these
// assertions on real strings must install a real one instead of relying on whichever
// other test class happened to run first.
public UsagePillViewModelTests()
{
var dir = AppContext.BaseDirectory;
while (dir is not null && !Directory.Exists(Path.Combine(dir, "src", "ClaudeDo.Localization", "locales")))
dir = Path.GetDirectoryName(dir);
Loc.Current = new Localizer(
LocaleStore.Load(Path.Combine(dir!, "src", "ClaudeDo.Localization", "locales")), "en");
}
private sealed class FakeWorker : StubWorkerClient
{
public UsageSnapshotDto? InitialSnapshot;
public override Task<UsageSnapshotDto?> GetUsageSnapshotAsync() => Task.FromResult(InitialSnapshot);
}
private static UsageSnapshotDto Snapshot(
double? fiveHourPercent = null,
DateTimeOffset? fiveHourResetsAt = null,
double? sevenDayPercent = null,
DateTimeOffset? sevenDayResetsAt = null,
int fiveHourThresholdPct = 80,
int sevenDayThresholdPct = 90,
bool isGateBlocked = false,
string? gateReason = null,
DateTime? fetchedAtUtc = null,
bool isStale = false,
string? lastError = null)
=> new(
fiveHourPercent, fiveHourResetsAt, sevenDayPercent, sevenDayResetsAt,
Array.Empty<UsageLimitDto>(), fiveHourThresholdPct, sevenDayThresholdPct,
isGateBlocked, gateReason, fetchedAtUtc ?? DateTime.UtcNow, isStale, lastError);
// ── Text formatting ─────────────────────────────────────────────────────
[Fact]
public void Text_BothBucketsKnown_ShowsBoth()
{
var worker = new FakeWorker { InitialSnapshot = Snapshot(fiveHourPercent: 30, sevenDayPercent: 57) };
var vm = new UsagePillViewModel(worker);
Assert.Contains("5h", vm.Text);
Assert.Contains("30", vm.Text);
Assert.Contains("7d", vm.Text);
Assert.Contains("57", vm.Text);
}
[Fact]
public void Text_OnlyFiveHourKnown_ShowsOnlyFiveHour()
{
var worker = new FakeWorker { InitialSnapshot = Snapshot(fiveHourPercent: 42) };
var vm = new UsagePillViewModel(worker);
Assert.Contains("5h", vm.Text);
Assert.Contains("42", vm.Text);
Assert.DoesNotContain("7d", vm.Text);
}
[Fact]
public void Text_NoSnapshot_ShowsEmptyPlaceholder()
{
var worker = new FakeWorker();
var vm = new UsagePillViewModel(worker);
Assert.Equal("Usage ", vm.Text);
}
// ── IsWarn / IsBlocked threshold logic ──────────────────────────────────
[Fact]
public void IsWarn_BelowThresholdMinusTen_IsFalse()
{
var worker = new FakeWorker { InitialSnapshot = Snapshot(fiveHourPercent: 69, fiveHourThresholdPct: 80) };
var vm = new UsagePillViewModel(worker);
Assert.False(vm.IsWarn);
Assert.False(vm.IsBlocked);
}
[Fact]
public void IsWarn_AtThresholdMinusTen_IsTrue()
{
var worker = new FakeWorker { InitialSnapshot = Snapshot(fiveHourPercent: 70, fiveHourThresholdPct: 80) };
var vm = new UsagePillViewModel(worker);
Assert.True(vm.IsWarn);
Assert.False(vm.IsBlocked);
}
[Fact]
public void IsBlocked_WhenGateReportsBlocked_IsTrue()
{
var worker = new FakeWorker
{
InitialSnapshot = Snapshot(fiveHourPercent: 80, fiveHourThresholdPct: 80, isGateBlocked: true, gateReason: "5h-Limit 80% >= 80%")
};
var vm = new UsagePillViewModel(worker);
Assert.True(vm.IsBlocked);
}
// ── Stale ────────────────────────────────────────────────────────────────
[Fact]
public void Stale_Snapshot_SetsIsStale_AndTooltipMentionsStamp()
{
var fetchedAt = new DateTime(2026, 8, 5, 9, 41, 0, DateTimeKind.Utc);
var worker = new FakeWorker { InitialSnapshot = Snapshot(isStale: true, fetchedAtUtc: fetchedAt, lastError: "timeout") };
var vm = new UsagePillViewModel(worker);
Assert.True(vm.IsStale);
Assert.Contains(fetchedAt.ToLocalTime().ToString("HH:mm"), vm.Tooltip);
Assert.Contains("timeout", vm.Tooltip);
}
// ── UsageUpdated event ───────────────────────────────────────────────────
[Fact]
public void UsageUpdated_Event_UpdatesText()
{
var worker = new FakeWorker();
var vm = new UsagePillViewModel(worker);
Assert.Equal("Usage ", vm.Text);
worker.RaiseUsageUpdated(Snapshot(fiveHourPercent: 12));
Assert.Contains("12", vm.Text);
}
}