field_ios

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

TeraErrorRecoveryTests.swift (4260B)


      1 import Foundation
      2 @testable import TeraApp
      3 import TeraKitBindings
      4 import XCTest
      5 
      6 final class TeraErrorRecoveryTests: XCTestCase {
      7   @MainActor
      8   func testStableCodesDriveDistinctMessagesAndHonestStoreStates() {
      9     let cases: [(String, TeraUserMessageKey)] = [
     10       ("database_busy", .secureStateUnavailable),
     11       ("store_incomplete", .secureStateUnavailable),
     12       ("storage_integrity_failed", .secureStateUnavailable),
     13       ("identity_unavailable", .identityOperationFailed),
     14       ("protected_data_unavailable", .protectedDataUnavailable),
     15       ("storage_space_insufficient", .storageFull),
     16       ("schema_too_new", .versionUnsupported),
     17       ("cancelled_no_commit", .operationCancelled),
     18       ("ios.runtime.cancelled", .operationOutcomeUnknown),
     19       ("ios.runtime.deadline_exceeded", .operationOutcomeUnknown),
     20       ("idempotency_conflict", .operationConflict),
     21       ("draft_revision_conflict", .stateChanged),
     22       ("today_cursor_invalid", .todayChanged),
     23       ("relay_auth_required", .networkPolicy),
     24       ("relay_rate_limited", .networkPolicy),
     25       ("today_relay_partial", .partialResult),
     26       ("future_corrupt_network", .todayUnavailable),
     27     ]
     28     for (code, key) in cases {
     29       let failure = makeFailure(code: code)
     30       let wrapped = TeraRuntimeClientError.today(failure)
     31       XCTAssertEqual(TeraUserMessages.key(for: wrapped, fallback: .todayUnavailable), key, code)
     32       XCTAssertEqual(
     33         TeraTodayFailure(wrapped),
     34         code == "today_cursor_invalid"
     35           ? .staleCursor(message: TeraUserMessages.text(key))
     36           : .failed(message: TeraUserMessages.text(key)), code
     37       )
     38       XCTAssertEqual(TeraMediaStore.failureState(wrapped), .failed, code)
     39     }
     40     let offline = makeFailure(code: "today_relay_offline")
     41     XCTAssertEqual(
     42       TeraTodayFailure(offline),
     43       .offline(message: TeraUserMessages.text(.networkUnavailable))
     44     )
     45     XCTAssertEqual(
     46       TeraMediaStore.failureState(makeFailure(code: "blossom_transport_failed")),
     47       .networkUnavailable
     48     )
     49     XCTAssertEqual(TeraMediaStore.failureState(makeFailure(code: "today_media_corrupt")), .corrupt)
     50   }
     51 
     52   func testDiagnosticTextAndClaimedRetryActionsCannotChangeRecovery() {
     53     for code in ["database_busy", "today_relay_offline", "future_corrupt_network"] {
     54       let first = makeFailure(code: code)
     55       let second = TeraRuntimeFailure(
     56         schemaVersion: 1, code: code, category: "storage", retryable: false,
     57         recoveryActions: [], operationID: "another-diagnostic", capabilityID: nil,
     58         safeMessage: "Un texte traduit sans indication de réseau."
     59       )
     60       XCTAssertEqual(first.recovery, second.recovery)
     61       XCTAssertEqual(
     62         TeraUserMessages.key(for: first, fallback: .todayUnavailable),
     63         TeraUserMessages.key(for: second, fallback: .todayUnavailable)
     64       )
     65       XCTAssertFalse(TeraUserMessages.text(for: first, fallback: .todayUnavailable).contains(first.safeMessage))
     66     }
     67     let unknown = makeFailure(code: "future_corrupt_network")
     68     XCTAssertEqual(unknown.recovery.disposition, .unknown)
     69     XCTAssertEqual(unknown.recovery.retry, .notAllowed)
     70     let unsupported = makeFailure(code: "today_relay_offline", schemaVersion: 2)
     71     XCTAssertEqual(unsupported.recovery.disposition, .unsupportedVersion)
     72     XCTAssertEqual(unsupported.recovery.retry, .notAllowed)
     73   }
     74 
     75   func testCallerCancellationAndTimeoutRequireReconciliation() {
     76     for code in ["ios.runtime.cancelled", "ios.runtime.deadline_exceeded", "deadline_exceeded"] {
     77       XCTAssertEqual(makeFailure(code: code).recovery.retry, .reconcileExistingOperation)
     78     }
     79     XCTAssertEqual(
     80       TeraUserMessages.key(for: CancellationError(), fallback: .addOperationFailed),
     81       .operationOutcomeUnknown
     82     )
     83     XCTAssertEqual(makeFailure(code: "cancelled_no_commit").recovery.disposition, .cancelledBeforeEffect)
     84   }
     85 
     86   private func makeFailure(code: String, schemaVersion: UInt16 = 1) -> TeraRuntimeFailure {
     87     TeraRuntimeFailure(
     88       schemaVersion: schemaVersion, code: code, category: "network relay corrupt",
     89       retryable: true, recoveryActions: ["publish_again"], operationID: "existing-operation",
     90       capabilityID: "transport", safeMessage: "Texte localisé: offline retry corruption"
     91     )
     92   }
     93 }