Skip to content

Commit ab7fa24

Browse files
committed
fix: clear stale DeepSeek web balance
1 parent 0f77312 commit ab7fa24

2 files changed

Lines changed: 86 additions & 1 deletion

File tree

Sources/CodexBar/Providers/DeepSeek/DeepSeekProviderImplementation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct DeepSeekProviderImplementation: ProviderImplementation {
2424
else { return [] }
2525
let apiKey = context.settings.selectedTokenAccount(for: .deepseek)?.token
2626
?? DeepSeekSettingsReader.apiKey(environment: context.store.environmentBase)
27+
let source = context.settings.providerConfig(for: .deepseek)?.source ?? .auto
2728
let selectedProfileID = context.settings.deepseekProfileID(apiKey: apiKey)
2829
let hasValidSelection = profiles.contains { $0.id == selectedProfileID }
2930
let profileBinding = Binding(
@@ -33,7 +34,7 @@ struct DeepSeekProviderImplementation: ProviderImplementation {
3334
},
3435
set: { profileID in
3536
guard !profileID.isEmpty else { return }
36-
context.store.beginDeepSeekProfileTransition(preservingBalance: apiKey != nil)
37+
context.store.beginDeepSeekProfileTransition(preservingBalance: apiKey != nil && source != .web)
3738
context.settings.setDeepSeekProfileID(profileID, apiKey: apiKey)
3839
})
3940
let options = (hasValidSelection
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import CodexBarCore
2+
import Foundation
3+
import SwiftUI
4+
import Testing
5+
@testable import CodexBar
6+
7+
@MainActor
8+
struct DeepSeekProfileTransitionTests {
9+
@Test(arguments: [false, true])
10+
func `forced web profile transition clears stale balance with an api key`(
11+
isCancellation: Bool) async throws
12+
{
13+
let apiKey = "test-deepseek-api-key"
14+
let suite = "DeepSeekProfileTransitionTests-forced-web-\(isCancellation)"
15+
let defaults = try #require(UserDefaults(suiteName: suite))
16+
defaults.removePersistentDomain(forName: suite)
17+
let settings = SettingsStore(
18+
userDefaults: defaults,
19+
configStore: testConfigStore(suiteName: suite),
20+
zaiTokenStore: NoopZaiTokenStore(),
21+
syntheticTokenStore: NoopSyntheticTokenStore())
22+
settings.updateProviderConfig(provider: .deepseek) { $0.source = .web }
23+
let store = UsageStore(
24+
fetcher: UsageFetcher(environment: [:]),
25+
browserDetection: BrowserDetection(cacheTTL: 0),
26+
settings: settings,
27+
environmentBase: [DeepSeekSettingsReader.apiKeyEnvironmentKey: apiKey])
28+
store.snapshots[.deepseek] = UsageSnapshot(
29+
primary: RateWindow(
30+
usedPercent: 0,
31+
windowMinutes: nil,
32+
resetsAt: nil,
33+
resetDescription: "$8.06 from previous profile"),
34+
secondary: nil,
35+
deepseekPlatformProfiles: [
36+
DeepSeekPlatformProfile(id: "chrome:Default", name: "Chrome — Personal"),
37+
DeepSeekPlatformProfile(id: "chrome:Profile 2", name: "Chrome — Work"),
38+
],
39+
updatedAt: Date())
40+
41+
let context = Self.settingsContext(settings: settings, store: store)
42+
let picker = try #require(DeepSeekProviderImplementation().settingsPickers(context: context).first)
43+
picker.binding.wrappedValue = "chrome:Profile 2"
44+
45+
#expect(store.deepseekProfileTransitionSnapshot?.primary?.resetDescription == "Refreshing")
46+
#expect(store.deepseekProfileTransitionSnapshot?.primary?.resetDescription?.contains("$8.06") == false)
47+
48+
let outcome = if isCancellation {
49+
ProviderFetchOutcome(result: .failure(CancellationError()), attempts: [])
50+
} else {
51+
ProviderFetchOutcome(result: .failure(DeepSeekUsageError.apiError("offline")), attempts: [])
52+
}
53+
await store.applySelectedOutcome(outcome, provider: .deepseek, account: nil, fallbackSnapshot: nil)
54+
55+
#expect(store.deepseekProfileTransitionSnapshot?.primary?.resetDescription == "Unavailable")
56+
#expect(store.deepseekProfileTransitionSnapshot?.primary?.resetDescription?.contains("$8.06") == false)
57+
}
58+
59+
private static func settingsContext(
60+
settings: SettingsStore,
61+
store: UsageStore) -> ProviderSettingsContext
62+
{
63+
ProviderSettingsContext(
64+
provider: .deepseek,
65+
settings: settings,
66+
store: store,
67+
boolBinding: { keyPath in
68+
Binding(
69+
get: { settings[keyPath: keyPath] },
70+
set: { settings[keyPath: keyPath] = $0 })
71+
},
72+
stringBinding: { keyPath in
73+
Binding(
74+
get: { settings[keyPath: keyPath] },
75+
set: { settings[keyPath: keyPath] = $0 })
76+
},
77+
statusText: { _ in nil },
78+
setStatusText: { _, _ in },
79+
lastAppActiveRunAt: { _ in nil },
80+
setLastAppActiveRunAt: { _, _ in },
81+
requestConfirmation: { _ in },
82+
runLoginFlow: {})
83+
}
84+
}

0 commit comments

Comments
 (0)