mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Verify Element X with an existing Element Classic account. (#5374)
* Read and import the secrets from ClassicAppAccounts. * Record snapshots. * Add some documentation, tidy up tests and fix the dismissal of the backup instructions. * Workaround flakey tests (the fulfilments weren't always firing). * Allow a custom Classic App deep link URL to be configured.
This commit is contained in:
@@ -14,13 +14,14 @@ import Testing
|
||||
@MainActor
|
||||
struct AuthenticationServiceTests {
|
||||
var client: ClientSDKMock!
|
||||
var encryption: EncryptionSDKMock!
|
||||
var userSessionStore: UserSessionStoreMock!
|
||||
var encryptionKeyProvider: MockEncryptionKeyProvider!
|
||||
var service: AuthenticationService!
|
||||
|
||||
@Test
|
||||
mutating func passwordLogin() async {
|
||||
setup(serverAddress: "example.com")
|
||||
mutating func passwordLogin() async throws {
|
||||
try await setup(serverAddress: "example.com")
|
||||
|
||||
switch await service.configure(for: "example.com", flow: .login) {
|
||||
case .success:
|
||||
@@ -44,30 +45,20 @@ struct AuthenticationServiceTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func configureLoginWithOIDC() async {
|
||||
setup()
|
||||
mutating func configureLoginWithOIDC() async throws {
|
||||
try await setup()
|
||||
|
||||
switch await service.configure(for: "matrix.org", flow: .login) {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
Issue.record("Unexpected failure: \(error)")
|
||||
}
|
||||
try await service.configure(for: "matrix.org", flow: .login).get()
|
||||
|
||||
#expect(service.flow == .login)
|
||||
#expect(service.homeserver.value == .mockMatrixDotOrg)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func configureRegisterWithOIDC() async {
|
||||
setup()
|
||||
mutating func configureRegisterWithOIDC() async throws {
|
||||
try await setup()
|
||||
|
||||
switch await service.configure(for: "matrix.org", flow: .register) {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
Issue.record("Unexpected failure: \(error)")
|
||||
}
|
||||
try await service.configure(for: "matrix.org", flow: .register).get()
|
||||
|
||||
#expect(service.flow == .register)
|
||||
#expect(service.homeserver.value == .mockMatrixDotOrg)
|
||||
@@ -75,37 +66,101 @@ struct AuthenticationServiceTests {
|
||||
|
||||
@Test
|
||||
@MainActor
|
||||
mutating func configureRegisterNoSupport() async {
|
||||
mutating func configureRegisterNoSupport() async throws {
|
||||
let homeserverAddress = "example.com"
|
||||
setup(serverAddress: homeserverAddress)
|
||||
try await setup(serverAddress: homeserverAddress)
|
||||
|
||||
switch await service.configure(for: homeserverAddress, flow: .register) {
|
||||
case .success:
|
||||
Issue.record("Configuration should have failed")
|
||||
case .failure(let error):
|
||||
#expect(error == .registrationNotSupported)
|
||||
try await #require(throws: AuthenticationServiceError.registrationNotSupported) {
|
||||
try await service.configure(for: homeserverAddress, flow: .register).get()
|
||||
}
|
||||
|
||||
#expect(service.flow == .login)
|
||||
#expect(service.homeserver.value == .init(address: "matrix.org", loginMode: .unknown))
|
||||
}
|
||||
|
||||
@Test
|
||||
@MainActor
|
||||
mutating func classicAppAccountSecretsBundleIsUsed() async throws {
|
||||
// Given an authentication service with an Element Classic account for Alice.
|
||||
try await setup(classicAppAccounts: [.mockAlice])
|
||||
try await service.configure(for: "matrix.org", flow: .login).get()
|
||||
#expect(service.flow == .login)
|
||||
#expect(service.classicAppAccount?.state.availableSecrets == .complete)
|
||||
|
||||
// When logging in as Alice.
|
||||
_ = try await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil).get()
|
||||
#expect(client.loginUsernamePasswordInitialDeviceNameDeviceIdCallsCount == 1)
|
||||
|
||||
// Then Alice's secrets from Element Classic should be imported.
|
||||
#expect(encryption.importSecretsBundleSecretsBundleCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
@MainActor
|
||||
mutating func classicAppAccountSecretsBundleIsIgnoredWhenUnavailable() async throws {
|
||||
// Given an authentication service with an Element Classic account for Alice
|
||||
// which isn't configured with any available secrets.
|
||||
try await setup(classicAppAccounts: [.mockAlice], availableSecrets: .unavailable)
|
||||
try await service.configure(for: "matrix.org", flow: .login).get()
|
||||
#expect(service.flow == .login)
|
||||
#expect(service.classicAppAccount?.state.availableSecrets == .unavailable)
|
||||
|
||||
// When logging in as Alice.
|
||||
_ = try await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil).get()
|
||||
#expect(client.loginUsernamePasswordInitialDeviceNameDeviceIdCallsCount == 1)
|
||||
|
||||
// Then an attempt to import Alice's secrets from Element Classic must not be made.
|
||||
#expect(!encryption.importSecretsBundleSecretsBundleCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
@MainActor
|
||||
mutating func classicAppAccountSecretsBundleIsIgnoredForDifferentUser() async throws {
|
||||
// Given an authentication service with an Element Classic account for Dan.
|
||||
try await setup(classicAppAccounts: [.mockDan])
|
||||
try await service.configure(for: "matrix.org", flow: .login).get()
|
||||
#expect(service.flow == .login)
|
||||
#expect(service.classicAppAccount?.state.availableSecrets == .complete)
|
||||
|
||||
// When logging in as Alice
|
||||
_ = try await service.login(username: "alice", password: "12345678", initialDeviceName: nil, deviceID: nil).get()
|
||||
#expect(client.loginUsernamePasswordInitialDeviceNameDeviceIdCallsCount == 1)
|
||||
|
||||
// Then Dan's secrets from Element Calssic should not be imported into Alice's client.
|
||||
#expect(!encryption.importSecretsBundleSecretsBundleCalled)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private mutating func setup(serverAddress: String = "matrix.org") {
|
||||
private mutating func setup(serverAddress: String = "matrix.org",
|
||||
classicAppAccounts: [ClassicAppAccount] = [],
|
||||
availableSecrets: ClassicAppAccount.AvailableSecrets = .complete) async throws {
|
||||
let configuration: AuthenticationClientFactoryMock.Configuration = .init()
|
||||
let clientFactory = AuthenticationClientFactoryMock(configuration: configuration)
|
||||
|
||||
client = configuration.homeserverClients[serverAddress]
|
||||
encryption = EncryptionSDKMock()
|
||||
client.encryptionReturnValue = encryption
|
||||
|
||||
userSessionStore = UserSessionStoreMock(configuration: .init())
|
||||
encryptionKeyProvider = MockEncryptionKeyProvider()
|
||||
|
||||
let classicAppManager = ClassicAppManagerMock(.init(accounts: classicAppAccounts,
|
||||
availableSecrets: availableSecrets,
|
||||
secretsBundle: .init(noHandle: .init())))
|
||||
|
||||
service = AuthenticationService(userSessionStore: userSessionStore,
|
||||
encryptionKeyProvider: encryptionKeyProvider,
|
||||
classicAppManager: nil,
|
||||
classicAppManager: classicAppManager,
|
||||
clientFactory: clientFactory,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appHooks: AppHooks())
|
||||
|
||||
if let classicAppAccount = service.classicAppAccount {
|
||||
await service.setupClassicAppAccountState()
|
||||
try #require(classicAppAccount.state.isServerSupported == true)
|
||||
try #require(classicAppAccount.state.availableSecrets == availableSecrets)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user