Files
2026-04-15 13:58:23 +02:00

345 lines
11 KiB
Swift

// Copyright (c) 2025 Proton Technologies AG
//
// This file is part of Proton Mail.
//
// Proton Mail is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail. If not, see https://www.gnu.org/licenses/.
import proton_app_uniffi
public final class MailSessionSpy: MailSessionProtocol {
public var onPrimaryAccountChanged: (@Sendable (String) -> Void)?
public var appProtectionStub: AppProtection = .none
public var primaryUserSessionStub: MailUserSession?
public var storedSessions: [StoredSessionStub] = [] {
didSet {
Task {
await watchSessionsAsyncCallback?.onUpdate()
}
}
}
public var stubbedRegisterDeviceTaskHandleFactory: () -> RegisterDeviceTaskHandle = {
fatalError("not stubbed")
}
public var userSessions: [MailUserSession] = []
private(set) public var changeAppSettingsInvocations: [AppSettingsDiff] = []
private(set) public var executeNotificationQuickActionInvocations: [(StoredSession, PushNotificationQuickAction)] = []
private(set) public var registerDeviceCallCount = 0
private(set) public var setPinCodeInvocations: [[UInt32]] = []
private(set) public var setPrimaryAccountInvocations: [String] = []
private(set) public var signOutAllCallCount = 0
private var stubbedAppSettings = AppSettings(
appearance: .system,
protection: .none,
autoLock: .minutes(0),
useCombineContacts: false,
useAlternativeRouting: true
)
private var watchSessionsAsyncCallback: AsyncLiveQueryCallback?
public init() {}
// MARK: - MailSessionProtocol
public func recordMeasurementPrelogin(
eventType: MeasurementEventType,
asid: String,
appPackageName: String,
fields: [String: MeasurementValue?]
) async -> MailSessionRecordMeasurementPreloginResult {
fatalError()
}
public func isFeatureEnabled(featureId: String) async -> Bool? {
false
}
public func biometricsCheckPassed() {
fatalError(#function)
}
public func appProtection() async -> MailSessionAppProtectionResult {
.ok(appProtectionStub)
}
public func changeAppSettings(settings: AppSettingsDiff) async -> MailSessionChangeAppSettingsResult {
changeAppSettingsInvocations.append(settings)
if let changedSetting = settings.appearance {
stubbedAppSettings.appearance = changedSetting
}
if let changedSetting = settings.autoLock {
stubbedAppSettings.autoLock = changedSetting
}
if let changedSetting = settings.useCombineContacts {
stubbedAppSettings.useCombineContacts = changedSetting
}
if let changedSetting = settings.useAlternativeRouting {
stubbedAppSettings.useAlternativeRouting = changedSetting
}
return .ok
}
public func executeNotificationQuickAction(session: StoredSession, action: PushNotificationQuickAction, timeLeftMs: UInt64?) async -> VoidActionResult {
executeNotificationQuickActionInvocations.append((session, action))
return .ok
}
public func startAutoLockCountdown() {
fatalError(#function)
}
public func deleteAccount(userId: String) async -> VoidSessionResult {
fatalError(#function)
}
public func deletePinCode(pin: [UInt32]) async -> proton_app_uniffi.MailSessionDeletePinCodeResult {
fatalError(#function)
}
public func exportLogs(filePath: String) -> proton_app_uniffi.MailSessionExportLogsResult {
fatalError(#function)
}
public func getAccount(userId: String) async -> MailSessionGetAccountResult {
fatalError(#function)
}
public func getAccountBlocking(userId: String) -> MailSessionGetAccountResult {
fatalError(#function)
}
public func getAccountSessions(account: StoredAccount) async -> MailSessionGetAccountSessionsResult {
fatalError(#function)
}
public func getAccountState(userId: String) async -> MailSessionGetAccountStateResult {
fatalError(#function)
}
public func getAccountStateBlocking(userId: String) -> MailSessionGetAccountStateResult {
fatalError()
}
public func getAccounts() async -> MailSessionGetAccountsResult {
fatalError(#function)
}
public func getAccountsBlocking() -> MailSessionGetAccountsResult {
fatalError(#function)
}
public func getAppSettings() async -> MailSessionGetAppSettingsResult {
.ok(stubbedAppSettings)
}
public func getPrimaryAccount() async -> MailSessionGetPrimaryAccountResult {
fatalError(#function)
}
public func getPrimaryAccountBlocking() -> MailSessionGetPrimaryAccountResult {
fatalError(#function)
}
public func getSession(sessionId: String) async -> MailSessionGetSessionResult {
.ok(storedSessions.first { $0.sessionId() == sessionId })
}
public func getSessionBlocking(sessionId: String) -> MailSessionGetSessionResult {
fatalError(#function)
}
public func getSessionState(sessionId: String) async -> MailSessionGetSessionStateResult {
fatalError(#function)
}
public func getSessionStateBlocking(sessionId: String) -> MailSessionGetSessionStateResult {
fatalError(#function)
}
public func getSessions() async -> MailSessionGetSessionsResult {
.ok(storedSessions)
}
public func getSessionsBlocking(account: StoredAccount) -> MailSessionGetAccountSessionsResult {
fatalError(#function)
}
public func initializedUserSessionFromStoredSession(session: StoredSession) -> MailSessionInitializedUserSessionFromStoredSessionResult {
if let userSession = userSessions.first(where: { $0.sessionId == session.sessionId() }) {
.ok(userSession)
} else {
fatalError("session not configured")
}
}
public func logoutAccount(userId: String) async -> VoidSessionResult {
fatalError(#function)
}
public func newLoginFlow() async -> MailSessionNewLoginFlowResult {
fatalError(#function)
}
public func newSignupFlow() async -> MailSessionNewSignupFlowResult {
fatalError(#function)
}
public func onEnterForeground() {
fatalError(#function)
}
public func onExitForeground() {
fatalError(#function)
}
public func pauseWork() {
fatalError(#function)
}
public func pauseWorkAndWait() {
fatalError(#function)
}
public func registerDeviceTask() -> MailSessionRegisterDeviceTaskResult {
registerDeviceCallCount += 1
return .ok(stubbedRegisterDeviceTaskHandleFactory())
}
public func remainingPinAttempts() async -> MailSessionRemainingPinAttemptsResult {
fatalError(#function)
}
public func resumeLoginFlow(userId: String, sessionId: String) async -> MailSessionResumeLoginFlowResult {
fatalError(#function)
}
public func resumeWork() {
fatalError(#function)
}
public func setBiometricsAppProtection() async -> MailSessionSetBiometricsAppProtectionResult {
.ok
}
public func setPinCode(pin: [UInt32]) async -> MailSessionSetPinCodeResult {
setPinCodeInvocations.append(pin)
return .ok
}
public func setPrimaryAccount(userId: String) async -> VoidSessionResult {
setPrimaryAccountInvocations.append(userId)
onPrimaryAccountChanged?(userId)
return .ok
}
public func shouldAutoLock() async -> MailSessionShouldAutoLockResult {
fatalError(#function)
}
public func signOutAll() async -> MailSessionSignOutAllResult {
signOutAllCallCount += 1
return .ok
}
public func startBackgroundExecution(callback: any BackgroundExecutionCallback) -> MailSessionStartBackgroundExecutionResult {
fatalError()
}
public func startBackgroundExecutionWithDuration(
durationSeconds: UInt64,
callback: any BackgroundExecutionCallback
) -> MailSessionStartBackgroundExecutionWithDurationResult {
fatalError(#function)
}
public func toPrimaryUserSession() async -> MailSessionToPrimaryUserSessionResult {
.ok(primaryUserSessionStub!)
}
public func toUserSession(ffiFlow: LoginFlow) async -> MailSessionToUserSessionResult {
fatalError(#function)
}
public func unsetBiometricsAppProtection() async -> MailSessionUnsetBiometricsAppProtectionResult {
fatalError(#function)
}
public func updateOsNetworkStatus(osNetworkStatus: OsNetworkStatus) {
fatalError(#function)
}
public func userSessionFromStoredSession(session: StoredSession) -> MailSessionUserSessionFromStoredSessionResult {
fatalError(#function)
}
public func verifyPinCode(pin: [UInt32]) async -> MailSessionVerifyPinCodeResult {
fatalError(#function)
}
public func watchAccountSessions(account: StoredAccount, callback: any LiveQueryCallback) async -> MailSessionWatchAccountSessionsResult {
fatalError(#function)
}
public func watchAccounts(callback: any LiveQueryCallback) async -> MailSessionWatchAccountsResult {
fatalError(#function)
}
public func watchAccountsAsync(callback: any AsyncLiveQueryCallback) async -> MailSessionWatchAccountsAsyncResult {
fatalError(#function)
}
public func watchSessions(callback: any LiveQueryCallback) async -> MailSessionWatchSessionsResult {
fatalError(#function)
}
public func watchSessionsAsync(callback: any AsyncLiveQueryCallback) async -> MailSessionWatchSessionsAsyncResult {
watchSessionsAsyncCallback = callback
return .ok(.init(sessions: storedSessions, handle: WatchHandleDummy(noPointer: .init())))
}
public func isFeatureEnabled(featureId: String) async -> MailSessionIsFeatureEnabledResult {
fatalError(#function)
}
public func watchFeatureFlags(callback: any LiveQueryCallback) async -> MailSessionWatchFeatureFlagsResult {
fatalError(#function)
}
public func watchFeatureFlagsAsync(callback: any AsyncLiveQueryCallback) async -> MailSessionWatchFeatureFlagsAsyncResult {
fatalError(#function)
}
}
private extension MailUserSession {
var sessionId: String {
switch sessionId() {
case .ok(let id):
id
case .error(let error):
fatalError("\(error)")
}
}
}