579 lines · 23.9 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Testing@testable import CodexBar @MainActor@Suitestruct StatusMenuTests {    private func disableMenuCardsForTesting() {        StatusItemController.menuCardRenderingEnabled = false        StatusItemController.menuRefreshEnabled = false    }     private func makeStatusBarForTesting() -> NSStatusBar {        let env = ProcessInfo.processInfo.environment        if env["GITHUB_ACTIONS"] == "true" || env["CI"] == "true" {            return .system        }        return NSStatusBar()    }     private func makeSettings() -> SettingsStore {        let suite = "StatusMenuTests-\(UUID().uuidString)"        let defaults = UserDefaults(suiteName: suite)!        defaults.removePersistentDomain(forName: suite)        let configStore = testConfigStore(suiteName: suite)        return SettingsStore(            userDefaults: defaults,            configStore: configStore,            zaiTokenStore: NoopZaiTokenStore(),            syntheticTokenStore: NoopSyntheticTokenStore())    }     @Test    func remembersProviderWhenMenuOpens() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: false)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: true)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         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())         let claudeMenu = controller.makeMenu()        controller.menuWillOpen(claudeMenu)        #expect(controller.lastMenuProvider == .claude)         // No providers enabled: fall back to Codex.        for provider in UsageProvider.allCases {            if let meta = registry.metadata[provider] {                settings.setProviderEnabled(provider: provider, metadata: meta, enabled: false)            }        }        let unmappedMenu = controller.makeMenu()        controller.menuWillOpen(unmappedMenu)        #expect(controller.lastMenuProvider == .codex)    }     @Test    func mergedMenuOpenDoesNotPersistResolvedProviderWhenSelectionIsNil() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = nil         let registry = ProviderRegistry.shared        var enabledProviders: [UsageProvider] = []        for provider in UsageProvider.allCases {            guard let metadata = registry.metadata[provider] else { continue }            let shouldEnable = enabledProviders.count < 2            settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: shouldEnable)            if shouldEnable {                enabledProviders.append(provider)            }        }        #expect(enabledProviders.count == 2)         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())         let expectedResolved = store.enabledProviders().first ?? .codex        #expect(store.enabledProviders().count > 1)        #expect(controller.shouldMergeIcons == true)        let menu = controller.makeMenu()        #expect(settings.selectedMenuProvider == nil)        controller.menuWillOpen(menu)        #expect(settings.selectedMenuProvider == nil)        #expect(controller.lastMenuProvider == expectedResolved)    }     @Test    func mergedMenuRefreshUsesResolvedEnabledProviderWhenPersistedSelectionIsDisabled() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .codex         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: false)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: true)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: true)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)        let event = CreditEvent(date: Date(), service: "CLI", creditsUsed: 1)        let breakdown = OpenAIDashboardSnapshot.makeDailyBreakdown(from: [event], maxDays: 30)        store.openAIDashboard = OpenAIDashboardSnapshot(            signedInEmail: "user@example.com",            codeReviewRemainingPercent: 100,            creditEvents: [event],            dailyBreakdown: breakdown,            usageBreakdown: breakdown,            creditsPurchaseURL: nil,            updatedAt: Date())        let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: self.makeStatusBarForTesting())         let expectedResolved = store.enabledProviders().first ?? .codex        #expect(store.enabledProviders().count > 1)        #expect(controller.shouldMergeIcons == true)         func hasOpenAIWebSubmenus(_ menu: NSMenu) -> Bool {            let usageItem = menu.items.first { ($0.representedObject as? String) == "menuCardUsage" }            let creditsItem = menu.items.first { ($0.representedObject as? String) == "menuCardCredits" }            let hasUsageBreakdown = usageItem?.submenu?.items                .contains { ($0.representedObject as? String) == "usageBreakdownChart" } == true            let hasCreditsHistory = creditsItem?.submenu?.items                .contains { ($0.representedObject as? String) == "creditsHistoryChart" } == true            return hasUsageBreakdown || hasCreditsHistory        }         let menu = controller.makeMenu()        controller.menuWillOpen(menu)         #expect(controller.lastMenuProvider == expectedResolved)        #expect(settings.selectedMenuProvider == .codex)        #expect(hasOpenAIWebSubmenus(menu) == false)         controller.menuContentVersion &+= 1        controller.refreshOpenMenusIfNeeded()         #expect(hasOpenAIWebSubmenus(menu) == false)    }     @Test    func openMergedMenuRebuildsSwitcherWhenUsageBarsModeChanges() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .codex        settings.usageBarsShowUsed = false         let registry = ProviderRegistry.shared        for provider in UsageProvider.allCases {            guard let metadata = registry.metadata[provider] else { continue }            let shouldEnable = provider == .codex || provider == .claude            settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: shouldEnable)        }         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())         #expect(store.enabledProviders().count == 2)        #expect(controller.shouldMergeIcons == true)         let menu = controller.makeMenu()        controller.menuWillOpen(menu)         let initialSwitcher = menu.items.first?.view as? ProviderSwitcherView        #expect(initialSwitcher != nil)        let initialSwitcherID = initialSwitcher.map(ObjectIdentifier.init)         settings.usageBarsShowUsed = true        controller.handleProviderConfigChange(reason: "usageBarsShowUsed")         let updatedSwitcher = menu.items.first?.view as? ProviderSwitcherView        #expect(updatedSwitcher != nil)        if let initialSwitcherID, let updatedSwitcher {            #expect(initialSwitcherID != ObjectIdentifier(updatedSwitcher))        }    }     @Test    func providerToggleUpdatesStatusItemVisibility() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = false        settings.providerDetectionCompleted = true         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: true)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: true)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         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())         #expect(controller.statusItems[.claude]?.isVisible == true)         if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: false)        }        controller.handleProviderConfigChange(reason: "test")        #expect(controller.statusItems[.claude]?.isVisible == false)    }     @Test    func hidesOpenAIWebSubmenusWhenNoHistory() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .codex         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: true)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: false)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)        store.openAIDashboard = OpenAIDashboardSnapshot(            signedInEmail: "user@example.com",            codeReviewRemainingPercent: 100,            creditEvents: [],            dailyBreakdown: [],            usageBreakdown: [],            creditsPurchaseURL: nil,            updatedAt: Date())         let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: self.makeStatusBarForTesting())         let menu = controller.makeMenu()        controller.menuWillOpen(menu)        let titles = Set(menu.items.map(\.title))        #expect(!titles.contains("Credits history"))        #expect(!titles.contains("Usage breakdown"))    }     @Test    func showsOpenAIWebSubmenusWhenHistoryExists() throws {        self.disableMenuCardsForTesting()        let settings = SettingsStore(            configStore: testConfigStore(suiteName: "StatusMenuTests-history"),            zaiTokenStore: NoopZaiTokenStore(),            syntheticTokenStore: NoopSyntheticTokenStore())        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .codex         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: true)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: false)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)         let calendar = Calendar(identifier: .gregorian)        var components = DateComponents()        components.calendar = calendar        components.timeZone = TimeZone(secondsFromGMT: 0)        components.year = 2025        components.month = 12        components.day = 18        let date = try #require(components.date)         let events = [CreditEvent(date: date, service: "CLI", creditsUsed: 1)]        let breakdown = OpenAIDashboardSnapshot.makeDailyBreakdown(from: events, maxDays: 30)        store.openAIDashboard = OpenAIDashboardSnapshot(            signedInEmail: "user@example.com",            codeReviewRemainingPercent: 100,            creditEvents: events,            dailyBreakdown: breakdown,            usageBreakdown: breakdown,            creditsPurchaseURL: nil,            updatedAt: Date())         let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: self.makeStatusBarForTesting())         let menu = controller.makeMenu()        controller.menuWillOpen(menu)        let usageItem = menu.items.first { ($0.representedObject as? String) == "menuCardUsage" }        let creditsItem = menu.items.first { ($0.representedObject as? String) == "menuCardCredits" }        #expect(            usageItem?.submenu?.items                .contains { ($0.representedObject as? String) == "usageBreakdownChart" } == true)        #expect(            creditsItem?.submenu?.items                .contains { ($0.representedObject as? String) == "creditsHistoryChart" } == true)    }     @Test    func showsCreditsBeforeCostInCodexMenuCardSections() throws {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .codex        settings.costUsageEnabled = true         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: true)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: false)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)        store.credits = CreditsSnapshot(remaining: 100, events: [], updatedAt: Date())        store.openAIDashboard = OpenAIDashboardSnapshot(            signedInEmail: "user@example.com",            codeReviewRemainingPercent: 100,            creditEvents: [],            dailyBreakdown: [],            usageBreakdown: [],            creditsPurchaseURL: nil,            updatedAt: Date())        store._setTokenSnapshotForTesting(CostUsageTokenSnapshot(            sessionTokens: 123,            sessionCostUSD: 0.12,            last30DaysTokens: 123,            last30DaysCostUSD: 1.23,            daily: [                CostUsageDailyReport.Entry(                    date: "2025-12-23",                    inputTokens: nil,                    outputTokens: nil,                    totalTokens: 123,                    costUSD: 1.23,                    modelsUsed: nil,                    modelBreakdowns: nil),            ],            updatedAt: Date()), provider: .codex)         let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: self.makeStatusBarForTesting())         let menu = controller.makeMenu()        controller.menuWillOpen(menu)        let ids = menu.items.compactMap { $0.representedObject as? String }        let creditsIndex = ids.firstIndex(of: "menuCardCredits")        let costIndex = ids.firstIndex(of: "menuCardCost")        #expect(creditsIndex != nil)        #expect(costIndex != nil)        #expect(try #require(creditsIndex) < costIndex!)    }     @Test    func showsExtraUsageForClaudeWhenUsingMenuCardSections() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .claude        settings.costUsageEnabled = true        settings.claudeWebExtrasEnabled = true         let registry = ProviderRegistry.shared        if let codexMeta = registry.metadata[.codex] {            settings.setProviderEnabled(provider: .codex, metadata: codexMeta, enabled: false)        }        if let claudeMeta = registry.metadata[.claude] {            settings.setProviderEnabled(provider: .claude, metadata: claudeMeta, enabled: true)        }        if let geminiMeta = registry.metadata[.gemini] {            settings.setProviderEnabled(provider: .gemini, metadata: geminiMeta, enabled: false)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)        let identity = ProviderIdentitySnapshot(            providerID: .claude,            accountEmail: "user@example.com",            accountOrganization: nil,            loginMethod: "web")        let snapshot = UsageSnapshot(            primary: RateWindow(usedPercent: 10, windowMinutes: nil, resetsAt: nil, resetDescription: "Resets soon"),            secondary: nil,            tertiary: nil,            providerCost: ProviderCostSnapshot(                used: 0,                limit: 2000,                currencyCode: "EUR",                period: "Monthly",                resetsAt: nil,                updatedAt: Date()),            updatedAt: Date(),            identity: identity)        store._setSnapshotForTesting(snapshot, provider: .claude)        store._setTokenSnapshotForTesting(CostUsageTokenSnapshot(            sessionTokens: 123,            sessionCostUSD: 0.12,            last30DaysTokens: 123,            last30DaysCostUSD: 1.23,            daily: [                CostUsageDailyReport.Entry(

Lines 1–500 of 579.