field_ios

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

TeraPublicationStageTests.swift (9332B)


      1 import Foundation
      2 @testable import TeraApp
      3 import XCTest
      4 
      5 @MainActor
      6 final class TeraPublicationStageTests: XCTestCase {
      7   func testEveryDurablePhaseHasScopedStageAndEligibleActions() async throws {
      8     let backend = AddBackend(advanceOffline: true)
      9     let client = try await TeraAddStoreTests.startedClient(backend)
     10     let store = TeraAddStore(runtimeClient: client)
     11     await store.configure(snapshot: backend.snapshot())
     12     await store.start()
     13     store.updateForm(\.content, "Saved original")
     14     await store.submit()
     15     let original = try XCTUnwrap(store.submissions.status)
     16     let expected: [(TeraOutboxState, String)] = [
     17       (.draft, "Saved on this device."), (.mediaPreparing, "Preparing photo."),
     18       (.mediaUploading, "Photo upload awaiting verification."),
     19       (.readyToSign, "Awaiting signing."), (.signing, "Awaiting signing."),
     20       (.signed, "Signed; local admission is pending."), (.queued, "Queued for the saved relays."),
     21       (.delivering, "Sending to the saved relays."),
     22       (.partiallyDelivered, "Partially delivered; review the saved relay outcomes."),
     23       (.retryable, "Saved for retry."), (.terminal, "Publication needs attention."),
     24       (.cancelled, "Local work stopped. Recorded remote effects are retained."),
     25       (.complete, "Delivery completed for the saved relay policy."),
     26     ]
     27     XCTAssertEqual(Set(expected.map(\.0)), Set(TeraOutboxState.allCases))
     28     for (state, label) in expected {
     29       let value = replacing(original, state: state)
     30       XCTAssertEqual(value.summary, label)
     31       XCTAssertEqual(state.summary(settlement: nil), label)
     32       XCTAssertEqual(value.canOfferContinuation, ![.complete, .cancelled, .terminal].contains(state))
     33       XCTAssertEqual(value.canOfferStop, state.canCancel)
     34     }
     35     store.stop()
     36     _ = try await client.stop()
     37   }
     38 
     39   func testLegacySatisfiedPlanDoesNotHideMixedOrStoppedEffects() {
     40     let settlement = TeraOperationSettlement(
     41       artifacts: 7, signed: 4, admitted: 3, pending: 1, retryable: 1,
     42       indeterminate: 1, failedTerminal: 1, cancelled: 1,
     43       deliveryPlans: 6, deliverySatisfied: 1, deliveryPending: 1,
     44       deliveryRetryable: 1, deliveryExhausted: 1, deliveryFailedTerminal: 1, deliveryCancelled: 1
     45     )
     46     let summary = TeraOutboxState.cancelled.summary(settlement: settlement)
     47     for retained in ["Local work stopped", "1 of 6", "unknown", "pending", "saved for retry",
     48                      "limit was reached", "failure", "effects are retained", "4 signed", "3 admitted locally"]
     49     {
     50       XCTAssertTrue(summary.contains(retained), retained)
     51     }
     52     XCTAssertFalse(summary.contains("Delivery completed"))
     53     XCTAssertFalse(summary.contains("Published"))
     54     let draft = TeraDraftStatus(
     55       id: "draft", revision: 1, authorPublicKey: "author", kind: .add, commandType: .createUpdate,
     56       form: nil, state: .cancelled, cardID: "card", operationID: "operation",
     57       createdAtUnixMilliseconds: 1, updatedAtUnixMilliseconds: 2,
     58       media: [], settlement: settlement, isRevision: false
     59     )
     60     XCTAssertEqual(draft.honestSummary, summary)
     61     let legacy = TeraLegacyDraftSummary(
     62       id: draft.id, revision: 1, kind: .add, commandType: .createUpdate, state: .cancelled,
     63       hasForm: false, isRevision: false, createdAtUnixMilliseconds: 1, updatedAtUnixMilliseconds: 2,
     64       mediaCount: 0, verifiedMediaCount: 0, possibleOrphanCount: 0, settlement: settlement
     65     )
     66     XCTAssertEqual(legacy.honestSummary, summary)
     67   }
     68 
     69   func testMainSubmitAndAccessibleStatusHonorEverySavedHoldWithoutEffects() async throws {
     70     let backend = AddBackend(advanceOffline: true)
     71     let client = try await TeraAddStoreTests.startedClient(backend)
     72     let store = TeraAddStore(runtimeClient: client)
     73     await store.configure(snapshot: backend.snapshot())
     74     await store.start()
     75     store.updateForm(\.content, "Saved original")
     76     await store.submit()
     77     let original = try XCTUnwrap(store.submissions.status)
     78     let before = await backend.submissionBackend.advanceCount
     79     let decisions: [(TeraPublicationRetry, String)] = [
     80       (.deferredUntil(1), "Publication waiting"), (.inFlightUntil(1), "Publication waiting"),
     81       (.needsAction(.deadlineExceeded), "Publication needs attention"),
     82       (.needsAction(.attemptLimit), "Publication needs attention"),
     83       (.needsAction(.coordinateChanged), "Publication needs attention"),
     84       (.needsAction(.authenticationRequired), "Publication needs attention"),
     85       (.needsAction(.quotaExceeded), "Publication needs attention"),
     86       (.needsAction(.invalidPayload), "Publication needs attention"),
     87       (.needsAction(.deliveryRefused), "Publication needs attention"),
     88     ]
     89     for (retry, label) in decisions {
     90       let value = replacing(original, retry: retry)
     91       await backend.submissionBackend.installPresentationFixture(value)
     92       await store.submissions.refreshSelected()
     93       XCTAssertFalse(store.canSubmit)
     94       XCTAssertFalse(store.submissions.canContinue)
     95       XCTAssertEqual(store.submitLabel, label)
     96       XCTAssertTrue(try store.submitAccessibilityValue.contains(XCTUnwrap(retry.explanation)))
     97       await store.submit()
     98       await store.submissions.continueSelected()
     99       XCTAssertEqual(store.submissions.status?.request, original.request)
    100       XCTAssertEqual(store.submissions.status?.captured, original.captured)
    101     }
    102     let after = await backend.submissionBackend.advanceCount
    103     XCTAssertEqual(after, before)
    104     await backend.submissionBackend.installPresentationFixture(original)
    105     await store.submissions.refreshSelected()
    106     XCTAssertTrue(store.canSubmit)
    107     XCTAssertEqual(store.submitLabel, "Continue original submission")
    108     store.stop()
    109     _ = try await client.stop()
    110   }
    111 
    112   func testAcceptedCompletionAndStoppedEffectsRemainAccessibleWithoutRetry() async throws {
    113     let backend = AddBackend()
    114     let client = try await TeraAddStoreTests.startedClient(backend)
    115     let store = TeraAddStore(runtimeClient: client)
    116     await store.configure(snapshot: backend.snapshot())
    117     await store.start()
    118     store.updateForm(\.content, "Saved original")
    119     await store.submit()
    120     XCTAssertEqual(store.submissions.status?.state, .complete)
    121     XCTAssertFalse(store.canSubmit)
    122     XCTAssertEqual(store.submitLabel, "Delivery policy complete")
    123     XCTAssertTrue(store.submitAccessibilityValue.contains("saved relay policy"))
    124     XCTAssertFalse(try XCTUnwrap(store.submissions.status).canOfferStop)
    125     // A previously requested stop can arrive after acceptance; preserve both.
    126     await store.submissions.requestStop()
    127     XCTAssertFalse(store.canSubmit)
    128     XCTAssertEqual(store.submitLabel, "Publication stopped")
    129     XCTAssertTrue(store.submitAccessibilityValue.contains("Stopped"))
    130     XCTAssertTrue(store.submitAccessibilityValue.contains("accepted"))
    131     XCTAssertTrue(try XCTUnwrap(store.submissions.status).targetDetails.targets[0].accepted)
    132     await store.submissions.refreshSelected()
    133     XCTAssertTrue(store.submitAccessibilityValue.contains("accepted"))
    134     store.stop()
    135     _ = try await client.stop()
    136   }
    137 
    138   func testPartialUnknownAndStoppedFactsTakePrecedenceOverNominalStage() async throws {
    139     let backend = AddBackend(advanceOffline: true)
    140     let client = try await TeraAddStoreTests.startedClient(backend)
    141     let store = TeraAddStore(runtimeClient: client)
    142     await store.configure(snapshot: backend.snapshot())
    143     await store.start()
    144     store.updateForm(\.content, "Saved original")
    145     await store.submit()
    146     let original = try XCTUnwrap(store.submissions.status)
    147     for delivery in [TeraPublicationDeliveryState.unknown, .partiallyAccepted, .accepted] {
    148       let evidence = TeraPublicationEvidence(
    149         state: delivery, stopRequestedAtUnixMilliseconds: nil, schedulingRevision: 2,
    150         retainedFacts: 1, recordedAttempts: 1, unresolvedClaims: true
    151       )
    152       let current = replacing(original, state: .complete, delivery: evidence)
    153       XCTAssertTrue(current.canOfferStop)
    154       XCTAssertFalse(current.canOfferContinuation)
    155       if delivery != .accepted {
    156         XCTAssertFalse(current.summary.contains("completed"))
    157       }
    158       let stopped = TeraPublicationEvidence(
    159         state: delivery, stopRequestedAtUnixMilliseconds: 10, schedulingRevision: 3,
    160         retainedFacts: 1, recordedAttempts: 1, unresolvedClaims: true
    161       )
    162       let value = replacing(original, state: .cancelled, delivery: stopped)
    163       XCTAssertEqual(value.summary, stopped.stoppedSummary)
    164       XCTAssertFalse(value.canOfferContinuation)
    165       XCTAssertFalse(value.canOfferStop)
    166     }
    167     store.stop()
    168     _ = try await client.stop()
    169   }
    170 
    171   private func replacing(_ original: TeraSubmissionStatus, state: TeraOutboxState = .queued,
    172                          retry: TeraPublicationRetry = .ready, delivery: TeraPublicationEvidence? = nil) -> TeraSubmissionStatus
    173   {
    174     TeraSubmissionStatus(
    175       request: original.request, intentID: original.intentID, operationID: original.operationID,
    176       revision: original.revision, captured: original.captured, state: state,
    177       committedAtUnixMilliseconds: original.committedAtUnixMilliseconds,
    178       updatedAtUnixMilliseconds: original.updatedAtUnixMilliseconds,
    179       media: original.media, settlement: original.settlement, delivery: delivery ?? original.delivery,
    180       targetDetails: original.targetDetails, retry: retry
    181     )
    182   }
    183 }