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

135 lines
5.5 KiB
Swift

import CodexBarCore
import SwiftUI
import Testing
@testable import CodexBar
@MainActor
@Suite(.serialized)
struct GeminiProviderMigrationSettingsTests {
private func makeSettings() -> SettingsStore {
let suite = "GeminiProviderMigrationSettingsTests-\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suite)!
defaults.removePersistentDomain(forName: suite)
let settings = SettingsStore(
userDefaults: defaults,
configStore: testConfigStore(suiteName: suite),
zaiTokenStore: NoopZaiTokenStore(),
syntheticTokenStore: NoopSyntheticTokenStore())
settings.providerDetectionCompleted = true
settings.statusChecksEnabled = false
settings.refreshFrequency = .manual
return settings
}
private func makeStore(settings: SettingsStore) -> UsageStore {
UsageStore(
fetcher: UsageFetcher(),
browserDetection: BrowserDetection(cacheTTL: 0),
settings: settings)
}
private func makeContext(settings: SettingsStore, store: UsageStore) -> ProviderSettingsContext {
ProviderSettingsContext(
provider: .gemini,
settings: settings,
store: store,
boolBinding: { keyPath in
Binding(
get: { settings[keyPath: keyPath] },
set: { settings[keyPath: keyPath] = $0 })
},
stringBinding: { keyPath in
Binding(
get: { settings[keyPath: keyPath] },
set: { settings[keyPath: keyPath] = $0 })
},
statusText: { _ in nil },
setStatusText: { _, _ in },
lastAppActiveRunAt: { _ in nil },
setLastAppActiveRunAt: { _, _ in },
requestConfirmation: { _ in },
runLoginFlow: {})
}
@Test
func `typed predicate accepts consumer tier deprecated only`() {
#expect(UsageStore.isGeminiConsumerTierDeprecationError(GeminiStatusProbeError.consumerTierDeprecated))
#expect(!UsageStore.isGeminiConsumerTierDeprecationError(GeminiStatusProbeError.notLoggedIn))
#expect(!UsageStore.isGeminiConsumerTierDeprecationError(nil))
}
@Test
func `ordinary auth errors do not set migration observation`() {
let settings = self.makeSettings()
let store = self.makeStore(settings: settings)
store.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.notLoggedIn)
#expect(!store.geminiObservedConsumerTierDeprecation)
}
@Test
func `settings action appears when deprecation was observed`() {
let settings = self.makeSettings()
let store = self.makeStore(settings: settings)
store.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.consumerTierDeprecated)
let impl = GeminiProviderImplementation()
let antigravity = ProviderDescriptorRegistry.descriptor(for: .antigravity).metadata
let wasEnabled = settings.isProviderEnabled(provider: .antigravity, metadata: antigravity)
let actions = impl.settingsActions(context: self.makeContext(settings: settings, store: store))
#expect(actions.map(\.id) == ["gemini-antigravity-migration"])
#expect(settings.isProviderEnabled(provider: .antigravity, metadata: antigravity) == wasEnabled)
}
@Test
func `settings action hidden for ordinary not logged in errors`() {
let settings = self.makeSettings()
let store = self.makeStore(settings: settings)
store.errors[.gemini] = GeminiStatusProbeError.notLoggedIn.errorDescription
store.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.notLoggedIn)
let impl = GeminiProviderImplementation()
let actions = impl.settingsActions(context: self.makeContext(settings: settings, store: store))
#expect(actions.isEmpty)
#expect(!store.geminiObservedConsumerTierDeprecation)
}
@Test
func `settings action hidden for unauthenticated401 style failures`() {
let unauthenticatedBody = """
{"error":{"code":401,"message":"Request had invalid authentication credentials.","status":"UNAUTHENTICATED"}}
"""
#expect(!GeminiStatusProbeError.isConsumerTierDeprecationSignal(unauthenticatedBody))
let settings = self.makeSettings()
let store = self.makeStore(settings: settings)
store.errors[.gemini] = GeminiStatusProbeError.notLoggedIn.errorDescription
store.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.notLoggedIn)
let impl = GeminiProviderImplementation()
let actions = impl.settingsActions(context: self.makeContext(settings: settings, store: store))
#expect(actions.isEmpty)
}
@Test
func `migration observation is store scoped and survives unrelated failures`() {
let firstSettings = self.makeSettings()
let firstStore = self.makeStore(settings: firstSettings)
let secondSettings = self.makeSettings()
let secondStore = self.makeStore(settings: secondSettings)
firstStore.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.consumerTierDeprecated)
firstStore.observeGeminiConsumerTierDeprecation(from: GeminiStatusProbeError.notLoggedIn)
#expect(firstStore.geminiObservedConsumerTierDeprecation)
#expect(!secondStore.geminiObservedConsumerTierDeprecation)
firstStore.clearGeminiConsumerTierDeprecationObservation()
#expect(!firstStore.geminiObservedConsumerTierDeprecation)
}
}