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