mirror of
https://codeberg.org/secana/Forji.git
synced 2026-06-16 05:13:55 -07:00
fix: crash merged overviews when two accounts share a sourceKey (#39)
Adding the same server and username twice gives two connections one `sourceKey`, and `rehydrate`'s `Dictionary(uniqueKeysWithValues:)` traps on it, crashing every merged overview on construction; this keeps the first source and adds a regression test (185 ForjiTests green). I grant Stefan Hausotte an irrevocable, worldwide, royalty-free license to use, sublicense, and distribute my contribution, including through Apple's App Store under the project's App Store exception. Reviewed-on: https://codeberg.org/secana/Forji/pulls/39
This commit is contained in:
parent
c6dda2cd70
commit
89a0cd0bb2
2 changed files with 33 additions and 1 deletions
|
|
@ -73,8 +73,11 @@ extension PaginationState where Item: Identifiable {
|
|||
/// Replace placeholder auth services using the given connection sources.
|
||||
/// Items whose sourceKey has no matching source are removed.
|
||||
func rehydrate<T>(from sources: [ConnectionSource]) where Item == TaggedItem<T> {
|
||||
// Two connected accounts with the same server+username share a sourceKey,
|
||||
// so keep the first rather than trapping on a duplicate key.
|
||||
let lookup = Dictionary(
|
||||
uniqueKeysWithValues: sources.map { ($0.sourceKey, $0.authService) },
|
||||
sources.map { ($0.sourceKey, $0.authService) },
|
||||
uniquingKeysWith: { first, _ in first },
|
||||
)
|
||||
items = items.compactMap { item in
|
||||
guard let auth = lookup[item.sourceKey] else { return nil }
|
||||
|
|
|
|||
|
|
@ -190,4 +190,33 @@ struct TaggedItemTests {
|
|||
|
||||
#expect(pagination.items.isEmpty)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Test func rehydrateHandlesDuplicateSourceKeys() {
|
||||
// Two connected accounts with the same server+username produce duplicate
|
||||
// sourceKeys; rehydrate must not trap on the duplicate key.
|
||||
let liveAuth = AuthenticationService()
|
||||
let key = "https://a.com:u"
|
||||
let sources = [
|
||||
ConnectionSource(
|
||||
sourceKey: key, name: "A",
|
||||
client: ForgejoClient(serverURL: "https://a.com", username: "u", token: "t"),
|
||||
authService: liveAuth,
|
||||
),
|
||||
ConnectionSource(
|
||||
sourceKey: key, name: "A (duplicate)",
|
||||
client: ForgejoClient(serverURL: "https://a.com", username: "u", token: "t"),
|
||||
authService: AuthenticationService(),
|
||||
),
|
||||
]
|
||||
let pagination = PaginationState<TaggedItem<FIssue>>()
|
||||
pagination.items = [
|
||||
TaggedItem(item: makeIssue(id: 1), sourceKey: key, instanceName: "A", authService: makeAuth()),
|
||||
]
|
||||
|
||||
pagination.rehydrate(from: sources)
|
||||
|
||||
#expect(pagination.items.count == 1)
|
||||
#expect(pagination.items[0].authService === liveAuth) // first source wins
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue