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

207 lines
7.4 KiB
Swift

import AppKit
import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
struct StatusItemControllerMenuTests {
@MainActor
private final class RecordingUpdater: UpdaterProviding {
var automaticallyChecksForUpdates = false
var automaticallyDownloadsUpdates = false
let isAvailable = true
let unavailableReason: String? = nil
let updateStatus = UpdateStatus(isUpdateReady: true)
var checkForUpdatesCount = 0
var installUpdateCount = 0
func checkForUpdates(_ sender: Any?) {
_ = sender
self.checkForUpdatesCount += 1
}
func installUpdate() {
self.installUpdateCount += 1
}
}
private func makeSnapshot(
primary: RateWindow?,
secondary: RateWindow?,
tertiary: RateWindow? = nil,
providerCost: ProviderCostSnapshot? = nil)
-> UsageSnapshot
{
UsageSnapshot(
primary: primary,
secondary: secondary,
tertiary: tertiary,
providerCost: providerCost,
updatedAt: Date())
}
@Test
func `cursor switcher falls back to on demand budget when plan exhausted and showing remaining`() {
let primary = RateWindow(usedPercent: 100, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let secondary = RateWindow(usedPercent: 36, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let providerCost = ProviderCostSnapshot(
used: 12,
limit: 200,
currencyCode: "USD",
updatedAt: Date())
let snapshot = self.makeSnapshot(primary: primary, secondary: secondary, providerCost: providerCost)
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .cursor,
snapshot: snapshot,
showUsed: false)
#expect(percent == 94)
}
@Test
func `cursor switcher uses primary when showing used`() {
let primary = RateWindow(usedPercent: 100, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let secondary = RateWindow(usedPercent: 36, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let snapshot = self.makeSnapshot(primary: primary, secondary: secondary)
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .cursor,
snapshot: snapshot,
showUsed: true)
#expect(percent == 100)
}
@Test
func `cursor switcher keeps primary when remaining is positive`() {
let primary = RateWindow(usedPercent: 20, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let secondary = RateWindow(usedPercent: 40, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let snapshot = self.makeSnapshot(primary: primary, secondary: secondary)
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .cursor,
snapshot: snapshot,
showUsed: false)
#expect(percent == 80)
}
@Test
func `cursor switcher does not treat auto lane as extra remaining quota`() {
let primary = RateWindow(usedPercent: 100, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let secondary = RateWindow(usedPercent: 36, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let snapshot = self.makeSnapshot(primary: primary, secondary: secondary)
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .cursor,
snapshot: snapshot,
showUsed: false)
#expect(percent == 0)
}
@Test
func `perplexity switcher falls back after recurring credits are exhausted`() {
let primary = RateWindow(usedPercent: 100, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let secondary = RateWindow(usedPercent: 100, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let tertiary = RateWindow(usedPercent: 24, windowMinutes: nil, resetsAt: nil, resetDescription: nil)
let snapshot = self.makeSnapshot(primary: primary, secondary: secondary, tertiary: tertiary)
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .perplexity,
snapshot: snapshot,
showUsed: false)
#expect(percent == 76)
}
@Test
func `mistral switcher uses monthly plan metric when selected`() {
let snapshot = UsageSnapshot(
primary: nil,
secondary: nil,
extraRateWindows: [
NamedRateWindow(
id: "mistral-monthly-plan",
title: "Monthly Plan",
window: RateWindow(usedPercent: 42, windowMinutes: nil, resetsAt: nil, resetDescription: nil)),
],
updatedAt: Date())
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .mistral,
snapshot: snapshot,
showUsed: true,
preference: .monthlyPlan)
#expect(percent == 42)
}
@Test
func `mistral switcher ignores pay as you go balance primary`() {
let snapshot = UsageSnapshot(
primary: RateWindow(
usedPercent: 0,
windowMinutes: nil,
resetsAt: nil,
resetDescription: "$12.50"),
secondary: nil,
updatedAt: Date())
let percent = StatusItemController.switcherWeeklyMetricPercent(
for: .mistral,
snapshot: snapshot,
showUsed: true,
preference: .automatic)
#expect(percent == nil)
}
@Test
@MainActor
func `menu card width stays at base width when menu accessories are present`() {
let shortcutMenu = NSMenu()
let refreshItem = NSMenuItem(title: "Refresh", action: nil, keyEquivalent: "r")
shortcutMenu.addItem(refreshItem)
#expect(ceil(shortcutMenu.size.width) < 310)
let submenuMenu = NSMenu()
let parentItem = NSMenuItem(title: "Session", action: nil, keyEquivalent: "")
parentItem.submenu = NSMenu(title: "Session")
submenuMenu.addItem(parentItem)
#expect(ceil(submenuMenu.size.width) < 310)
}
@Test
@MainActor
func `update menu action installs prepared update instead of checking again`() throws {
let suite = "StatusItemControllerMenuTests-\(UUID().uuidString)"
let defaults = try #require(UserDefaults(suiteName: suite))
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.statusChecksEnabled = false
settings.refreshFrequency = .manual
let fetcher = UsageFetcher()
let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)
let updater = RecordingUpdater()
let controller = StatusItemController(
store: store,
settings: settings,
account: fetcher.loadAccountInfo(),
updater: updater,
preferencesSelection: PreferencesSelection(),
statusBar: .system)
controller.installUpdate()
#expect(updater.installUpdateCount == 1)
#expect(updater.checkForUpdatesCount == 0)
}
}