field_ios

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

TeraRecoveryInventoryTests.swift (7270B)


      1 import Foundation
      2 import RadrootsKit
      3 @testable import TeraApp
      4 import TeraKitBindings
      5 import XCTest
      6 
      7 final class TeraRecoveryInventoryTests: XCTestCase {
      8   func testGeneratedInventoryAndExactParentPreserveScopedOperationIdentity() async throws {
      9     let fixture = try MediaOwnershipFixture()
     10     defer { fixture.remove() }
     11     let runtime = try await fixture.runtime()
     12     try await runtime.configureSimulatorRelays(loopbackRelays: ["ws://127.0.0.1:19999"])
     13     let backend = TeraGeneratedRuntimeBackend(runtime: runtime)
     14     let scope = TeraComposerScope(authorPublicKey: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", localNetworkID: "other-context")
     15     var form = TeraComposerForm(commandType: .createUpdate)
     16     form.content = "saved operation outside display selection"
     17     let source = try await backend.saveComposer(request: TeraComposerSaveRequest(
     18       scope: scope, id: backend.reserveComposerID(), expectedRevision: nil, editSequence: 1, form: form
     19     ))
     20     let request = try await TeraSubmissionRequest(commandID: backend.reserveSubmissionID(), scope: scope, composerID: source.draft.id, expectedRevision: 1)
     21     let status = try await backend.prepareSubmission(request: request, media: [])
     22     let exact = try await backend.recoveryParent(key: status.intentID)
     23     XCTAssertEqual(exact?.owner, .submission(request))
     24     let display = try await backend.draftHeads(limit: 100)
     25     XCTAssertTrue(display.isEmpty)
     26     var cursor: String?
     27     var entries: [TeraRecoveryEntry] = []
     28     var scanned = 0
     29     repeat {
     30       let page = try await backend.recoveryPage(limit: 1, cursor: cursor)
     31       XCTAssertEqual(page.author, scope.authorPublicKey)
     32       entries += page.entries
     33       scanned += Int(page.scanned)
     34       cursor = page.nextCursor
     35     } while cursor != nil
     36     XCTAssertEqual(scanned, 3)
     37     XCTAssertEqual(entries, [exact].compactMap(\.self))
     38     do {
     39       _ = try await runtime.recoveryPage(schemaVersion: 2, limit: 1, cursor: nil)
     40       XCTFail("Unknown inventory version must fail")
     41     } catch let TeraAppError.Failure(report) {
     42       XCTAssertEqual(report.code, "unsupported_schema_version")
     43     }
     44     _ = try await runtime.shutdown()
     45   }
     46 
     47   func testThousandNativeOwnersContinueBeyondDisplayLimitAndIsolateMissingParent() async throws {
     48     let fixture = try BackgroundUploadFixture()
     49     defer { fixture.remove() }
     50     let transfer = BackgroundTransferHarness()
     51     let request = try fixture.request(job: fixture.job(revision: 2, operation: String(repeating: "a", count: 32)))
     52     let verified = TeraNativeUploadRecoveryOwner(draft: fixture.draft(revision: 3, stage: .verified))
     53     for number in 1 ... 1000 {
     54       let identifier = try RadrootsBackgroundTransferIdentifier("radroots.add.\(Self.key(number)).2.\(String(repeating: "a", count: 32))")
     55       try await transfer.seed(request: TeraBackgroundUploadRequest.replacingIdentifier(in: request, with: identifier), state: .awaitingVerification)
     56     }
     57     let seen = RecoveryLookups()
     58     var cursor: String?
     59     var visited = 0
     60     var passes = 0
     61     repeat {
     62       let result = try await RecoverySettlementFixture.run(transfer: transfer, cursor: cursor) { key in
     63         await seen.append(key)
     64         if key == Self.key(1) {
     65           return nil
     66         }
     67         return Self.owner(key: key, template: verified)
     68       }
     69       XCTAssertLessThanOrEqual(result.progress.visited, 64)
     70       visited += result.progress.visited
     71       passes += 1
     72       cursor = result.cursor
     73       if passes == 1 {
     74         XCTAssertEqual(result.progress.remaining, 936)
     75       }
     76     } while cursor != nil
     77     XCTAssertEqual(visited, 1000)
     78     XCTAssertEqual(passes, 16)
     79     let keys = await seen.keys
     80     XCTAssertEqual(Set(keys), Set((1 ... 1000).map(Self.key)))
     81     XCTAssertEqual(keys.count, 1000)
     82     let counts = await transfer.counts
     83     XCTAssertEqual(counts.acceptedSettlement, 999)
     84     XCTAssertEqual(counts.enqueue, 0)
     85     XCTAssertEqual(counts.retry, 0)
     86     let revisited = try await Self.revisit(transfer: transfer, verified: verified)
     87     XCTAssertEqual(revisited, 1000, "Completed positions remain part of the bounded live sweep")
     88     let finalCounts = await transfer.counts
     89     XCTAssertEqual(finalCounts.acceptedSettlement, 1000)
     90   }
     91 
     92   private static func revisit(transfer: BackgroundTransferHarness, verified: TeraNativeUploadRecoveryOwner) async throws -> Int {
     93     var cursor: String?
     94     var revisited = 0
     95     repeat {
     96       let fresh = try await RecoverySettlementFixture.run(transfer: transfer, cursor: cursor) { key in
     97         Self.owner(key: key, template: verified)
     98       }
     99       XCTAssertLessThanOrEqual(fresh.progress.visited, 64)
    100       XCTAssertFalse(fresh.progress.needsAttention)
    101       revisited += fresh.progress.visited
    102       cursor = fresh.cursor
    103     } while cursor != nil
    104     return revisited
    105   }
    106 
    107   func testCancelledExactLookupRetainsReceiptAndFreshPassRecovers() async throws {
    108     let fixture = try BackgroundUploadFixture()
    109     defer { fixture.remove() }
    110     let transfer = BackgroundTransferHarness()
    111     let request = try fixture.request(job: fixture.job(revision: 2, operation: String(repeating: "a", count: 32)))
    112     try await transfer.seed(request: request, state: .awaitingVerification)
    113     let pause = ResourceTestPause()
    114     let owner = TeraNativeUploadRecoveryOwner(draft: fixture.draft(revision: 3, stage: .verified))
    115     let task = Task {
    116       try await RecoverySettlementFixture.run(transfer: transfer, cursor: nil) { _ in
    117         await pause.wait()
    118         return owner
    119       }
    120     }
    121     await pause.entered.wait()
    122     task.cancel()
    123     await pause.resume.open()
    124     do { _ = try await task.value; XCTFail("Cancelled pass must not settle") } catch is CancellationError {}
    125     let counts = await transfer.counts
    126     XCTAssertEqual(counts.acceptedSettlement, 0)
    127     let result = try await RecoverySettlementFixture.run(transfer: transfer, cursor: nil) { _ in owner }
    128     XCTAssertEqual(result.progress, .init(visited: 1, remaining: 0, needsAttention: false))
    129   }
    130 
    131   func testMismatchedExactParentRemainsUnsettled() async throws {
    132     let fixture = try BackgroundUploadFixture()
    133     defer { fixture.remove() }
    134     let transfer = BackgroundTransferHarness()
    135     try await transfer.seed(request: fixture.request(job: fixture.job(revision: 2, operation: String(repeating: "a", count: 32))), state: .awaitingVerification)
    136     let owner = TeraNativeUploadRecoveryOwner(draft: fixture.draft(revision: 3, stage: .verified))
    137     let result = try await RecoverySettlementFixture.run(transfer: transfer, cursor: nil) { _ in
    138       Self.owner(key: Self.key(7), template: owner)
    139     }
    140     XCTAssertTrue(result.progress.needsAttention)
    141     let counts = await transfer.counts
    142     XCTAssertEqual(counts.acceptedSettlement, 0)
    143     XCTAssertEqual(counts.cancel, 0)
    144   }
    145 
    146   private static func key(_ value: Int) -> String {
    147     String(format: "%032x", value)
    148   }
    149 
    150   private static func owner(key: String, template: TeraNativeUploadRecoveryOwner) -> TeraNativeUploadRecoveryOwner {
    151     .init(id: key, revision: template.revision, media: template.media, verifiedURLs: template.verifiedURLs, uploadURLs: template.uploadURLs)
    152   }
    153 }
    154 
    155 private actor RecoveryLookups {
    156   private(set) var keys: [String] = []
    157   func append(_ key: String) {
    158     keys.append(key)
    159   }
    160 }