Files
steipete--codexbar/Tests/CodexBarTests/StatusItemCombinedMetricPlaceholderTests.swift
2026-07-13 12:22:33 +08:00

185 lines
8.2 KiB
Swift

import AppKit
import CodexBarCore
import Testing
@testable import CodexBar
/// Regression coverage for the combined "Session + Weekly" menu-bar metric ignoring Claude web's
/// synthetic `five_hour: null` session placeholder. Claude web parses an account with no live session
/// (but a real `seven_day` lane) into a 0% 5-hour `primary` with no reset signal; the combined metric
/// must drop that placeholder so the readout falls back to the weekly lane instead of rendering a
/// non-existent `5h 0%`/`5h 100%` session. A genuine, freshly reset session (which still carries a
/// `resetsAt`) must survive the filter.
@MainActor
@Suite(.serialized)
struct StatusItemCombinedMetricPlaceholderTests {
private func makeStatusBarForTesting() -> NSStatusBar {
// Use the real system status bar in tests. Standalone NSStatusBar instances have caused
// AppKit teardown crashes under swiftpm-testing-helper.
.system
}
/// Builds a Claude-only controller with the combined Session + Weekly metric selected.
private func makeClaudeCombinedController(
suiteName: String,
displayMode: MenuBarDisplayMode,
showUsed: Bool) -> (controller: StatusItemController, store: UsageStore)
{
let settings = SettingsStore(
configStore: testConfigStore(suiteName: suiteName),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.refreshFrequency = .manual
settings.mergeIcons = true
settings.selectedMenuProvider = .claude
settings.menuBarDisplayMode = displayMode
settings.usageBarsShowUsed = showUsed
settings.setMenuBarMetricPreference(.primaryAndSecondary, for: .claude)
let registry = ProviderRegistry.shared
if let claudeMeta = registry.metadata[.claude] {
settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: true)
}
let fetcher = UsageFetcher()
let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)
let controller = StatusItemController(
store: store,
settings: settings,
account: fetcher.loadAccountInfo(),
updater: DisabledUpdaterController(),
preferencesSelection: PreferencesSelection(),
statusBar: self.makeStatusBarForTesting())
return (controller, store)
}
@Test
func `combined metric ignores the claude web null-session placeholder in percent mode`() {
let (controller, store) = self.makeClaudeCombinedController(
suiteName: "StatusItemCombinedMetricPlaceholderTests-percent",
displayMode: .percent,
showUsed: true)
defer { controller.releaseStatusItemsForTesting() }
let now = Date()
// `primary` is the synthetic placeholder Claude web emits for `five_hour: null`: a 0% 5h window
// flagged `isSyntheticPlaceholder`. `secondary` is the real weekly lane.
let snapshot = UsageSnapshot(
primary: RateWindow(
usedPercent: 0,
windowMinutes: 300,
resetsAt: nil,
resetDescription: nil,
isSyntheticPlaceholder: true),
secondary: RateWindow(
usedPercent: 42,
windowMinutes: 7 * 24 * 60,
resetsAt: now.addingTimeInterval(24 * 60 * 60),
resetDescription: nil),
updatedAt: now)
store._setSnapshotForTesting(snapshot, provider: .claude)
store._setErrorForTesting(nil, provider: .claude)
let displayText = controller.menuBarDisplayText(for: .claude, snapshot: snapshot)
// Weekly lane only — the placeholder session is dropped (no "5h" component).
#expect(displayText == "W 42%")
#expect(displayText?.contains("5h") == false)
}
@Test
func `combined metric ignores the claude web null-session placeholder in both mode`() {
let (controller, store) = self.makeClaudeCombinedController(
suiteName: "StatusItemCombinedMetricPlaceholderTests-both",
displayMode: .both,
showUsed: false)
defer { controller.releaseStatusItemsForTesting() }
let now = Date()
let snapshot = UsageSnapshot(
primary: RateWindow(
usedPercent: 0,
windowMinutes: 300,
resetsAt: nil,
resetDescription: nil,
isSyntheticPlaceholder: true),
secondary: RateWindow(
usedPercent: 42,
windowMinutes: 7 * 24 * 60,
resetsAt: now.addingTimeInterval(60 * 60),
resetDescription: nil),
updatedAt: now)
store._setSnapshotForTesting(snapshot, provider: .claude)
store._setErrorForTesting(nil, provider: .claude)
let displayText = controller.menuBarDisplayText(for: .claude, snapshot: snapshot)
// Percent comes from the weekly lane (58% remaining), not the placeholder session's 100%.
#expect(displayText?.hasPrefix("58%") == true)
#expect(displayText?.hasPrefix("100%") == false)
// The placeholder session lane never surfaces, so no "5h" label appears.
#expect(displayText?.contains("5h") == false)
}
@Test
func `combined metric keeps a real freshly reset claude session lane`() {
let (controller, store) = self.makeClaudeCombinedController(
suiteName: "StatusItemCombinedMetricPlaceholderTests-fresh",
displayMode: .percent,
showUsed: true)
defer { controller.releaseStatusItemsForTesting() }
let now = Date()
// A real, freshly reset session: 0% used but with a concrete reset time — unlike the placeholder.
let snapshot = UsageSnapshot(
primary: RateWindow(
usedPercent: 0,
windowMinutes: 300,
resetsAt: now.addingTimeInterval(4 * 60 * 60),
resetDescription: nil),
secondary: RateWindow(
usedPercent: 42,
windowMinutes: 7 * 24 * 60,
resetsAt: now.addingTimeInterval(24 * 60 * 60),
resetDescription: nil),
updatedAt: now)
store._setSnapshotForTesting(snapshot, provider: .claude)
store._setErrorForTesting(nil, provider: .claude)
let displayText = controller.menuBarDisplayText(for: .claude, snapshot: snapshot)
// The real session lane survives the filter, so both lanes render.
#expect(displayText == "5h 0% · W 42%")
}
@Test
func `combined metric keeps an unflagged zero-usage session sharing the placeholder shape`() {
// Precision guard: a real empty session can share the placeholder's exact RateWindow shape
// (0% used, 5h cadence, no reset) — e.g. the Claude CLI scrape, where session reset text can be
// absent. Because the drop keys on the explicit `isSyntheticPlaceholder` marker (set only at the
// Claude web boundary) rather than the shape, this unflagged session must be kept, not dropped.
let (controller, store) = self.makeClaudeCombinedController(
suiteName: "StatusItemCombinedMetricPlaceholderTests-unflagged-shape",
displayMode: .percent,
showUsed: true)
defer { controller.releaseStatusItemsForTesting() }
let now = Date()
let snapshot = UsageSnapshot(
primary: RateWindow(usedPercent: 0, windowMinutes: 300, resetsAt: nil, resetDescription: nil),
secondary: RateWindow(
usedPercent: 42,
windowMinutes: 7 * 24 * 60,
resetsAt: now.addingTimeInterval(24 * 60 * 60),
resetDescription: nil),
updatedAt: now)
store._setSnapshotForTesting(snapshot, provider: .claude)
store._setErrorForTesting(nil, provider: .claude)
let displayText = controller.menuBarDisplayText(for: .claude, snapshot: snapshot)
// Unflagged session is real, so it renders despite matching the placeholder shape.
#expect(displayText == "5h 0% · W 42%")
}
}