field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

TeraIdentityCancellationTests.swift (2911B)


      1 import Foundation
      2 import RadrootsKit
      3 @testable import TeraApp
      4 import XCTest
      5 
      6 final class TeraIdentityCancellationTests: XCTestCase {
      7     func testCancelledHostImportCannotInstallAfterSuccessfulPresenceCallback() async throws {
      8         let secure = InMemorySecureStore()
      9         let presence = IdentityCancellationPresence()
     10         let custody = try RadrootsIdentityCustody(
     11           configuration: RadrootsIdentityCustodyConfiguration(namespace: "radroots_identity_v1", secretPolicy: .secureLocalSecret),
     12           secureStore: secure, metadataStore: InMemoryIdentityMetadataStore(), userPresence: presence
     13         )
     14         let prefix = "tera.identity.cancellation.\(UUID().uuidString.lowercased())"
     15         let defaults = try XCTUnwrap(UserDefaults(suiteName: prefix))
     16         defer { UserDefaults(suiteName: prefix)?.removePersistentDomain(forName: prefix) }
     17         let store = TeraIdentityStore(custody: custody, secureStore: secure, servicePrefix: prefix, userDefaults: defaults)
     18         let task = Task {
     19             try await store.importIdentity(RadrootsIdentitySecretMaterial(rawRepresentation: Data(repeating: 1, count: 32)))
     20         }
     21         for await _ in presence.entered {
     22           break
     23         }
     24         task.cancel()
     25         await presence.release()
     26         do {
     27             _ = try await task.value
     28             XCTFail("Cancellation must reject a later successful presence callback")
     29         } catch {
     30             XCTAssertEqual(error as? RadrootsIdentityCustodyError, .cancelled)
     31         }
     32         let state = await store.snapshot()
     33         XCTAssertEqual(state.state, .absent)
     34         XCTAssertFalse(try secure.contains(RadrootsSecureStoreKey(namespace: "radroots_identity_v1", name: "active_secret_v1")))
     35     }
     36 
     37     func testCustodyErrorPresentationDoesNotExposeSecretOrInternalText() {
     38         let sentinel = "synthetic-secret-sentinel"
     39         let error = TeraIdentityStoreError.custody(sentinel)
     40         XCTAssertFalse(error.localizedDescription.contains(sentinel))
     41         XCTAssertEqual(error.localizedDescription, "The local identity needs attention before Tera can continue.")
     42     }
     43 }
     44 
     45 private actor IdentityCancellationPresence: RadrootsUserPresence {
     46     nonisolated let entered: AsyncStream<Void>
     47     private let signal: AsyncStream<Void>.Continuation
     48     private var pending: CheckedContinuation<Void, Never>?
     49     init() {
     50       (entered, signal) = AsyncStream.makeStream()
     51     }
     52 
     53     func currentStatus() async throws -> RadrootsUserPresenceStatus {
     54       .unavailable
     55     }
     56 
     57     func release() {
     58         pending?.resume()
     59         pending = nil
     60     }
     61 
     62     func verify(_ request: RadrootsUserPresenceRequest) async throws -> RadrootsUserPresenceResult {
     63         await withCheckedContinuation { continuation in
     64             pending = continuation
     65             signal.yield(())
     66         }
     67         return RadrootsUserPresenceResult(policy: request.policy, verified: true)
     68     }
     69 }