apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

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