Peter Steinbergerfix: stabilize menu tests05f5a0b9 months agoHistory
416 lines · 16.7 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport Testing@testable import CodexBar @MainActor@Suitestruct StatusMenuTests {    private func disableMenuCardsForTesting() {        StatusItemController.menuCardRenderingEnabled = false        StatusItemController.menuRefreshEnabled = false    }     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: NSStatusBar())         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 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: NSStatusBar())         #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: NSStatusBar())         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() {        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 = 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: NSStatusBar())         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() {        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: NSStatusBar())         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(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(                    date: "2025-12-23",                    inputTokens: nil,                    outputTokens: nil,                    totalTokens: 123,                    costUSD: 1.23,                    modelsUsed: nil,                    modelBreakdowns: nil),            ],            updatedAt: Date()), provider: .claude)         let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: NSStatusBar())         let menu = controller.makeMenu()        controller.menuWillOpen(menu)        let ids = menu.items.compactMap { $0.representedObject as? String }        #expect(ids.contains("menuCardExtraUsage"))    }     @Test    func showsVertexCostWhenUsageErrorPresent() {        self.disableMenuCardsForTesting()        let settings = self.makeSettings()        settings.statusChecksEnabled = false        settings.refreshFrequency = .manual        settings.mergeIcons = true        settings.selectedMenuProvider = .vertexai        settings.costUsageEnabled = true         let registry = ProviderRegistry.shared        if let vertexMeta = registry.metadata[.vertexai] {            settings.setProviderEnabled(provider: .vertexai, metadata: vertexMeta, enabled: true)        }        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: false)        }         let fetcher = UsageFetcher()        let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings)        store._setErrorForTesting("No Vertex AI usage data found for the current project.", provider: .vertexai)        store._setTokenSnapshotForTesting(CostUsageTokenSnapshot(            sessionTokens: 10,            sessionCostUSD: 0.01,            last30DaysTokens: 100,            last30DaysCostUSD: 1.0,            daily: [                CostUsageDailyReport.Entry(                    date: "2025-12-23",                    inputTokens: nil,                    outputTokens: nil,                    totalTokens: 100,                    costUSD: 1.0,                    modelsUsed: nil,                    modelBreakdowns: nil),            ],            updatedAt: Date()), provider: .vertexai)         let controller = StatusItemController(            store: store,            settings: settings,            account: fetcher.loadAccountInfo(),            updater: DisabledUpdaterController(),            preferencesSelection: PreferencesSelection(),            statusBar: NSStatusBar())         let menu = controller.makeMenu()        controller.menuWillOpen(menu)        let ids = menu.items.compactMap { $0.representedObject as? String }        #expect(ids.contains("menuCardCost"))    }}