Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  KeychainManagerTests.swift   Sprache: JAVA

 

import Foundation
import RemoteSecretProtocol
import RemoteSecretProtocolTestHelper
import Testing
import ThreemaEssentials
@testable import Keychain
@testable import KeychainTestHelper

@Suite("KeychainManagerTests")
struct KeychainManagerTests {
    
    // MARK: - Identity Tests
    
    @Test("Store Identity (Remote Secret Disabled)")
    func testStoreIdentityWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)

        let identity = try keychainManager.storeTestIdentity()
        
        let storeKeychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = storeKeychainCall else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeKeychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .identity())
        #expect(storeOperation.account == identity.$identity)
        #expect(storeOperation.password == identity.$clientKey)
        #expect(storeOperation.generic == identity.$publicKey)
        #expect(storeOperation.service == identity.$serverGroup)

        #expect(cryptoMock.calls == [])
        #expect(keychainMock.calls.count == 1)
    }
    
    @Test("Store Identity (Remote Secret Enabled)")
    func testStoreIdentityWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let identity = try keychainManager.storeTestIdentity()
        
        let storeKeychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = storeKeychainCall else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeKeychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .identity())
        #expect(storeOperation.account == identity.$identity)
        
        /// Because we lose the real values, we just check if they exist
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic != nil)
        #expect(storeOperation.service != nil)

        #expect(cryptoMock.calls == [
            .encryptData(identity.$clientKey),
            .encryptData(identity.$publicKey),
            .encryptString(identity.$serverGroup),
        ])
        #expect(keychainMock.calls.count == 1)
    }
    
    @Test("Load Identity (Remote Secret Disabled)")
    func testLoadIdentityWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let identity = try keychainManager.storeTestIdentity()
        
        let loadedItem = try #require(try keychainManager.loadIdentity())
        #expect(loadedItem == identity)
        #expect(keychainMock.calls.count == 2)
        #expect(cryptoMock.calls.isEmpty)
        
        let loadKeychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = loadKeychainCall else {
            Issue.record("Expected call to be .load, but got \(loadKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(loadKeychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .identity())
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test("Load Identity (Remote Secret Enabled)")
    func testLoadIdentityWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let identity = try keychainManager.storeTestIdentity()
        
        let loadedItem = try #require(try keychainManager.loadIdentity())
        #expect(loadedItem.$identity == identity.$identity)
        
        /// 1 from storing, 1 from loading
        #expect(keychainMock.calls.count == 2)
        
        /// 3 calls are from the storing operation, 3 from loading
        #expect(cryptoMock.calls.count == 6)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 3
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptData = decryptFirstCall else {
            Issue.record("Expected decryptData at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptSecondCallIndex = 4
        let decryptSecondCall = cryptoMock.calls[decryptSecondCallIndex]
        guard case .decryptData = decryptSecondCall else {
            Issue.record("Expected decryptData at index \(decryptSecondCallIndex), but got \(decryptSecondCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptThirdCallIndex = 5
        let decryptThirdCall = cryptoMock.calls[decryptThirdCallIndex]
        guard case .decryptDataToString = decryptThirdCall else {
            Issue.record("Expected decryptDataToString at index \(decryptThirdCallIndex), but got \(decryptThirdCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let loadKeychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = loadKeychainCall else {
            Issue.record("Expected call to be .load, but got \(loadKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(loadKeychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .identity())
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test
    func testDeleteIdentity() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try keychainManager.deleteIdentity()
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .delete(keychainItem) = keychainCall else {
            Issue.record("Expected call to be .delete, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isDeleting == true)
        #expect(keychainItem == .identity())
        #expect(cryptoMock.calls.isEmpty)
    }
    
    @Test("Store identity with this device only true")
    func testStoreIdentityThisDeviceOnlyTrue() throws {
        let (
            keychainManager,
            keychainMock,
            _,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)

        _ = try keychainManager.storeTestIdentity(thisDeviceOnly: true)
        
        let storeKeychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = storeKeychainCall else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeOperation.item.accessibility == kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
        #expect(try keychainManager.isIdentityThisDeviceOnly() == true)
    }
    
    @Test("Store identity with this device only false")
    func testStoreIdentityThisDeviceOnlyFalse() throws {
        let (
            keychainManager,
            keychainMock,
            _,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)

        _ = try keychainManager.storeTestIdentity(thisDeviceOnly: false)
        
        let storeKeychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = storeKeychainCall else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeOperation.item.accessibility == kSecAttrAccessibleAfterFirstUnlock)
        #expect(try keychainManager.isIdentityThisDeviceOnly() == false)
    }
    
    @Test("Store identity with this device only true and false")
    func testStoreIdentityChangeThisDeviceOnly() throws {
        let (
            keychainManager,
            keychainMock,
            _,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        // Store this device only
        _ = try keychainManager.storeTestIdentity(thisDeviceOnly: true)
        
        let storeKeychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = storeKeychainCall else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeOperation.item.accessibility == kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
        #expect(try keychainManager.isIdentityThisDeviceOnly() == true)
        
        // Reset mock
        keychainMock.reset()
        
        // Store without this device only
        _ = try keychainManager.storeTestIdentity(thisDeviceOnly: false)
        
        let storeKeychainCall2 = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation2) = storeKeychainCall2 else {
            Issue.record("Expected call to be .store, but got \(storeKeychainCall2)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(storeOperation2.item.accessibility == kSecAttrAccessibleAfterFirstUnlock)
        #expect(try keychainManager.isIdentityThisDeviceOnly() == false)
    }
    
    @Test("Change identity accessibility")
    func testChangeIdentityAccessibility() throws {
        let (
            keychainManager,
            _,
            _,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        // Store this device only
        _ = try keychainManager.storeTestIdentity()
        
        #expect(try keychainManager.isIdentityThisDeviceOnly() == true)
        
        try keychainManager.changeIdentityAccessibility(thisDeviceOnly: false)
        
        #expect(try keychainManager.isIdentityThisDeviceOnly() == false)
        
        try keychainManager.changeIdentityAccessibility(thisDeviceOnly: true)

        #expect(try keychainManager.isIdentityThisDeviceOnly() == true)
    }
    
    // MARK: - Device Cookie Tests

    @Test("Store Device Cookie (Remote Secret Disabled)")
    func testStoreDeviceCookieWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let cookieData = Data("device_cookie".utf8)
        
        try keychainManager.storeDeviceCookie(cookieData)
        #expect(cryptoMock.calls == [])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.deviceCookie()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password == cookieData)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }
    
    @Test("Store Device Cookie (Remote Secret Enabled)")
    func testStoreDeviceCookieWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let cookieData = Data("device_cookie".utf8)
        
        try keychainManager.storeDeviceCookie(cookieData)
        #expect(cryptoMock.calls == [
            .encryptData(cookieData),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.deviceCookie()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }
    
    @Test("Load Device Cookie (Remote Secret Disabled)")
    func testLoadDeviceCookieWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let cookieData = Data("device_cookie".utf8)
        try keychainManager.storeDeviceCookie(cookieData)
        
        _ = try #require(try keychainManager.loadDeviceCookie())
        
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .deviceCookie())
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test("Load Device Cookie (Remote Secret Enabled)")
    func testLoadDeviceCookieWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let cookieData = Data("device_cookie".utf8)
        try keychainManager.storeDeviceCookie(cookieData)
        
        _ = try #require(try keychainManager.loadDeviceCookie())
        
        #expect(keychainMock.calls.count == 2)
        
        /// 1 from storing operation, 1 from loading
        #expect(cryptoMock.calls.count == 2)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 1
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptData = decryptFirstCall else {
            Issue.record("Expected decryptData at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected first call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .deviceCookie())
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test
    func testLoadDeviceCookieNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadDeviceCookie() == nil)
        #expect(keychainMock.loadCalls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .deviceCookie())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }
    
    // MARK: - Multi-Device Group Key Tests
    
    @Test("Store Multi-Device Group Key (Remote Secret Disabled)")
    func testStoreMultiDeviceGroupKeyWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let keyData = Data("multi_device_group_key".utf8)
        
        try keychainManager.storeMultiDeviceGroupKey(key: keyData)
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.multiDeviceGroupKey()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password == keyData)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }
    
    @Test("Store Multi-Device Group Key (Remote Secret Enabled)")
    func testStoreMultiDeviceGroupKeyWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let keyData = Data("multi_device_group_key".utf8)
        
        try keychainManager.storeMultiDeviceGroupKey(key: keyData)
        #expect(cryptoMock.calls == [
            .encryptData(keyData),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.multiDeviceGroupKey()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }
    
    @Test("Load Multi-Device Group Key (Remote Secret Disabled)")
    func testLoadMultiDeviceGroupKeyWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let keyData = Data("multi_device_group_key".utf8)
        let keychainItem = KeychainItem.multiDeviceGroupKey()
        try keychainManager.storeMultiDeviceGroupKey(key: keyData)
        
        _ = try #require(try keychainManager.loadMultiDeviceGroupKey())
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == keychainItem)
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test("Load Multi-Device Group Key (Remote Secret Enabled)")
    func testLoadMultiDeviceGroupKeyWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let keyData = Data("multi_device_group_key".utf8)
        let keychainItem = KeychainItem.multiDeviceGroupKey()
        try keychainManager.storeMultiDeviceGroupKey(key: keyData)
        
        _ = try #require(try keychainManager.loadMultiDeviceGroupKey())
        #expect(keychainMock.calls.count == 2)
        
        /// 1 from storing operation, 1 from loading
        #expect(cryptoMock.calls.count == 2)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 1
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptData = decryptFirstCall else {
            Issue.record("Expected decryptData at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == keychainItem)
        #expect(loadOperation.searchAccount == nil)
    }
    
    @Test
    func testLoadMultiDeviceGroupKeyNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadMultiDeviceGroupKey() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
                
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .multiDeviceGroupKey())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }
    
    // MARK: - Multi-Device ID Tests
    
    @Test("Store Multi-Device ID (Remote Secret Disabled)")
    func testStoreMultiDeviceIDWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let idData = Data("multi_device_id".utf8)
        try keychainManager.storeMultiDeviceID(id: idData)
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.multiDeviceID()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password == idData)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }
    
    @Test("Store Multi-Device ID (Remote Secret Enabled)")
    func testStoreMultiDeviceIDWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let idData = Data("multi_device_id".utf8)
        
        try keychainManager.storeMultiDeviceID(id: idData)
        #expect(cryptoMock.calls == [
            .encryptData(idData),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.multiDeviceID()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }

    @Test("Load Multi-Device ID (Remote Secret Disabled)")
    func testLoadMultiDeviceIDWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let idData = Data("multi_device_id".utf8)
        try keychainManager.storeMultiDeviceID(id: idData)
        
        _ = try #require(try keychainManager.loadMultiDeviceID())
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .multiDeviceID())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test("Load Multi-Device ID (Remote Secret Enabled)")
    func testLoadMultiDeviceIDWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let idData = Data("multi_device_id".utf8)
        try keychainManager.storeMultiDeviceID(id: idData)
        
        _ = try #require(try keychainManager.loadMultiDeviceID())
        #expect(keychainMock.calls.count == 2)
        
        /// 1 from storing operation, 1 from loading
        #expect(cryptoMock.calls.count == 2)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 1
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptData = decryptFirstCall else {
            Issue.record("Expected decryptData at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .multiDeviceID())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test
    func testLoadMultiDeviceIDNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadMultiDeviceID() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .multiDeviceID())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test
    func testDeleteMultiDeviceID() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try keychainManager.deleteMultiDeviceID()
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .delete(keychainItem) = keychainCall else {
            Issue.record("Expected call to be .delete, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isDeleting == true)
        #expect(keychainItem == .multiDeviceID())
        #expect(cryptoMock.calls.isEmpty)
    }

    // MARK: - Forward Security Key Tests

    @Test("Store Forward Security Wrapping Key (Remote Secret Disabled)")
    func testStoreForwardSecurityWrappingKeyWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let keyData = Data("forward_security_key".utf8)
        
        try keychainManager.storeForwardSecurityWrappingKey(keyData)
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.forwardSecurityWrappingKey()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password == keyData)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }

    @Test("Store Forward Security Wrapping Key (Remote Secret Enabled)")
    func testStoreForwardSecurityWrappingKeyWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let keyData = Data("forward_security_key".utf8)
        
        try keychainManager.storeForwardSecurityWrappingKey(keyData)
        #expect(cryptoMock.calls == [
            .encryptData(keyData),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.forwardSecurityWrappingKey()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic == nil)
        #expect(storeOperation.service == nil)
    }

    @Test("Load Forward Security Wrapping Key (Remote Secret Disabled)")
    func testLoadForwardSecurityWrappingKeyWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let keyData = Data("forward_security_key".utf8)
        try keychainManager.storeForwardSecurityWrappingKey(keyData)
        
        _ = try #require(try keychainManager.loadForwardSecurityWrappingKey())
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .forwardSecurityWrappingKey())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test("Load Forward Security Wrapping Key (Remote Secret Enabled)")
    func testLoadForwardSecurityWrappingKeyWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let keyData = Data("forward_security_key".utf8)
        try keychainManager.storeForwardSecurityWrappingKey(keyData)
        
        _ = try #require(try keychainManager.loadForwardSecurityWrappingKey())
        #expect(keychainMock.calls.count == 2)
        
        /// 1 from storing operation, 1 from loading
        #expect(cryptoMock.calls.count == 2)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 1
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptData = decryptFirstCall else {
            Issue.record("Expected decryptData at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .forwardSecurityWrappingKey())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test
    func testLoadForwardSecurityWrappingKeyNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadForwardSecurityWrappingKey() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .forwardSecurityWrappingKey())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }

    // MARK: - License Tests

    @Test("Store License (Remote Secret Disabled)")
    func testStoreLicenseWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let license = Keychain.ThreemaLicense(
            user: "test_user",
            password: "test_password",
            deviceID: "device123",
            onPremServer: "test.server.com"
        )
        
        try keychainManager.storeLicense(license)
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .license())
        #expect(storeOperation.account == license.user)
        #expect(storeOperation.password == Data(license.password.utf8))
        #expect(storeOperation.generic == Data(license.deviceID!.utf8))
        #expect(storeOperation.service == license.onPremServer)
    }

    @Test("Store License (Remote Secret Enabled)")
    func testStoreLicenseWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let license = Keychain.ThreemaLicense(
            user: "test_user",
            password: "test_password",
            deviceID: "device123",
            onPremServer: "test.server.com"
        )
        
        try keychainManager.storeLicense(license)
        #expect(cryptoMock.calls == [
            .encryptString(license.user),
            .encryptString(license.password),
            .encryptString(license.deviceID!),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .license())
        #expect(storeOperation.account != nil)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.generic != nil)
        #expect(storeOperation.service == license.onPremServer)
    }

    @Test("Load License (Remote Secret Disabled)")
    func testLoadLicenseWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let license = Keychain.ThreemaLicense(
            user: "test_user",
            password: "test_password",
            deviceID: "device123",
            onPremServer: "test.server.com"
        )
        try keychainManager.storeLicense(license)
        
        _ = try #require(try keychainManager.loadLicense())
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .license())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test("Load License (Remote Secret Enabled)")
    func testLoadLicenseWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let license = Keychain.ThreemaLicense(
            user: "test_user",
            password: "test_password",
            deviceID: "device123",
            onPremServer: "test.server.com"
        )
        try keychainManager.storeLicense(license)
        
        _ = try #require(try keychainManager.loadLicense())
        #expect(keychainMock.calls.count == 2)
        
        /// 3 from storing operation, 3 from loading
        #expect(cryptoMock.calls.count == 6)

        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 3
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptDataToString = decryptFirstCall else {
            Issue.record("Expected decryptDataToString at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptSecondCallIndex = 4
        let decryptSecondCall = cryptoMock.calls[decryptSecondCallIndex]
        guard case .decryptData = decryptSecondCall else {
            Issue
                .record("Expected decryptData at index \(decryptSecondCallIndex), but got \(decryptSecondCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptThirdCallIndex = 5
        let decryptThirdCall = cryptoMock.calls[decryptThirdCallIndex]
        guard case .decryptData = decryptThirdCall else {
            Issue.record("Expected decryptData at index \(decryptThirdCallIndex), but got \(decryptThirdCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .license())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test
    func testLoadLicenseNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadLicense() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .license())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test
    func testDeleteLicense() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try keychainManager.deleteLicense()
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .delete(keychainItem) = keychainCall else {
            Issue.record("Expected call to be .delete, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isDeleting == true)
        #expect(keychainItem == .license())
        #expect(cryptoMock.calls.isEmpty)
    }

    // MARK: - Unencrypted License Tests

    @Test("Load unencrypted license")
    func testLoadUnencryptedLicense() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)

        let license = Keychain.ThreemaLicense(
            user: "test_user",
            password: "test_password",
            deviceID: "device123",
            onPremServer: "test.server.com"
        )
        try keychainManager.storeLicense(license)

        let loadedLicense = try #require(
            try KeychainManager.loadUnencryptedLicense(keychainProvider: keychainMock)
        )

        #expect(loadedLicense.user == license.user)
        #expect(loadedLicense.password == license.password)
        #expect(loadedLicense.deviceID == license.deviceID)
        #expect(loadedLicense.onPremServer == license.onPremServer)
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test("Load unencrypted license without a stored license")
    func testLoadUnencryptedLicenseNotFound() throws {
        let (
            _,
            keychainMock,
            _,
            _
        ) = makeSUT()

        try #require(try KeychainManager.loadUnencryptedLicense(keychainProvider: keychainMock) == nil)
    }

    /// With a remote secret the license is encrypted with it, and it can only be fetched for an
    /// existing Threema ID
    @Test("Load unencrypted license with a remote secret in the keychain")
    func testLoadUnencryptedLicenseWithRemoteSecret() throws {
        let (
            keychainManager,
            keychainMock,
            _,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)

        try keychainManager.storeLicense(
            Keychain.ThreemaLicense(
                user: "test_user",
                password: "test_password",
                deviceID: "device123",
                onPremServer: "test.server.com"
            )
        )
        try keychainMock.store(.remoteSecret(), password: Data([0x01]), generic: Data([0x02]))

        try #require(try KeychainManager.loadUnencryptedLicense(keychainProvider: keychainMock) == nil)

        // The license item is not even read
        #expect(keychainMock.loadCalls.map(\.searchItem) == [.remoteSecret()])
    }

    // MARK: - Threema Safe Key Tests (No remote secret encryption)

    @Test
    func testLoadThreemaSafeKey() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        let keyData = Data("threema_safe_key".utf8)
        try keychainManager.storeThreemaSafeKey(key: keyData)
        
        let loadedItem = try #require(try keychainManager.loadThreemaSafeKey())
        #expect(loadedItem == keyData)
        #expect(keychainMock.calls.count == 2)
        #expect(cryptoMock.calls.isEmpty)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .threemaSafeKey())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test
    func testLoadThreemaSafeKeyNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadThreemaSafeKey() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .threemaSafeKey())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test
    func testStoreThreemaSafeKey() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        let keyData = Data("threema_safe_key".utf8)
        try keychainManager.storeThreemaSafeKey(key: keyData)
        #expect(keychainMock.calls.count == 1)
        #expect(cryptoMock.calls.isEmpty)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        
        let keychainItem = KeychainItem.threemaSafeKey()
        #expect(storeOperation.item == keychainItem)
        #expect(storeOperation.account == keychainItem.label)
        #expect(storeOperation.password == keyData)
    }

    @Test
    func testDeleteThreemaSafeKey() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try keychainManager.deleteThreemaSafeKey()
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .delete(keychainItem) = keychainCall else {
            Issue.record("Expected call to be .delete, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isDeleting == true)
        #expect(keychainItem == .threemaSafeKey())
        #expect(cryptoMock.calls.isEmpty)
    }

    // MARK: - Threema Safe Server Tests

    @Test("Store Threema Safe Server (Remote Secret Disabled)")
    func testStoreThreemaSafeServerWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let server = Keychain.ThreemaSafeServerInfo(
            user: "safe_user",
            password: "safe_password",
            server: "safe.threema.ch"
        )
        
        try keychainManager.storeThreemaSafeServer(server)
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .threemaSafeServer())
        #expect(storeOperation.account == server.user)
        #expect(storeOperation.password == Data(server.password!.utf8))
        #expect(storeOperation.service == server.server)
    }

    @Test("Store Threema Safe Server (Remote Secret Enabled)")
    func testStoreThreemaSafeServerWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let server = Keychain.ThreemaSafeServerInfo(
            user: "safe_user",
            password: "safe_password",
            server: "safe.threema.ch"
        )
        
        try keychainManager.storeThreemaSafeServer(server)
        #expect(cryptoMock.calls == [
            .encryptString(server.user!),
            .encryptString(server.password!),
            .encryptString(server.server),
        ])
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .store(storeOperation) = keychainCall else {
            Issue.record("Expected call to be .store, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isStoring == true)
        #expect(storeOperation.searchItem == nil)
        #expect(storeOperation.searchAccount == nil)
        #expect(storeOperation.item == .threemaSafeServer())
        #expect(storeOperation.account != nil)
        #expect(storeOperation.password != nil)
        #expect(storeOperation.service != nil)
    }

    @Test("Load Threema Safe Server (Remote Secret Disabled)")
    func testLoadThreemaSafeServerWithRemoteSecretDisabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: false)
        
        let server = Keychain.ThreemaSafeServerInfo(
            user: "safe_user",
            password: "safe_password",
            server: "safe.threema.ch"
        )
        try keychainManager.storeThreemaSafeServer(server)
        
        _ = try #require(try keychainManager.loadThreemaSafeServer())
        #expect(cryptoMock.calls.isEmpty)
        #expect(keychainMock.calls.count == 2)
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .threemaSafeServer())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test("Load Threema Safe Server (Remote Secret Enabled)")
    func testLoadThreemaSafeServerWithRemoteSecretEnabled() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT(isRemoteSecretEnabled: true)
        
        let server = Keychain.ThreemaSafeServerInfo(
            user: "safe_user",
            password: "safe_password",
            server: "safe.threema.ch"
        )
        try keychainManager.storeThreemaSafeServer(server)
        
        _ = try #require(try keychainManager.loadThreemaSafeServer())
        #expect(keychainMock.calls.count == 2)
        
        /// 3 from storing operation, 3 from loading
        #expect(cryptoMock.calls.count == 6)
        
        /// Verify the types of calls without caring about data, as the previous
        /// storing operations cleared up the data.
        let decryptFirstCallIndex = 3
        let decryptFirstCall = cryptoMock.calls[decryptFirstCallIndex]
        guard case .decryptDataToString = decryptFirstCall else {
            Issue.record("Expected decryptString at index \(decryptFirstCallIndex), but got \(decryptFirstCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptSecondCallIndex = 4
        let decryptSecondCall = cryptoMock.calls[decryptSecondCallIndex]
        guard case .decryptDataToString = decryptSecondCall else {
            // swiftformat:disable:next all
            Issue.record("Expected decryptDataToString at index \(decryptSecondCallIndex), but got \(decryptSecondCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let decryptThirdCallIndex = 5
        let decryptThirdCall = cryptoMock.calls[decryptThirdCallIndex]
        guard case .decryptData = decryptThirdCall else {
            Issue.record("Expected decryptData at index \(decryptThirdCallIndex), but got \(decryptThirdCall)")
            throw NSError(domain: "", code: -1)
        }
        
        let keychainCall = try #require(keychainMock.calls.last)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .threemaSafeServer())
        #expect(loadOperation.searchAccount == nil)
    }

    @Test
    func testLoadThreemaSafeServerNotFound() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try #require(try keychainManager.loadThreemaSafeServer() == nil)
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .load(loadOperation) = keychainCall else {
            Issue.record("Expected call to be .load, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isLoading == true)
        #expect(loadOperation.searchItem == .threemaSafeServer())
        #expect(loadOperation.searchAccount == nil)
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test
    func testDeleteThreemaSafeServer() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        try keychainManager.deleteThreemaSafeServer()
        #expect(keychainMock.calls.count == 1)
        
        let keychainCall = try #require(keychainMock.calls.first)
        guard case let .delete(keychainItem) = keychainCall else {
            Issue.record("Expected call to be .delete, but got \(keychainCall)")
            throw NSError(domain: "", code: -1)
        }
        
        #expect(keychainCall.isDeleting == true)
        #expect(keychainItem == .threemaSafeServer())
        #expect(cryptoMock.calls.isEmpty)
    }

    @Test
    func testKeychainError() throws {
        let (
            keychainManager,
            keychainMock,
            cryptoMock,
            _
        ) = makeSUT()
        
        let expectedError = KeychainProviderError.loadFailed(osStatus: -1)
        keychainMock.updateError(.loadError(expectedError))
        
        #expect(throws: KeychainProviderError.self) {
            _ = try keychainManager.loadIdentity()
        }
        
        #expect(keychainMock.calls.count == 1)
        #expect(keychainMock.calls.first?.isLoading == true)
        #expect(cryptoMock.calls.isEmpty)
    }
    
    // MARK: - Helpers
    
    private func makeSUT(isRemoteSecretEnabled: Bool = false) -> (
        KeychainManager,
        KeychainProviderMock,
        RemoteSecretCryptoMock,
        RemoteSecretManagerMock
    ) {
        let keychainMock = KeychainProviderMock()
        let cryptoMock = RemoteSecretCryptoMock()
        let remoteSecretManagerMock = RemoteSecretManagerMock(
            isRemoteSecretEnabled: isRemoteSecretEnabled,
            crypto: cryptoMock
        )
        
        let keychainManager = KeychainManager(
            remoteSecretManager: remoteSecretManagerMock,
            keychainProvider: keychainMock
        )
        
        return (
            keychainManager,
            keychainMock,
            cryptoMock,
            remoteSecretManagerMock
        )
    }
}

// MARK: Keychain Manager Helpers

extension KeychainManager {
    func storeTestIdentity(
        identity: String = "ECHOECHO",
        clientKey: Data = Data("clientKey".utf8),
        publicKey: Data = Data("publicKey".utf8),
        serverGroup: String = "serverGroup",
        thisDeviceOnly: Bool = true
    ) throws -> Keychain.MyIdentity {
        let identity = Keychain.MyIdentity(
            identity: ThreemaIdentity(identity),
            clientKey: ThreemaClientKey(clientKey),
            publicKey: ThreemaPublicKey(publicKey),
            serverGroup: ServerGroup(serverGroup)
        )
        try storeIdentity(identity, thisDeviceOnly: thisDeviceOnly)
        return identity
    }
}

// MARK: - Keychain.MyIdentity + Equatable

extension Keychain.MyIdentity: Equatable {
    public static func == (lhs: Keychain.MyIdentity, rhs: Keychain.MyIdentity) -> Bool {
        lhs.$identity == rhs.$identity
            && lhs.$clientKey == rhs.$clientKey
            && lhs.$publicKey == rhs.$publicKey
            && lhs.$serverGroup == rhs.$serverGroup
    }
}

Messung
CHZG
829610092

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






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104848
#Domains=416964