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 }