TeraSubmissionAdmissionTests.swift (4048B)
1 import Foundation 2 @testable import TeraApp 3 import XCTest 4 5 final class TeraSubmissionAdmissionTests: XCTestCase { 6 func testTimeoutOrCancellationReturnsPromptlyButKeepsAdmissionUntilActualCallback() async throws { 7 for cancel in [false, true] { 8 let backend = AddBackend() 9 let client = TeraRuntimeClient(factory: { _ in 10 await TeraRuntimeBackendStart(backend: backend, snapshot: backend.snapshot()) 11 }, deadlines: TeraRuntimeDeadlinePolicy(operationNanoseconds: 100_000_000)) 12 _ = try await client.start(configuration: TeraAddStoreTests.configuration()) 13 let scope = TeraComposerScope(authorPublicKey: String(repeating: "ab", count: 32), localNetworkID: "nearby") 14 var form = TeraComposerForm(commandType: .createUpdate) 15 form.content = "original" 16 let saved = try await client.saveComposer(request: TeraComposerSaveRequest( 17 scope: scope, id: client.reserveComposerID(), expectedRevision: nil, editSequence: 1, form: form 18 )) 19 let request = try await TeraSubmissionRequest(commandID: client.reserveSubmissionID(), scope: scope, 20 composerID: saved.draft.id, expectedRevision: saved.draft.revision) 21 let pause = ResourceTestPause() 22 await backend.submissionBackend.pausePrepare(pause) 23 let first = Task { try await client.prepareSubmission(request: request, media: []) } 24 let entered = expectation(description: "Reached the original submission call") 25 let observer = Task { await pause.entered.wait(); entered.fulfill() } 26 await fulfillment(of: [entered], timeout: 3) 27 await pause.entered.open() 28 await observer.value 29 if cancel { 30 first.cancel() 31 } 32 await assertBoundedReturn(first, cancelled: cancel) 33 await assertHeldAdmission(client, request: request) 34 let calls = await backend.submissionBackend.prepareCount 35 XCTAssertEqual(calls, 1) 36 await pause.resume.open() 37 var recovered: TeraSubmissionStatus? 38 let deadline = ContinuousClock.now.advanced(by: .seconds(3)) 39 while recovered == nil, ContinuousClock.now < deadline { 40 recovered = try? await client.recoverSubmission(request: request) 41 if recovered == nil { 42 try await Task.sleep(for: .milliseconds(1)) 43 } 44 } 45 let original = try XCTUnwrap(recovered) 46 let replay = try await client.prepareSubmission(request: request, media: []) 47 XCTAssertEqual(replay, original) 48 XCTAssertEqual(replay.request, request) 49 XCTAssertEqual(replay.captured, saved.draft) 50 let operations = await backend.submissionBackend.operationCount 51 XCTAssertEqual(operations, 1) 52 _ = try await client.stop() 53 } 54 } 55 56 private func assertHeldAdmission(_ client: TeraRuntimeClient, request: TeraSubmissionRequest) async { 57 for _ in 0 ..< 100 { 58 do { 59 _ = try await client.prepareSubmission(request: request, media: []) 60 XCTFail("A late original callback must retain admission") 61 } catch { XCTAssertEqual(TeraAddPresentation.failure(for: error)?.code, "operation_in_progress") } 62 } 63 do { 64 _ = try await client.recoverSubmission(request: request) 65 XCTFail("Recovery cannot infer absence while the original call is unfinished") 66 } catch { XCTAssertEqual(TeraAddPresentation.failure(for: error)?.code, "operation_in_progress") } 67 } 68 69 private func assertBoundedReturn(_ first: Task<TeraSubmissionStatus, Error>, cancelled cancel: Bool) async { 70 let returned = expectation(description: "Native waiter keeps its original deadline") 71 let waiter = Task { 72 do { 73 _ = try await first.value 74 XCTFail("Expected bounded wait to end") 75 } catch { 76 XCTAssertEqual(TeraAddPresentation.failure(for: error)?.code, 77 cancel ? "ios.runtime.cancelled" : "ios.runtime.deadline_exceeded") 78 } 79 returned.fulfill() 80 } 81 await fulfillment(of: [returned], timeout: 2) 82 await waiter.value 83 } 84 }