mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Add timeouts after verifying the device owner when linking a new device. (#5970)
* Support for waitingForAuth continuation sender during Link new device
* Stop automatically regenerating new QR codes when they expire.
This reverts part of commit e235e3ba81.
* Add a timeout after the Face/TouchID verification when linking a desktop computer.
---------
Co-authored-by: Hugh Nimmo-Smith <hughns@element.io>
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
import Combine
|
||||
@testable import ElementX
|
||||
import MatrixRustSDKMocks
|
||||
import SwiftUI
|
||||
import Testing
|
||||
|
||||
@MainActor
|
||||
@@ -19,7 +18,6 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
var qrLoginProgressSubject: CurrentValueSubject<QRLoginProgress, AuthenticationServiceError>!
|
||||
var qrCodeLoginService: QRCodeLoginServiceMock!
|
||||
|
||||
var regeneratedQRCodeImage: UIImage!
|
||||
var linkMobileProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkMobileProgress, QRCodeLoginError>!
|
||||
var linkDesktopProgressSubject: CurrentValueSubject<LinkNewDeviceService.LinkDesktopProgress, QRCodeLoginError>!
|
||||
var linkNewDeviceService: LinkNewDeviceServiceMock!
|
||||
@@ -152,7 +150,8 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
guard case .requestOAuthAuthorisation = action else { return false }
|
||||
return true
|
||||
}
|
||||
linkDesktopProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory))
|
||||
linkDesktopProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory,
|
||||
continuationSender: .init(underlyingSender: ContinuationMessageSenderSDKMock())))
|
||||
try await deferredAction.fulfill()
|
||||
|
||||
let currentState = context.viewState.state
|
||||
@@ -168,6 +167,25 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
try await deferredAction.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func linkDesktopExpiresWithoutSecureChannel() async throws {
|
||||
setup(mode: .linkDesktop, linkDesktopTimeout: .zero)
|
||||
#expect(context.viewState.state == .linkDesktopInstructions)
|
||||
|
||||
// The timeout begins as soon as the screen appears, so the flow expires before a channel is established.
|
||||
let deferred = deferFulfillment(context.$viewState) { $0.state == .error(.expired) }
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func loginDoesNotExpire() async throws {
|
||||
setup(mode: .login, linkDesktopTimeout: .zero)
|
||||
|
||||
// The timeout only applies when linking a desktop, so signing in must never expire.
|
||||
let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.state == .error(.expired) }
|
||||
try await deferredFailure.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func linkMobileDevice() async throws {
|
||||
setup(mode: .linkMobile)
|
||||
@@ -188,7 +206,8 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
guard case .requestOAuthAuthorisation = action else { return false }
|
||||
return true
|
||||
}
|
||||
linkMobileProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory))
|
||||
linkMobileProgressSubject.send(.waitingForAuthorisation(verificationURL: .homeDirectory,
|
||||
continuationSender: .init(underlyingSender: ContinuationMessageSenderSDKMock())))
|
||||
try await deferredAction.fulfill()
|
||||
|
||||
let currentState = context.viewState.state
|
||||
@@ -204,20 +223,9 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
try await deferredAction.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func linkMobileDeviceQRCodeExpiry() async throws {
|
||||
setup(mode: .linkMobile)
|
||||
#expect(context.viewState.state.isDisplayQR)
|
||||
|
||||
let deferred = deferFulfillment(context.$viewState, keyPath: \.state, transitionValues: [.displayQR(.expired),
|
||||
.displayQR(.active(regeneratedQRCodeImage))])
|
||||
linkMobileProgressSubject.send(completion: .failure(.expired))
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private mutating func setup(mode: Mode) {
|
||||
private mutating func setup(mode: Mode, linkDesktopTimeout: Duration = .seconds(120)) {
|
||||
qrLoginProgressSubject = .init(.starting)
|
||||
qrCodeLoginService = QRCodeLoginServiceMock()
|
||||
qrCodeLoginService.loginWithQRCodeDataReturnValue = qrLoginProgressSubject.asCurrentValuePublisher()
|
||||
@@ -227,11 +235,6 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
linkNewDeviceService = LinkNewDeviceServiceMock(.init(linkMobileProgressPublisher: linkMobileProgressSubject.asCurrentValuePublisher(),
|
||||
linkDesktopProgressPublisher: linkDesktopProgressSubject.asCurrentValuePublisher()))
|
||||
|
||||
regeneratedQRCodeImage = LinkNewDeviceServiceMock.mockQRCodeImage
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
clientProxy.linkNewDeviceServiceReturnValue = LinkNewDeviceServiceMock(.init(linkMobileProgressPublisher: .init(.qrReady(regeneratedQRCodeImage)),
|
||||
linkDesktopProgressPublisher: .init(.starting)))
|
||||
|
||||
let screenMode: QRCodeLoginScreenMode
|
||||
switch mode {
|
||||
case .login:
|
||||
@@ -239,12 +242,13 @@ struct QRCodeLoginScreenViewModelTests {
|
||||
case .linkDesktop:
|
||||
screenMode = .linkDesktop(linkNewDeviceService)
|
||||
case .linkMobile:
|
||||
screenMode = .linkMobile(linkNewDeviceService.linkMobileDevice(), clientProxy)
|
||||
screenMode = .linkMobile(linkNewDeviceService.linkMobileDevice())
|
||||
}
|
||||
|
||||
appMediator = AppMediatorMock(.init())
|
||||
viewModel = QRCodeLoginScreenViewModel(mode: screenMode,
|
||||
canSignInManually: true,
|
||||
appMediator: appMediator)
|
||||
appMediator: appMediator,
|
||||
linkDesktopTimeout: linkDesktopTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user