Isolate nearby notes settings in tests

This commit is contained in:
jack
2026-07-26 02:09:32 +02:00
parent 7511e98557
commit cbe234b363
2 changed files with 49 additions and 30 deletions
+14 -5
View File
@@ -33,15 +33,25 @@ final class NearbyNotesCounter: ObservableObject {
private let locationManager: LocationChannelManager
private let managerFactory: @MainActor (String) -> LocationNotesManager
private let releaseManager: @MainActor (LocationNotesManager?) -> Void
private let locationNotesEnabled: @MainActor () -> Bool
private let locationNotesSettingsPublisher: AnyPublisher<Void, Never>
init(
locationManager: LocationChannelManager = .shared,
managerFactory: @escaping @MainActor (String) -> LocationNotesManager = { LocationNotesPool.shared.acquire($0) },
releaseManager: @escaping @MainActor (LocationNotesManager?) -> Void = { LocationNotesPool.shared.release($0) }
releaseManager: @escaping @MainActor (LocationNotesManager?) -> Void = { LocationNotesPool.shared.release($0) },
locationNotesEnabled: @escaping @MainActor () -> Bool = { LocationNotesSettings.enabled },
locationNotesSettings: AnyPublisher<Void, Never>? = nil
) {
self.locationManager = locationManager
self.managerFactory = managerFactory
self.releaseManager = releaseManager
self.locationNotesEnabled = locationNotesEnabled
self.locationNotesSettingsPublisher = locationNotesSettings
?? NotificationCenter.default
.publisher(for: LocationNotesSettings.didChangeNotification)
.map { _ in () }
.eraseToAnyPublisher()
}
/// Whether the empty-timeline "check for notes" hint should render.
@@ -53,7 +63,7 @@ final class NearbyNotesCounter: ObservableObject {
/// passes its own observed permission state so the hint re-renders when
/// authorization changes.
func offersRevealHint(permissionState: LocationChannelManager.PermissionState) -> Bool {
!revealed && LocationNotesSettings.enabled && permissionState == .authorized
!revealed && locationNotesEnabled() && permissionState == .authorized
}
/// Marks the one explicit act that lets the counter subscribe. Sticky for
@@ -83,8 +93,7 @@ final class NearbyNotesCounter: ObservableObject {
.sink { [weak self] _ in self?.retarget() }
// The app-info kill switch must take effect immediately, not on the
// next location change or remount.
settingCancellable = NotificationCenter.default
.publisher(for: LocationNotesSettings.didChangeNotification)
settingCancellable = locationNotesSettingsPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.retarget() }
retarget()
@@ -105,7 +114,7 @@ final class NearbyNotesCounter: ObservableObject {
private func retarget() {
guard activeHolders > 0,
revealed,
LocationNotesSettings.enabled,
locationNotesEnabled(),
locationManager.permissionState == .authorized,
let geohash = locationManager.availableChannels
.first(where: { $0.level == .building })?.geohash
+35 -25
View File
@@ -8,25 +8,9 @@ import XCTest
/// the pooled subscription must come up exactly once and go down exactly once.
@MainActor
final class NearbyNotesCounterTests: XCTestCase {
private var previousNotesEnabled: Any?
override func setUp() {
super.setUp()
previousNotesEnabled = UserDefaults.standard.object(forKey: "locationNotes.enabled")
UserDefaults.standard.set(true, forKey: "locationNotes.enabled")
}
override func tearDown() {
if let previous = previousNotesEnabled as? Bool {
UserDefaults.standard.set(previous, forKey: "locationNotes.enabled")
} else {
UserDefaults.standard.removeObject(forKey: "locationNotes.enabled")
}
super.tearDown()
}
func test_counterOnlySubscribesAfterReveal_countsUnexpiredNotes_andUnsubscribesOnDeactivate() async throws {
let relays = SubscriptionRecorder()
let settings = LocationNotesSettingsStub()
let locationManager = try await makeAuthorizedLocationManager()
let buildingGeohash = try XCTUnwrap(
locationManager.availableChannels.first(where: { $0.level == .building })?.geohash
@@ -35,7 +19,9 @@ final class NearbyNotesCounterTests: XCTestCase {
let counter = NearbyNotesCounter(
locationManager: locationManager,
managerFactory: { LocationNotesManager(geohash: $0, dependencies: relays.dependencies) },
releaseManager: { $0?.cancel() }
releaseManager: { $0?.cancel() },
locationNotesEnabled: { settings.enabled },
locationNotesSettings: settings.changes
)
counter.activate()
@@ -93,11 +79,14 @@ final class NearbyNotesCounterTests: XCTestCase {
func test_permissionRevocation_releasesBuildingSubscriptionDespiteCachedChannels() async throws {
let relays = SubscriptionRecorder()
let settings = LocationNotesSettingsStub()
let locationManager = try await makeAuthorizedLocationManager()
let counter = NearbyNotesCounter(
locationManager: locationManager,
managerFactory: { LocationNotesManager(geohash: $0, dependencies: relays.dependencies) },
releaseManager: { $0?.cancel() }
releaseManager: { $0?.cancel() },
locationNotesEnabled: { settings.enabled },
locationNotesSettings: settings.changes
)
counter.activate()
@@ -122,24 +111,27 @@ final class NearbyNotesCounterTests: XCTestCase {
func test_locationNotesKillSwitch_releasesAndCanReacquireBuildingSubscription() async throws {
let relays = SubscriptionRecorder()
let settings = LocationNotesSettingsStub()
let locationManager = try await makeAuthorizedLocationManager()
let counter = NearbyNotesCounter(
locationManager: locationManager,
managerFactory: { LocationNotesManager(geohash: $0, dependencies: relays.dependencies) },
releaseManager: { $0?.cancel() }
releaseManager: { $0?.cancel() },
locationNotesEnabled: { settings.enabled },
locationNotesSettings: settings.changes
)
counter.activate()
counter.reveal()
XCTAssertEqual(relays.subscribeCount, 1)
LocationNotesSettings.enabled = false
settings.setEnabled(false)
let released = await waitUntil { relays.unsubscribeCount == 1 }
XCTAssertTrue(released)
XCTAssertEqual(counter.noteCount, 0)
LocationNotesSettings.enabled = true
settings.setEnabled(true)
let reacquired = await waitUntil { relays.subscribeCount == 2 }
XCTAssertTrue(reacquired)
@@ -149,10 +141,13 @@ final class NearbyNotesCounterTests: XCTestCase {
func test_checkNotesHint_requiresAuthorizedLocationPermission() {
let relays = SubscriptionRecorder()
let settings = LocationNotesSettingsStub()
let counter = NearbyNotesCounter(
locationManager: makeBareLocationManager(),
managerFactory: { LocationNotesManager(geohash: $0, dependencies: relays.dependencies) },
releaseManager: { $0?.cancel() }
releaseManager: { $0?.cancel() },
locationNotesEnabled: { settings.enabled },
locationNotesSettings: settings.changes
)
// An unauthorized install must never see the hint: the tap can't
@@ -164,9 +159,9 @@ final class NearbyNotesCounterTests: XCTestCase {
XCTAssertTrue(counter.offersRevealHint(permissionState: .authorized))
// The app-info kill switch hides it too.
LocationNotesSettings.enabled = false
settings.setEnabled(false)
XCTAssertFalse(counter.offersRevealHint(permissionState: .authorized))
LocationNotesSettings.enabled = true
settings.setEnabled(true)
// Once revealed, the hint yields to the live strip and count.
counter.reveal()
@@ -411,6 +406,21 @@ final class NearbyNotesCounterTests: XCTestCase {
}
}
@MainActor
private final class LocationNotesSettingsStub {
private let changesSubject = PassthroughSubject<Void, Never>()
private(set) var enabled = true
var changes: AnyPublisher<Void, Never> {
changesSubject.eraseToAnyPublisher()
}
func setEnabled(_ enabled: Bool) {
self.enabled = enabled
changesSubject.send(())
}
}
/// Stub relay layer: counts REQs, captures the last filter/handler, and never
/// touches the network.
@MainActor