fix: key multi-instance fallback by account

This commit is contained in:
Piotr Durlej 2026-05-09 21:36:32 +02:00
parent f7eba701e2
commit 64809c5845
2 changed files with 68 additions and 2 deletions

View file

@ -52,7 +52,7 @@ final class MultiInstanceManager {
return await group.reduce(into: [(Int, Bool, String?)]()) { $0.append($1) }
}
let bootstrapped = Dictionary(uniqueKeysWithValues: connections.map { ($0.instance.serverURL, $0) })
let bootstrapped = bootstrappedConnectionsByAccountKey()
var newConnections: [(instance: ForgejoInstance, authService: AuthenticationService)] = []
var newFailed: [(instance: ForgejoInstance, error: String)] = []
@ -60,7 +60,7 @@ final class MultiInstanceManager {
let instance = instances[index]
if success {
newConnections.append((instance: instance, authService: authServices[index]))
} else if let existing = bootstrapped[instance.serverURL] {
} else if let existing = bootstrapped[accountKey(for: instance)] {
newConnections.append(existing)
} else {
newFailed.append((instance: instance, error: error ?? "Unknown error"))
@ -115,6 +115,20 @@ final class MultiInstanceManager {
func displayName(for instance: ForgejoInstance) -> String {
instance.name.isEmpty ? instance.serverURL : instance.name
}
func accountKey(for instance: ForgejoInstance) -> String {
"\(ForgejoClient.normalizeServerURL(instance.serverURL)):\(instance.username)"
}
func bootstrappedConnectionsByAccountKey()
-> [String: (instance: ForgejoInstance, authService: AuthenticationService)]
{
var bootstrapped: [String: (instance: ForgejoInstance, authService: AuthenticationService)] = [:]
for connection in connections {
bootstrapped[accountKey(for: connection.instance)] = connection
}
return bootstrapped
}
}
/// Sendable snapshot of ForgejoInstance data needed for session restore.

View file

@ -1,4 +1,5 @@
import ForgejoKit
import Foundation
import Testing
@testable import Forji
@ -68,4 +69,55 @@ struct MultiInstanceManagerTests {
let sources = manager.connectionSources()
#expect(sources.isEmpty)
}
@Test func bootstrappedConnectionMapKeepsAccountsWithSameServerURL() async throws {
let serverURL = "https://forgejo.example.com/"
let normalizedServerURL = ForgejoClient.normalizeServerURL(serverURL)
let suffix = UUID().uuidString
let firstUsername = "alice-\(suffix)"
let secondUsername = "bob-\(suffix)"
let first = ForgejoInstance(
serverURL: serverURL,
username: firstUsername,
useTokenAuth: true,
)
let second = ForgejoInstance(
serverURL: serverURL,
username: secondUsername,
useTokenAuth: true,
)
try await KeychainManager.shared.saveToken(
"token-\(firstUsername)",
for: normalizedServerURL,
username: firstUsername,
)
try await KeychainManager.shared.saveToken(
"token-\(secondUsername)",
for: normalizedServerURL,
username: secondUsername,
)
let manager = MultiInstanceManager()
manager.bootstrap(instances: [first, second])
let bootstrapped = manager.bootstrappedConnectionsByAccountKey()
#expect(manager.connections.count == 2)
#expect(bootstrapped.count == 2)
#expect(
bootstrapped[manager.accountKey(for: first)]?.instance.username == firstUsername
)
#expect(
bootstrapped[manager.accountKey(for: second)]?.instance.username == secondUsername
)
try await KeychainManager.shared.deleteToken(
for: normalizedServerURL,
username: firstUsername,
)
try await KeychainManager.shared.deleteToken(
for: normalizedServerURL,
username: secondUsername,
)
}
}