135 lines
5.5 KiB
Swift
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)
|
|
}
|
|
}
|