RadrootsAppleUserPresenceTests.swift (3663B)
1 import Foundation 2 import Testing 3 @testable import RadrootsKit 4 5 @Test func appleUserPresenceReportsStatusThroughAdapter() async throws { 6 let expectedStatus = RadrootsUserPresenceStatus( 7 support: .deviceCredential, 8 biometryKind: .none, 9 canEvaluateDeviceCredential: true, 10 canEvaluateBiometrics: false 11 ) 12 let service = RadrootsAppleUserPresence( 13 adapters: RadrootsAppleUserPresenceAdapters( 14 currentStatus: { 15 expectedStatus 16 }, 17 verify: { request in 18 RadrootsUserPresenceResult(policy: request.policy, verified: true) 19 } 20 ) 21 ) 22 23 let status = try await service.currentStatus() 24 25 #expect(status == expectedStatus) 26 } 27 28 @Test func appleUserPresenceVerifiesThroughAdapter() async throws { 29 let request = try RadrootsUserPresenceRequest( 30 policy: .deviceOwnerAuthenticationWithBiometrics, 31 reason: "Unlock local Nostr identity" 32 ) 33 let service = RadrootsAppleUserPresence( 34 adapters: RadrootsAppleUserPresenceAdapters( 35 currentStatus: { 36 .unavailable 37 }, 38 verify: { request in 39 RadrootsUserPresenceResult(policy: request.policy, verified: true) 40 } 41 ) 42 ) 43 44 let result = try await service.verify(request) 45 46 #expect(result.policy == .deviceOwnerAuthenticationWithBiometrics) 47 #expect(result.verified) 48 } 49 50 @Test func appleUserPresencePropagatesAdapterFailures() async throws { 51 let service = RadrootsAppleUserPresence( 52 adapters: RadrootsAppleUserPresenceAdapters( 53 currentStatus: { 54 .unavailable 55 }, 56 verify: { _ in 57 throw RadrootsUserPresenceError.unavailable("user presence unavailable") 58 } 59 ) 60 ) 61 62 await #expect(throws: RadrootsUserPresenceError.unavailable("user presence unavailable")) { 63 try await service.verify(RadrootsUserPresenceRequest(reason: "Delete local Nostr identity")) 64 } 65 } 66 67 @Test func appleUserPresenceAsyncSupportTimesOutUnresolvedCallbacks() async { 68 await #expect(throws: RadrootsUserPresenceError.timeout("timed out")) { 69 let _: Bool = try await RadrootsAppleUserPresenceAsyncSupport.awaitCallback( 70 timeout: 0.001, 71 timeoutMessage: "timed out" 72 ) { _ in } 73 } 74 } 75 76 #if canImport(LocalAuthentication) 77 import LocalAuthentication 78 79 @Test func appleUserPresenceMapsLocalAuthenticationPolicies() { 80 #expect( 81 RadrootsAppleUserPresenceAdapters.platformPolicy(.deviceOwnerAuthentication) == 82 LAPolicy.deviceOwnerAuthentication 83 ) 84 #expect( 85 RadrootsAppleUserPresenceAdapters.platformPolicy(.deviceOwnerAuthenticationWithBiometrics) == 86 LAPolicy.deviceOwnerAuthenticationWithBiometrics 87 ) 88 } 89 90 @Test func appleUserPresenceMapsLocalAuthenticationErrors() { 91 assertUserPresenceError( 92 RadrootsAppleUserPresenceAdapters.adapt(error: LAError(.userCancel)), 93 matches: { if case .userCancelled = $0 { true } else { false } } 94 ) 95 assertUserPresenceError( 96 RadrootsAppleUserPresenceAdapters.adapt(error: LAError(.biometryNotAvailable)), 97 matches: { if case .unavailable = $0 { true } else { false } } 98 ) 99 assertUserPresenceError( 100 RadrootsAppleUserPresenceAdapters.adapt(error: LAError(.authenticationFailed)), 101 matches: { if case .permissionDenied = $0 { true } else { false } } 102 ) 103 } 104 105 private func assertUserPresenceError( 106 _ error: RadrootsUserPresenceError, 107 matches: (RadrootsUserPresenceError) -> Bool 108 ) { 109 #expect(matches(error)) 110 } 111 #endif