From bd11940151de8d2cccfc89db2784999b8af714c4 Mon Sep 17 00:00:00 2001 From: Islam <2553451+qalandarov@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:59:48 +0100 Subject: [PATCH] Injectable UserDefaults to fix race condition in tests (#741) --- bitchat/Services/GeohashBookmarksStore.swift | 13 ++++++++----- bitchatTests/GeohashBookmarksStoreTests.swift | 19 ++++++++++--------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/bitchat/Services/GeohashBookmarksStore.swift b/bitchat/Services/GeohashBookmarksStore.swift index 39e2d7a1..9d6144f3 100644 --- a/bitchat/Services/GeohashBookmarksStore.swift +++ b/bitchat/Services/GeohashBookmarksStore.swift @@ -20,8 +20,11 @@ final class GeohashBookmarksStore: ObservableObject { private let geocoder = CLGeocoder() private var resolving: Set = [] #endif + + private let storage: UserDefaults - private init() { + init(storage: UserDefaults = .standard) { + self.storage = storage load() } @@ -64,7 +67,7 @@ final class GeohashBookmarksStore: ObservableObject { // MARK: - Persistence private func load() { - guard let data = UserDefaults.standard.data(forKey: storeKey) else { return } + guard let data = storage.data(forKey: storeKey) else { return } if let arr = try? JSONDecoder().decode([String].self, from: data) { // Sanitize, normalize, dedupe while preserving order (first occurrence wins) var seen = Set() @@ -81,7 +84,7 @@ final class GeohashBookmarksStore: ObservableObject { membership = seen } // Load any saved names - if let namesData = UserDefaults.standard.data(forKey: namesStoreKey), + if let namesData = storage.data(forKey: namesStoreKey), let dict = try? JSONDecoder().decode([String: String].self, from: namesData) { bookmarkNames = dict } @@ -89,13 +92,13 @@ final class GeohashBookmarksStore: ObservableObject { private func persist() { if let data = try? JSONEncoder().encode(bookmarks) { - UserDefaults.standard.set(data, forKey: storeKey) + storage.set(data, forKey: storeKey) } } private func persistNames() { if let data = try? JSONEncoder().encode(bookmarkNames) { - UserDefaults.standard.set(data, forKey: namesStoreKey) + storage.set(data, forKey: namesStoreKey) } } diff --git a/bitchatTests/GeohashBookmarksStoreTests.swift b/bitchatTests/GeohashBookmarksStoreTests.swift index 6b3fdac1..a7804ef9 100644 --- a/bitchatTests/GeohashBookmarksStoreTests.swift +++ b/bitchatTests/GeohashBookmarksStoreTests.swift @@ -3,23 +3,25 @@ import XCTest final class GeohashBookmarksStoreTests: XCTestCase { let storeKey = "locationChannel.bookmarks" + var storage: UserDefaults! + var store: GeohashBookmarksStore! override func setUp() { super.setUp() - // Clear persisted state before each test - UserDefaults.standard.removeObject(forKey: storeKey) - GeohashBookmarksStore.shared._resetForTesting() + // Unique instance for each test to avoid race condition + storage = UserDefaults(suiteName: UUID().uuidString) + store = GeohashBookmarksStore(storage: storage!) } override func tearDown() { - // Clean after each test - UserDefaults.standard.removeObject(forKey: storeKey) - GeohashBookmarksStore.shared._resetForTesting() + storage.removeObject(forKey: storeKey) + store._resetForTesting() + store = nil + storage = nil super.tearDown() } func testToggleAndNormalize() { - let store = GeohashBookmarksStore.shared // Start clean XCTAssertTrue(store.bookmarks.isEmpty) @@ -35,12 +37,11 @@ final class GeohashBookmarksStoreTests: XCTestCase { } func testPersistenceWritten() throws { - let store = GeohashBookmarksStore.shared store.toggle("ezs42") store.toggle("u4pruy") // Verify persisted JSON contains both (order not enforced here) - guard let data = UserDefaults.standard.data(forKey: storeKey) else { + guard let data = storage.data(forKey: storeKey) else { XCTFail("No persisted data found") return }