diff --git a/apps/macos/Sources/OpenClaw/NodeMode/MacNodeRuntime.swift b/apps/macos/Sources/OpenClaw/NodeMode/MacNodeRuntime.swift index 16d4a38d7d2f..646d4e9e45ed 100644 --- a/apps/macos/Sources/OpenClaw/NodeMode/MacNodeRuntime.swift +++ b/apps/macos/Sources/OpenClaw/NodeMode/MacNodeRuntime.swift @@ -500,14 +500,9 @@ extension MacNodeRuntime { (Self.locationPreciseEnabled() ? .precise : .balanced) let services = await mainActorServices() let status = await services.locationAuthorizationStatus() - let hasPermission = switch mode { - case .always: - status == .authorizedAlways - case .whileUsing: - status == .authorizedAlways - case .off: - false - } + let hasPermission = PermissionManager.isLocationAuthorized( + status: status, + requireAlways: mode == .always) if !hasPermission { return BridgeInvokeResponse( id: req.id, diff --git a/apps/macos/Tests/OpenClawIPCTests/MacNodeRuntimeTests.swift b/apps/macos/Tests/OpenClawIPCTests/MacNodeRuntimeTests.swift index abfb06a1af18..f8b849787b2e 100644 --- a/apps/macos/Tests/OpenClawIPCTests/MacNodeRuntimeTests.swift +++ b/apps/macos/Tests/OpenClawIPCTests/MacNodeRuntimeTests.swift @@ -130,6 +130,7 @@ struct MacNodeRuntimeTests { var actError: Error? var performCallCount = 0 var releaseCallCount = 0 + var locationStatus: CLAuthorizationStatus var receivedLifecycleGenerations: [UInt64] = [] var receivedReleaseGenerations: [UInt64] = [] private let snapshotInspection: SnapshotInspection? @@ -147,6 +148,7 @@ struct MacNodeRuntimeTests { snapshotError: Error? = nil, snapshotInspection: SnapshotInspection? = nil, actError: Error? = nil, + locationAuthorizationStatus: CLAuthorizationStatus = .authorizedAlways, performEnteredGate: AsyncTestGate? = nil, allowPerformGate: AsyncTestGate? = nil) { @@ -154,6 +156,7 @@ struct MacNodeRuntimeTests { self.snapshotError = snapshotError self.snapshotInspection = snapshotInspection self.actError = actError + self.locationStatus = locationAuthorizationStatus self.performEnteredGate = performEnteredGate self.allowPerformGate = allowPerformGate } @@ -192,7 +195,7 @@ struct MacNodeRuntimeTests { } func locationAuthorizationStatus() -> CLAuthorizationStatus { - .authorizedAlways + self.locationStatus } func locationAccuracyAuthorization() -> CLAccuracyAuthorization { @@ -453,6 +456,30 @@ struct MacNodeRuntimeTests { } } + @Test func `handle location invoke applies authorization required by mode`() async throws { + let authorizedWhenInUse = try #require(CLAuthorizationStatus(rawValue: 4)) + let cases: [(mode: OpenClawLocationMode, status: CLAuthorizationStatus, accepted: Bool)] = [ + (.whileUsing, authorizedWhenInUse, true), + (.always, authorizedWhenInUse, false), + (.whileUsing, .authorizedAlways, true), + (.always, .authorizedAlways, true), + ] + + for testCase in cases { + await TestIsolation.withUserDefaultsValues([locationModeKey: testCase.mode.rawValue]) { + let services = await MainActor.run { + MainActorServicesProbe(locationAuthorizationStatus: testCase.status) + } + let runtime = MacNodeRuntime(makeMainActorServices: { services }) + + let response = await self.invoke( + runtime, "req-location", OpenClawLocationCommand.get.rawValue) + + #expect(response.ok == testCase.accepted) + } + } + } + @Test func `handle invoke screen record uses injected services`() async throws { let services = await MainActor.run { MainActorServicesProbe() } let runtime = MacNodeRuntime(makeMainActorServices: { services })