Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaTests/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 6 kB image not shown  

Quelle  AppLaunchSequenceManagerTests.swift   Sprache: JAVA

 

import Foundation
import Testing
import ThreemaEssentials
import ThreemaFramework
@testable import Threema

@MainActor
struct AppLaunchSequenceManagerTests {

    // MARK: - Regression: the "open to fetch" push bug

    @Test("A transient keychain miss is NOT treated as a missing identity")
    func transientMissIsNotConfirmedMissing() async throws {
        // First read reports the identity as not-found (the transient), every retry finds it.
        let reader = IdentityReader(
            responses: [.missing],
            fallback: .identity(ThreemaIdentity("ECHOECHO"))
        )
        let manager = makeManager(reader: reader)

        let confirmedMissing = try await manager.isIdentityConfirmedMissing()

        // The identity is present, so the destructive AppSetupState reset must NOT run.
        #expect(confirmedMissing == false)
        // The transient miss triggered a retry that then found the identity.
        #expect(reader.callCount == 2)
    }

    // MARK: - Preserved behavior

    @Test("A persistent keychain miss IS treated as a missing identity (genuine device restore)")
    func persistentMissIsConfirmedMissing() async throws {
        // The identity stays missing across every attempt. This is a real restore.
        let reader = IdentityReader(responses: [], fallback: .missing)
        let manager = makeManager(reader: reader, attempts: 3)

        let confirmedMissing = try await manager.isIdentityConfirmedMissing()

        #expect(confirmedMissing == true)
        // Every attempt was used before concluding the identity is gone.
        #expect(reader.callCount == 3)
    }

    @Test("A present identity is confirmed on the first read, without retrying")
    func presentIdentityIsNotMissing() async throws {
        let reader = IdentityReader(responses: [], fallback: .identity(ThreemaIdentity("ECHOECHO")))
        let manager = makeManager(reader: reader)

        let confirmedMissing = try await manager.isIdentityConfirmedMissing()

        #expect(confirmedMissing == false)
        // The common launch path reads once and adds no retry latency.
        #expect(reader.callCount == 1)
    }

    @Test("A keychain read error is propagated, never mistaken for a missing identity")
    func keychainErrorPropagates() async {
        let reader = IdentityReader(responses: [], fallback: .failure(KeychainReadError()))
        let manager = makeManager(reader: reader)

        // A throwing read must surface as an error (routed to `.failed(.keychainError)` by `run()`),
        // not silently collapse to "identity missing" and reset the setup state.
        await #expect(throws: KeychainReadError.self) {
            try await manager.isIdentityConfirmedMissing()
        }
    }
    
    // MARK: - Regression: an undetermined setup state must not start onboarding

    @Test("An unreadable keychain surfaces as a retryable keychain error, not as onboarding")
    func unreadableStateDoesNotStartOnboarding() async {
        let manager = makeManager(
            reader: IdentityReader(responses: [], fallback: .identity(ThreemaIdentity("ECHOECHO"))),
            appSetupState: (.notSetup, .keychainUnreadable)
        )

        let result = await manager.run()

        guard case let .failed(error) = result, case .keychainError = error else {
            Issue.record("Expected .failed(.keychainError), got \(result)")
            return
        }
        #expect(error.isRetryable)
    }

    @Test("A state read blocked by protected data surfaces as .protectedDataUnavailable")
    func protectedDataUnavailableStateWaitsInsteadOfOnboarding() async {
        let manager = makeManager(
            reader: IdentityReader(responses: [], fallback: .identity(ThreemaIdentity("ECHOECHO"))),
            // The early `isKeychainLocked` check passes; only the later state read is blocked.
            appSetupState: (.notSetup, .protectedDataUnavailable)
        )

        let result = await manager.run()

        guard case .protectedDataUnavailable = result else {
            Issue.record("Expected .protectedDataUnavailable, got \(result)")
            return
        }
    }

    @Test("A determined .notSetup still leads to onboarding")
    func determinedNotSetupStartsOnboarding() async {
        let appLaunchManager = AppLaunchManagerMock()
        appLaunchManager.isAppSetupCompleted = false
        // Keeps the Remote Secret cleanup that follows the state check from touching the test host's real
        // keychain; this test is only about which result a determined `.notSetup` produces.
        appLaunchManager.hasPreexistingDatabaseFile = true

        let manager = makeManager(
            reader: IdentityReader(responses: [], fallback: .identity(ThreemaIdentity("ECHOECHO"))),
            appSetupState: (.notSetup, nil),
            appLaunchManager: appLaunchManager
        )

        let result = await manager.run()

        guard case .needsOnboarding = result else {
            Issue.record("Expected .needsOnboarding, got \(result)")
            return
        }
    }

    // MARK: - Test helpers

    private final class IdentityReader {
        enum Response {
            case identity(ThreemaIdentity)
            case missing
            case failure(any Error)
        }

        private var responses: [Response]
        private let fallback: Response
        private(set) var callCount = 0

        init(responses: [Response], fallback: Response) {
            self.responses = responses
            self.fallback = fallback
        }

        func read() throws -> ThreemaIdentity? {
            callCount += 1
            let response = responses.isEmpty ? fallback : responses.removeFirst()
            switch response {
            case let .identity(identity):
                return identity
            case .missing:
                return nil
            case let .failure(error):
                throw error
            }
        }
    }

    private struct KeychainReadError: Error { }

    private func makeManager(
        reader: IdentityReader,
        attempts: Int = 3,
        appSetupState: (state: AppSetupState, unavailable: AppSetup.StateUnavailableReason?) = (.notSetup, nil),
        appLaunchManager: AppLaunchManagerMock? = nil
    ) -> AppLaunchSequenceManager {
        AppLaunchSequenceManager(
            bootstrap: .mock(appLaunchManager: appLaunchManager),
            loadThreemaIdentity: { try reader.read() },
            resolveAppSetupState: { appSetupState },
            missingIdentityConfirmationAttempts: attempts,
            // No delay so the retries do not slow the test down.
            missingIdentityConfirmationDelay: nil
        )
    }
}

Messung
CHZG
919410094

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.