field_ios

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

TeraRuntimeResourceFixtures.swift (7001B)


      1 import Foundation
      2 @testable import TeraApp
      3 
      4 actor ResourceTestGate {
      5   private var opened = false
      6   private var waiters: [CheckedContinuation<Void, Never>] = []
      7 
      8   func wait() async {
      9     guard !opened else { return }
     10     await withCheckedContinuation { waiters.append($0) }
     11   }
     12 
     13   func open() {
     14     guard !opened else { return }
     15     opened = true
     16     let pending = waiters
     17     waiters.removeAll()
     18     for waiter in pending {
     19       waiter.resume()
     20     }
     21   }
     22 }
     23 
     24 struct ResourceTestPause: Sendable {
     25   let entered = ResourceTestGate()
     26   let resume = ResourceTestGate()
     27 
     28   func wait() async {
     29     await entered.open()
     30     await resume.wait()
     31   }
     32 }
     33 
     34 actor ResourceTestToken: TeraRuntimeSubscriptionToken {
     35   let cancelled = ResourceTestGate()
     36   private(set) var cancelCount = 0
     37 
     38   func cancel() async {
     39     cancelCount += 1
     40     await cancelled.open()
     41   }
     42 }
     43 
     44 actor ResourceTestBackend: TeraRuntimeBackend {
     45   let token = ResourceTestToken()
     46   let closed = ResourceTestGate()
     47   private(set) var shutdownCount = 0
     48   private let value: TeraRuntimeSnapshot
     49   private var subscriptionPause: ResourceTestPause?
     50   private var snapshotPause: ResourceTestPause?
     51   private var settingsPause: ResourceTestPause?
     52   private var profilePause: ResourceTestPause?
     53   private var shutdownPause: ResourceTestPause?
     54   private var shutdownFailure: TeraRuntimeFailure?
     55   private var shutdownCompleted = false
     56   private var removalPause: ResourceTestPause?
     57   private var removalFailureID: String?
     58   private(set) var preparedRemovals: [String] = []
     59   private(set) var profileMutations = 0
     60   private(set) var identityCommands = 0
     61   private var receive: (@Sendable (TeraRuntimeChange) async -> Void)?
     62 
     63   init(publicKeyHex: String) {
     64     value = TeraRuntimeSnapshot(
     65       identity: TeraRuntimeIdentity(publicKeyHex: publicKeyHex, hostSignerConfigured: true),
     66       relay: nil, blossomConfiguration: nil, blossomEvidence: nil,
     67       crateName: "tera_ffi", crateVersion: "0.1.0-alpha", isClosed: false
     68     )
     69   }
     70 
     71   func start() -> TeraRuntimeBackendStart {
     72     TeraRuntimeBackendStart(backend: self, snapshot: value)
     73   }
     74 
     75   func pauseSubscription(_ pause: ResourceTestPause) {
     76     subscriptionPause = pause
     77   }
     78 
     79   func pauseSnapshot(_ pause: ResourceTestPause) {
     80     snapshotPause = pause
     81   }
     82 
     83   func pauseSettings(_ pause: ResourceTestPause) {
     84     settingsPause = pause
     85   }
     86 
     87   func pauseShutdown(_ pause: ResourceTestPause) {
     88     shutdownPause = pause
     89   }
     90 
     91   func configureRemoval(pause: ResourceTestPause? = nil, failureID: String? = nil) {
     92     removalPause = pause
     93     removalFailureID = failureID
     94   }
     95 
     96   func prepareRetractionForKeyRemoval(_ request: TeraKeyRemovalRequest) async throws {
     97     let pause = removalPause
     98     removalPause = nil
     99     await pause?.wait()
    100     try Task.checkCancellation()
    101     if request.id == removalFailureID {
    102       throw unsupported()
    103     }
    104     preparedRemovals.append(request.id)
    105   }
    106 
    107   func failShutdownOnce(_ failure: TeraRuntimeFailure) {
    108     shutdownFailure = failure
    109   }
    110 
    111   func pauseProfile(_ pause: ResourceTestPause) {
    112     profilePause = pause
    113   }
    114 
    115   func saveProfileMetadata(input _: TeraProfileMetadataInput) async -> TeraProfileStatus {
    116     await profileMutation()
    117   }
    118 
    119   func advanceProfile(operationID _: String) async -> TeraProfileStatus {
    120     await profileMutation()
    121   }
    122 
    123   func cancelProfile(operationID _: String, expectedRevision _: UInt64) async -> TeraProfileStatus {
    124     await profileMutation()
    125   }
    126 
    127   private func profileMutation() async -> TeraProfileStatus {
    128     profileMutations += 1
    129     let pause = profilePause
    130     profilePause = nil
    131     await pause?.wait()
    132     return TeraProfileStatus(
    133       id: String(repeating: "a", count: 32), revision: UInt64(profileMutations),
    134       authorPublicKey: value.identity.publicKeyHex, state: .draft, deliveryID: nil,
    135       createdAtUnixMilliseconds: 1_800_000_000_000, updatedAtUnixMilliseconds: 1_800_000_000_000,
    136       settlement: nil
    137     )
    138   }
    139 
    140   func snapshot() async throws -> TeraRuntimeSnapshot {
    141     let pause = snapshotPause
    142     snapshotPause = nil
    143     await pause?.wait()
    144     guard shutdownCount == 0 else { throw unsupported() }
    145     return value
    146   }
    147 
    148   func todayPage(request _: TeraTodayPageRequest) throws -> TeraTodayPage {
    149     throw unsupported()
    150   }
    151 
    152   func refreshToday(
    153     context _: TeraLocalNetwork, nowUnixSeconds _: UInt64, update _: TeraTodayProjectionUpdate,
    154     backfillCursor _: String?
    155   ) throws -> TeraTodaySyncReceipt {
    156     throw unsupported()
    157   }
    158 
    159   func subscribe(
    160     bufferCapacity _: Int, receive: @escaping @Sendable (TeraRuntimeChange) async -> Void
    161   ) async -> any TeraRuntimeSubscriptionToken {
    162     self.receive = receive
    163     let pause = subscriptionPause
    164     subscriptionPause = nil
    165     await pause?.wait()
    166     return token
    167   }
    168 
    169   func emit(_ revision: UInt64) async {
    170     await receive?(TeraRuntimeChange(
    171       schemaVersion: 3,
    172       scope: TeraRuntimeChangeScope(publicKey: value.identity.publicKeyHex, sourceGeneration: value.identity.publicKeyHex, context: nil),
    173       epoch: String(repeating: "1", count: 32),
    174       revision: TeraProjectionRevision(rawValue: revision), delivery: .change, kind: .today, entityID: nil
    175     ))
    176   }
    177 
    178   func shutdown() async throws -> TeraRuntimeShutdownReceipt {
    179     shutdownCount += 1
    180     let pause = shutdownPause
    181     shutdownPause = nil
    182     await pause?.wait()
    183     if let failure = shutdownFailure {
    184       shutdownFailure = nil
    185       throw failure
    186     }
    187     let alreadyClosed = shutdownCompleted
    188     shutdownCompleted = true
    189     await closed.open()
    190     return TeraRuntimeShutdownReceipt(state: "closed", alreadyClosed: alreadyClosed)
    191   }
    192 
    193   func mobileSettings() async -> TeraMobileSettings {
    194     let pause = settingsPause
    195     settingsPause = nil
    196     await pause?.wait()
    197     return settings()
    198   }
    199 
    200   func applyIdentityCommand(
    201     expectedRevision _: UInt64, command _: TeraIdentityCommand
    202   ) -> TeraSettingsTransition {
    203     identityCommands += 1
    204     return TeraSettingsTransition(
    205       settings: settings(), runtimeRestartRequired: false,
    206       outboxRequeueRequired: false, mediaCacheInvalidationRequired: false
    207     )
    208   }
    209 
    210   private func settings() -> TeraMobileSettings {
    211     TeraMobileSettings(
    212       revision: 1,
    213       identity: TeraSettingsIdentityState(
    214         identities: [TeraSettingsIdentity(id: "identity", publicKeyHex: value.identity.publicKeyHex)],
    215         activeIdentityID: "identity", lockState: .unlocked, pendingImportOperationID: nil
    216       ),
    217       networkEnvironment: .simulator,
    218       relays: [TeraRelayPreference(url: "ws://127.0.0.1:7447", access: .readWrite)],
    219       blossomAuthority: .loopbackDevelopment, blossomPrimaryOrigin: "http://127.0.0.1:3000",
    220       blossomFallbackOrigins: [], allowCellularDownloads: true,
    221       allowCellularUploads: true, allowBackgroundTransfers: true,
    222       mediaCacheBytes: 256 * 1_048_576, mediaCacheArtifacts: 1024
    223     )
    224   }
    225 
    226   private func unsupported() -> TeraRuntimeFailure {
    227     .local(operation: "test.resource", code: "test.unavailable", safeMessage: "Test operation unavailable")
    228   }
    229 }