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

Quelle  LicenseServiceTests.swift   Sprache: JAVA

 

import Foundation
import Testing
import ThreemaFramework
@testable import Threema

@MainActor
struct LicenseServiceTests {

    // MARK: - Validation

    @Test("A private app is licensed without a license check")
    func validatesPrivateAppWithoutCheck() async {
        let licenseStore = LicenseStoreAdapterMock()
        let service = makeService(licenseStore: licenseStore, flavorService: .threema)

        #expect(await service.validate() == .licensed)
        #expect(licenseStore.performLicenseCheckCount == 0)
    }

    @Test("A valid license is not checked again")
    func validatesValidLicenseWithoutCheck() async {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.isValid = true
        let service = makeService(licenseStore: licenseStore)

        #expect(await service.validate() == .licensed)
        #expect(licenseStore.performLicenseCheckCount == 0)
    }

    @Test("An unchecked license is checked", arguments: [true, false])
    func validatesUncheckedLicense(isLicensed: Bool) async {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckResult = isLicensed
        let service = makeService(licenseStore: licenseStore)

        #expect(await service.validate() == (isLicensed ? .licensed : .notLicensed))
        #expect(licenseStore.performLicenseCheckCount == 1)
    }

    // MARK: - License Activation Link

    @Test("Credentials of a license activation link are written to the license store")
    func appliesCredentials() {
        let licenseStore = LicenseStoreAdapterMock()
        let service = makeService(licenseStore: licenseStore)

        let result = service.apply(
            LicenseCredentials(username: "test-user", password: "test-password", server: nil)
        )

        #expect(result == .complete)
        #expect(licenseStore.licenseStoreMock.licenseUsername == "test-user")
        #expect(licenseStore.licenseStoreMock.licensePassword == "test-password")
    }

    @Test("An incomplete license activation link is reported as such")
    func appliesIncompleteCredentials() {
        let service = makeService()

        let result = service.apply(
            LicenseCredentials(username: "test-user", password: nil, server: nil)
        )

        #expect(result == .incomplete)
    }

    /// The result of a check that started before the link arrived belongs to the credentials from
    /// before, so it must not decide whether the license entry screen is shown.
    @Test("A check repeats for credentials that arrive while it runs")
    func repeatsCheckForCredentialsArrivingDuringCheck() async {
        let gate = CheckGate()
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckHandler = { [gate] callNumber in
            guard callNumber == 1 else {
                // Check for the credentials of the link
                return true
            }

            // Check for the credentials from before the link
            await gate.startAndWait()
            return false
        }
        let service = makeService(licenseStore: licenseStore)

        let validation = Task { await service.validate() }
        await gate.waitUntilStarted()

        #expect(
            service.apply(
                LicenseCredentials(username: "test-user", password: "test-password", server: nil)
            ) == .complete
        )

        gate.release()

