field_ios

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

TeraRestoreRecoveryStoreTests.swift (4522B)


      1 @testable import TeraApp
      2 import XCTest
      3 
      4 @MainActor
      5 final class TeraRestoreRecoveryStoreTests: XCTestCase {
      6   func testStatusAndReviewNeverResumeWithoutTheSeparateAction() async throws {
      7     let backend = try TeraScopeBackend()
      8     await backend.restoreStorage.configure(targets: [])
      9     let client = try await TeraScopeFixtures.client(backend)
     10     let store = TeraRestoreRecoveryStore(client: client)
     11     await store.load()
     12     XCTAssertEqual(store.status?.phase, .held)
     13     XCTAssertNil(store.reviewedInventory)
     14     await store.review()
     15     XCTAssertEqual(store.status?.phase, .reviewed)
     16     XCTAssertEqual(store.reviewedInventory, "reviewed")
     17     let before = await backend.restoreStorage.calls
     18     XCTAssertEqual(before, ["review"])
     19     await store.resume()
     20     XCTAssertEqual(store.status?.phase, .resumed)
     21     XCTAssertNil(store.reviewedInventory)
     22     let after = await backend.restoreStorage.calls
     23     XCTAssertEqual(after, ["review", "resume"])
     24     _ = try await client.stop()
     25   }
     26 
     27   func testOneActionChecksOneDestinationAndDoesNotStarveUncheckedWork() async throws {
     28     let backend = try TeraScopeBackend()
     29     await backend.restoreStorage.configure(targets: [
     30       TeraRestoreTarget(draftID: "offline", eventID: "a", targetFingerprint: "first", observation: .incomplete),
     31       TeraRestoreTarget(draftID: "unchecked", eventID: "b", targetFingerprint: "second", observation: nil),
     32     ])
     33     let client = try await TeraScopeFixtures.client(backend)
     34     let store = TeraRestoreRecoveryStore(client: client)
     35     await store.load()
     36     await store.checkNext()
     37     let first = await backend.restoreStorage.calls
     38     XCTAssertEqual(first, ["check:unchecked"])
     39     XCTAssertNil(store.reviewedInventory)
     40     await store.checkNext()
     41     let second = await backend.restoreStorage.calls
     42     XCTAssertEqual(second, ["check:unchecked", "check:offline"])
     43     _ = try await client.stop()
     44   }
     45 
     46   func testAccountInvalidationDiscardsLateReviewAndCannotExposeResume() async throws {
     47     let backend = try TeraScopeBackend()
     48     let pause = ResourceTestPause()
     49     await backend.restoreStorage.configure(targets: [], pause: pause)
     50     let client = try await TeraScopeFixtures.client(backend)
     51     let store = TeraRestoreRecoveryStore(client: client)
     52     await store.load()
     53     let task = Task { await store.review() }
     54     await pause.entered.wait()
     55     store.invalidate()
     56     await pause.resume.open()
     57     await task.value
     58     XCTAssertNil(store.status)
     59     XCTAssertNil(store.reviewedInventory)
     60     await store.resume()
     61     let calls = await backend.restoreStorage.calls
     62     XCTAssertEqual(calls, ["review"])
     63     _ = try await client.stop()
     64   }
     65 }
     66 
     67 actor RestoreTestStorage {
     68   private var value: TeraRestoreStatus?
     69   private var pause: ResourceTestPause?
     70   private(set) var calls: [String] = []
     71   func configure(targets: [TeraRestoreTarget], pause: ResourceTestPause? = nil) {
     72     value = TeraRestoreStatus(attemptID: "fixture", phase: .held, targets: targets)
     73     self.pause = pause
     74   }
     75 
     76   func status() -> TeraRestoreStatus? {
     77     value
     78   }
     79 
     80   func check(_ target: TeraRestoreTarget) {
     81     calls.append("check:\(target.draftID)")
     82     guard let value else { return }
     83     self.value = TeraRestoreStatus(attemptID: value.attemptID, phase: value.phase, targets: value.targets.map {
     84       $0 == target ? TeraRestoreTarget(draftID: $0.draftID, eventID: $0.eventID, targetFingerprint: $0.targetFingerprint, observation: .notObserved) : $0
     85     })
     86   }
     87 
     88   func review() async throws -> String {
     89     calls.append("review")
     90     if let pause {
     91       await pause.wait()
     92     }
     93     guard let value else { throw TeraComposerAcknowledgment.unconfirmed }
     94     self.value = TeraRestoreStatus(attemptID: value.attemptID, phase: .reviewed, targets: value.targets)
     95     return "reviewed"
     96   }
     97 
     98   func resume(_ digest: String) throws {
     99     guard digest == "reviewed", let value, value.phase == .reviewed else { throw TeraComposerAcknowledgment.unconfirmed }
    100     calls.append("resume")
    101     self.value = TeraRestoreStatus(attemptID: value.attemptID, phase: .resumed, targets: value.targets)
    102   }
    103 }
    104 
    105 extension TeraScopeBackend {
    106   func restoreStatus() async -> TeraRestoreStatus? {
    107     await restoreStorage.status()
    108   }
    109 
    110   func reconcileRestoredTarget(_ target: TeraRestoreTarget) async {
    111     await restoreStorage.check(target)
    112   }
    113 
    114   func reviewRestoredWork() async throws -> String {
    115     try await restoreStorage.review()
    116   }
    117 
    118   func resumeRestoredWork(reviewedInventory: String) async throws {
    119     try await restoreStorage.resume(reviewedInventory)
    120   }
    121 }