        #expect(await validation.value == .licensed)
        #expect(licenseStore.performLicenseCheckCount == 2)
    }

    @Test("A check doesn't repeat for an incomplete license activation link")
    func doesNotRepeatCheckForIncompleteCredentials() async {
        let gate = CheckGate()
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckResult = false
        licenseStore.performLicenseCheckHandler = { [gate] _ in
            await gate.startAndWait()
            return false
        }
        let service = makeService(licenseStore: licenseStore)

        let validation = Task { await service.validate() }
        await gate.waitUntilStarted()

        #expect(
            service.apply(
                LicenseCredentials(username: "test-user", password: nil, server: nil)
            ) == .incomplete
        )

        gate.release()

        #expect(await validation.value == .notLicensed)
        #expect(licenseStore.performLicenseCheckCount == 1)
    }

    @Test("Only one validation runs at a time")
    func rejectsValidationWhileValidating() async {
        let gate = CheckGate()
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckHandler = { [gate] _ in
            await gate.startAndWait()
            return true
        }
        let service = makeService(licenseStore: licenseStore)

        let validation = Task { await service.validate() }
        await gate.waitUntilStarted()

        #expect(await service.validate() == .alreadyValidating)
        #expect(licenseStore.performLicenseCheckCount == 1)

        gate.release()

        #expect(await validation.value == .licensed)
    }

    @Test("A validation runs again after the previous one finished")
    func validatesAgainAfterValidation() async {
        let licenseStore = LicenseStoreAdapterMock()
        let service = makeService(licenseStore: licenseStore)

        #expect(await service.validate() == .licensed)
        #expect(await service.validate() == .licensed)
        #expect(licenseStore.performLicenseCheckCount == 2)
    }

    // MARK: - Stored Credentials

    @Test("Stored credentials of a previous installation are restored")
    func restoresStoredCredentials() {
        let licenseStore = LicenseStoreAdapterMock()
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(
            licenseStore: licenseStore,
            flavorService: .onPrem,
            credentialsLoader: loader
        )

        #expect(service.restoreStoredCredentials())

        #expect(licenseStore.licenseUsername == "stored-user")
        #expect(licenseStore.licensePassword == "stored-password")
        #expect(licenseStore.onPremConfigURL == "https://onprem.example.com/prov/config.oppf")
        // Keeps the license seat of the previous installation
        #expect(licenseStore.licenseDeviceID == "stored-device-id")
    }

    @Test("Credentials that are already set are not replaced")
    func doesNotRestoreOverExistingCredentials() {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.licenseUsername = "link-user"
        licenseStore.licensePassword = "link-password"
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(licenseStore: licenseStore, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials() == false)

        #expect(loader.loadCredentialsCallCount == 0)
        #expect(licenseStore.licenseUsername == "link-user")
        #expect(licenseStore.licensePassword == "link-password")
    }

    @Test("A missing password is filled in from the keychain")
    func restoresMissingPassword() {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.licenseUsername = "mdm-user"
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let mdmSetup = LicenseMDMSetupMock(managedKeys: [MDM_KEY_LICENSE_USERNAME])
        let service = makeService(
            licenseStore: licenseStore,
            mdmSetup: mdmSetup,
            credentialsLoader: loader
        )

        #expect(service.restoreStoredCredentials())

        // The username is managed by MDM and must not be overwritten
        #expect(licenseStore.licenseUsername == "mdm-user")
        #expect(licenseStore.licensePassword == "stored-password")
    }

    @Test("A private app has no license to restore")
    func doesNotRestoreForPrivateApp() {
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(flavorService: .threema, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials() == false)
        #expect(loader.loadCredentialsCallCount == 0)
    }

    @Test("Nothing is restored without stored credentials")
    func doesNotRestoreWithoutStoredCredentials() {
        let licenseStore = LicenseStoreAdapterMock()
        let service = makeService(licenseStore: licenseStore)

        #expect(service.restoreStoredCredentials() == false)
        #expect(licenseStore.licenseUsername == nil)
    }

    @Test("A failing keychain is not fatal")
    func doesNotRestoreWhenLoadingFails() {
        let licenseStore = LicenseStoreAdapterMock()
        let loader = LicenseCredentialsLoaderMock(error: TestError.loadFailed)
        let service = makeService(licenseStore: licenseStore, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials() == false)
        #expect(licenseStore.licenseUsername == nil)
    }

    @Test("Restored credentials the license check rejects are discarded")
    func discardsRejectedRestoredCredentials() async {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckResult = false
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(licenseStore: licenseStore, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials())
        #expect(await service.validate() == .notLicensed)

        #expect(licenseStore.licenseUsername == nil)
        #expect(licenseStore.licensePassword == nil)
        // The device ID is not a credential and stays, so a later check keeps the same seat
        #expect(licenseStore.licenseDeviceID == "stored-device-id")
    }

    @Test("Restored credentials the license check accepts are kept")
    func keepsAcceptedRestoredCredentials() async {
        let licenseStore = LicenseStoreAdapterMock()
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(licenseStore: licenseStore, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials())
        #expect(await service.validate() == .licensed)

        #expect(licenseStore.licenseUsername == "stored-user")
        #expect(licenseStore.licensePassword == "stored-password")
    }

    @Test("Credentials entered after a rejected restore are not discarded")
    func doesNotDiscardCredentialsOfALaterCheck() async {
        let licenseStore = LicenseStoreAdapterMock()
        licenseStore.performLicenseCheckResult = false
        let loader = LicenseCredentialsLoaderMock(credentials: storedCredentials)
        let service = makeService(licenseStore: licenseStore, credentialsLoader: loader)

        #expect(service.restoreStoredCredentials())
        #expect(await service.validate() == .notLicensed)

        // As the license entry screen does it
        licenseStore.licenseUsername = "entered-user"
        licenseStore.licensePassword = "entered-password"

        #expect(await service.validate() == .notLicensed)

        #expect(licenseStore.licenseUsername == "entered-user")
        #expect(licenseStore.licensePassword == "entered-password")
    }

    // MARK: - Private helpers

    private enum TestError: Error {
        case loadFailed
    }

    private var storedCredentials: LicenseCredentials {
        LicenseCredentials(
            username: "stored-user",
            password: "stored-password",
            server: "https://onprem.example.com/prov/config.oppf",
            deviceID: "stored-device-id"
        )
    }

    private func makeService(
        licenseStore: LicenseStoreAdapterMock? = nil,
        flavorService: AppFlavorServiceMock = .work,
        mdmSetup: LicenseMDMSetupMock = LicenseMDMSetupMock(),
        credentialsLoader: LicenseCredentialsLoaderMock = LicenseCredentialsLoaderMock()
    ) -> LicenseService {
        LicenseService(
            licenseStore: licenseStore ?? LicenseStoreAdapterMock(),
            flavorService: flavorService,
            licenseMDMSetup: mdmSetup,
            credentialsLoader: credentialsLoader
        )
    }
}

// MARK: - CheckGate

/// Holds a license check until the test releases it, so a test can act while a validation is in
/// flight
@MainActor
private final class CheckGate {

    private var didStart = false
    private var isReleased = false
    private var startContinuation: CheckedContinuation<Void, Never>?
    private var releaseContinuation: CheckedContinuation<Void, Never>?

    /// Called by the license check: reports that it started and waits for ``release()``
    func startAndWait() async {
        didStart = true
        startContinuation?.resume()
        startContinuation = nil

        guard !isReleased else {
            return
        }

        await withCheckedContinuation { continuation in
            releaseContinuation = continuation
        }
    }

    /// Waits until the license check started
    func waitUntilStarted() async {
        guard !didStart else {
            return
        }

        await withCheckedContinuation { continuation in
            startContinuation = continuation
        }
    }

    func release() {
        isReleased = true
        releaseContinuation?.resume()
        releaseContinuation = nil
    }
}

Messung
CHZG
839610093

¤ Dauer der Verarbeitung: 0.2 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.