Add initial tests on the authentication service. (#6229)

This commit is contained in:
Doug
2022-06-06 13:20:46 +01:00
committed by GitHub
parent 6139e34ae0
commit c3c78a51fd
21 changed files with 718 additions and 31 deletions

View File

@@ -18,22 +18,33 @@ import XCTest
@testable import Riot
class AuthenticationServiceTests: XCTestCase {
func testRegistrationWizardWhenStartingLoginFlow() async throws {
@MainActor class AuthenticationServiceTests: XCTestCase {
var service: AuthenticationService!
/// Makes a new service configured for testing.
@MainActor override func setUp() {
service = AuthenticationService(sessionCreator: MockSessionCreator())
service.clientType = MockAuthenticationRestClient.self
}
// MARK: - Service State
func testWizardsWhenStartingLoginFlow() async throws {
// Given a fresh service.
let service = AuthenticationService()
XCTAssertNil(service.loginWizard, "A new service shouldn't have a login wizard.")
XCTAssertNil(service.registrationWizard, "A new service shouldn't have a registration wizard.")
// When starting a new login flow.
try await service.startFlow(.login, for: "https://matrix.org")
// Then a registration wizard shouldn't have been created.
XCTAssertNotNil(service.loginWizard, "The login wizard should exist after starting a login flow.")
XCTAssertNil(service.registrationWizard, "The registration wizard should not exist if startFlow was called for login.")
}
func testRegistrationWizard() async throws {
func testWizardsWhenStartingRegistrationFlow() async throws {
// Given a fresh service.
let service = AuthenticationService()
XCTAssertNil(service.loginWizard, "A new service shouldn't have a login wizard.")
XCTAssertNil(service.registrationWizard, "A new service shouldn't provide a registration wizard.")
XCTAssertNil(service.state.homeserver.registrationFlow, "A new service shouldn't provide a registration flow for the homeserver.")
@@ -41,13 +52,13 @@ class AuthenticationServiceTests: XCTestCase {
try await service.startFlow(.register, for: "https://matrix.org")
// Then a registration wizard should be available for use.
XCTAssertNotNil(service.loginWizard, "The login wizard should exist after starting a registration flow.")
XCTAssertNotNil(service.registrationWizard, "The registration wizard should exist after starting a registration flow.")
XCTAssertNotNil(service.state.homeserver.registrationFlow, "The supported registration flow should be stored after starting a registration flow.")
}
func testReset() async throws {
// Given a service that has begun registration.
let service = AuthenticationService()
try await service.startFlow(.register, for: "https://matrix.org")
_ = try await service.registrationWizard?.createAccount(username: UUID().uuidString, password: UUID().uuidString, initialDeviceDisplayName: "Test")
XCTAssertNotNil(service.loginWizard, "The login wizard should exist after starting a registration flow.")
@@ -55,6 +66,8 @@ class AuthenticationServiceTests: XCTestCase {
XCTAssertNotNil(service.state.homeserver.registrationFlow, "The supported registration flow should be stored after starting a registration flow.")
XCTAssertTrue(service.isRegistrationStarted, "The service should show as having started registration.")
XCTAssertEqual(service.state.flow, .register, "The service should show as using a registration flow.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix-client.matrix.org", "The actual homeserver address should be discovered.")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://matrix.org", "The address from the startFlow call should be stored.")
// When resetting the service.
service.reset()
@@ -65,14 +78,14 @@ class AuthenticationServiceTests: XCTestCase {
XCTAssertNil(service.state.homeserver.registrationFlow, "The supported registration flow should be cleared when calling reset.")
XCTAssertFalse(service.isRegistrationStarted, "The service should not indicate it has started registration after calling reset.")
XCTAssertEqual(service.state.flow, .login, "The flow should have been set back to login when calling reset.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.org", "The address should reset to the value entered by the user.")
}
func testHomeserverState() async throws {
// Given a service that has begun login for one homeserver.
let service = AuthenticationService()
try await service.startFlow(.login, for: "https://glasgow.social")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://glasgow.social", "The initial address entered by the user should be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.glasgow.social", "The initial address discovered from the well-known should be stored.")
try await service.startFlow(.login, for: "https://example.com")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://example.com", "The initial address entered by the user should be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.example.com", "The initial address discovered from the well-known should be stored.")
// When switching to a different homeserver
try await service.startFlow(.login, for: "https://matrix.org")
@@ -82,6 +95,142 @@ class AuthenticationServiceTests: XCTestCase {
XCTAssertEqual(service.state.homeserver.address, "https://matrix-client.matrix.org", "The new address discovered from the well-known should be stored.")
}
func testStartingLoginWithInvalidURL() async throws {
// Given a service that has started the register flow for one homeserver.
try await service.startFlow(.login, for: "https://example.com")
XCTAssertEqual(service.client.homeserver, "https://matrix.example.com", "The client should be set up for the homeserver")
XCTAssertEqual(service.state.flow, .login, "The flow should be set as login.")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://example.com", "The initial address entered by the user should be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.example.com", "The initial address discovered from the well-known should be stored.")
// When failing to start login by entering an invalid address.
do {
try await service.startFlow(.login, for: "https://google.com")
XCTFail("The registration flow should fail for an incorrect homeserver address.")
} catch {
XCTAssertNotNil(error, "The client should throw an error for an incorrect address.")
}
// Then the service's state and client should be unchanged.
XCTAssertEqual(service.client.homeserver, "https://matrix.example.com", "The client should be set up for the homeserver")
XCTAssertEqual(service.state.flow, .login, "The flow should still be set as login.")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://example.com", "The initial address entered by the user should be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.example.com", "The initial address discovered from the well-known should be stored.")
}
func testStartingRegistrationForLoginOnlyServer() async throws {
// Given a service that has started the register flow for one homeserver.
try await service.startFlow(.register, for: "https://example.com")
XCTAssertEqual(service.client.homeserver, "https://matrix.example.com", "The client should be set up for the homeserver")
XCTAssertEqual(service.state.flow, .register, "The flow should be set as registration.")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://example.com", "The initial address entered by the user should be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.example.com", "The initial address discovered from the well-known should be stored.")
// When failing to start registration for another homeserver that only supports login.
do {
try await service.startFlow(.register, for: "https://private.com")
XCTFail("The registration flow should fail for a server that doesn't support registration")
} catch {
XCTAssertEqual(error as? MockAuthenticationRestClient.MockError, MockAuthenticationRestClient.MockError.registrationDisabled,
"The client should throw with disabled registration.")
}
// The the service's state and client should be unchanged.
XCTAssertEqual(service.client.homeserver, "https://matrix.example.com", "The client should still be set up for the homeserver")
XCTAssertEqual(service.state.flow, .register, "The flow should still be set as registration.")
XCTAssertEqual(service.state.homeserver.addressFromUser, "https://example.com", "The initial address entered by the user should still be stored.")
XCTAssertEqual(service.state.homeserver.address, "https://matrix.example.com", "The initial address discovered from the well-known should still be stored.")
}
func testPasswordLogin() async throws {
// Given a server ready for login.
try await service.startFlow(.login, for: "https://matrix.org")
guard let loginWizard = service.loginWizard else {
XCTFail("The login wizard should exist after starting a login flow.")
return
}
// When logging in with valid credentials.
let account = MockAuthenticationRestClient.registeredAccount
let session = try await loginWizard.login(login: account.username,
password: account.password,
initialDeviceName: UIDevice.current.initialDisplayName)
// Then the MXSession should be created for the user ID.
XCTAssertEqual(session.myUserId, "@alice:matrix.org")
}
func testBasicRegistration() async throws {
// Given a basic server ready for registration (only has a dummy stage).
try await service.startFlow(.register, for: "https://example.com")
guard let registrationWizard = service.registrationWizard else {
XCTFail("The registration wizard should exist after starting a registration flow.")
return
}
// When registering with a username and password.
let result = try await registrationWizard.createAccount(username: "bob",
password: "password",
initialDeviceDisplayName: "whatever")
// Then an MXSession should be created for the new account.
guard case let .success(session) = result else {
XCTFail("The dummy stage should be performed and registration should be successful.")
return
}
XCTAssertEqual(session.myUserId, "@bob:example.com")
}
func testInteractiveRegistration() async throws {
// Given a server ready for registration with multiple mandatory stages.
try await service.startFlow(.register, for: "https://matrix.org")
guard let registrationWizard = service.registrationWizard else {
XCTFail("The registration wizard should exist after starting a registration flow.")
return
}
XCTAssertFalse(registrationWizard.state.isRegistrationStarted, "Registration should not be started yet.")
// When registering with a username and password.
let createAccountResult = try await registrationWizard.createAccount(username: "bob",
password: "password",
initialDeviceDisplayName: "whatever")
// Then the registration should be started and be waiting for all of the stages to be completed.
guard case let .flowResponse(flowResult) = createAccountResult else {
XCTFail("The registration should not have completed.")
return
}
XCTAssertEqual(flowResult.completedStages.count, 0)
XCTAssertEqual(flowResult.missingStages.count, 3)
XCTAssertTrue(registrationWizard.state.isRegistrationStarted, "Registration should be started after calling create account.")
// TODO: Email step
// When performing the terms stage.
let termsResult = try await registrationWizard.acceptTerms()
// Then the completed and missing stages should be updated accordingly.
guard case let .flowResponse(termsFlowResult) = termsResult else {
XCTFail("The registration should not have completed.")
return
}
XCTAssertEqual(termsFlowResult.completedStages.count, 1)
XCTAssertEqual(termsFlowResult.missingStages.count, 2)
// When performing the ReCaptcha stage.
let reCaptchaResult = try await registrationWizard.performReCaptcha(response: "trafficlights")
// Then the completed and missing stages should be updated accordingly.
guard case let .flowResponse(reCaptchaFlowResult) = reCaptchaResult else {
XCTFail("The registration should not have completed.")
return
}
XCTAssertEqual(reCaptchaFlowResult.completedStages.count, 2)
XCTAssertEqual(reCaptchaFlowResult.missingStages.count, 1)
}
// MARK: - Homeserver View Data
func testHomeserverViewDataForMatrixDotOrg() {
// Given a homeserver such as matrix.org.
let address = "https://matrix-client.matrix.org"

View File

@@ -0,0 +1,13 @@
{
"flows": [
{
"type": "m.login.password"
},
{
"type": "m.login.application_service"
},
{
"type": "uk.half-shot.msc2778.login.application_service"
}
]
}

View File

@@ -0,0 +1,11 @@
{
"session": "123456",
"flows": [
{
"stages": [
"m.login.dummy"
]
}
],
"params": {}
}

View File

@@ -0,0 +1,5 @@
{
"m.homeserver": {
"base_url": "https://matrix.example.com"
}
}

View File

@@ -0,0 +1,13 @@
{
"flows": [
{
"type": "m.login.password"
},
{
"type": "m.login.application_service"
},
{
"type": "uk.half-shot.msc2778.login.application_service"
}
]
}

View File

@@ -0,0 +1,48 @@
{
"flows": [
{
"type": "m.login.sso",
"identity_providers": [
{
"id": "oidc-github",
"name": "GitHub",
"icon": "mxc://matrix.org/sVesTtrFDTpXRbYfpahuJsKP",
"brand": "github"
},
{
"id": "oidc-google",
"name": "Google",
"icon": "mxc://matrix.org/ZlnaaZNPxtUuQemvgQzlOlkz",
"brand": "google"
},
{
"id": "oidc-gitlab",
"name": "GitLab",
"icon": "mxc://matrix.org/MCVOEmFgVieKFshPxmnejWOq",
"brand": "gitlab"
},
{
"id": "oidc-facebook",
"name": "Facebook",
"icon": "mxc://matrix.org/nsyeLIgzxazZmJadflMAsAWG",
"brand": "facebook"
},
{
"id": "oidc-apple",
"name": "Apple",
"icon": "mxc://matrix.org/QQKNSOdLiMHtJhzeAObmkFiU",
"brand": "apple"
}
]
},
{
"type": "m.login.token"
},
{
"type": "m.login.password"
},
{
"type": "m.login.application_service"
}
]
}

View File

@@ -0,0 +1,28 @@
{
"session": "123456",
"flows": [
{
"stages": [
"m.login.recaptcha",
"m.login.terms",
"m.login.email.identity"
]
}
],
"params": {
"m.login.recaptcha": {
"public_key": "1234"
},
"m.login.terms": {
"policies": {
"privacy_policy": {
"version": "1.0",
"en": {
"name": "Terms and Conditions",
"url": "https://matrix-client.matrix.org/_matrix/consent?v=1.0"
}
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"m.homeserver": {
"base_url": "https://matrix-client.matrix.org"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
}

View File

@@ -0,0 +1,19 @@
{
"flows": [
{
"type": "m.login.sso",
"identity_providers": [
{
"id": "saml",
"name": "SAML"
}
]
},
{
"type": "m.login.token"
},
{
"type": "m.login.application_service"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"m.homeserver": {
"base_url": "https://matrix.company.com"
},
"m.identity_server": {
"base_url": "https://identity.company.com"
}
}

View File

@@ -0,0 +1,211 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
@testable import Riot
/// A mock REST client that can be used for authentication.
class MockAuthenticationRestClient: AuthenticationRestClient {
enum MockError: Error {
/// The fixture is missing.
case fixture
/// The method isn't implemented.
case unhandled
/// Login attempted with incorrect credentials.
case invalidCredentials
/// The homeserver doesn't allow for registration.
case registrationDisabled
/// A registration stage was attempted without first registering a username and password.
case createAccountNotCalled
/// The request is invalid.
case invalidRequest
}
/// An account to test password based login with.
static let registeredAccount = (username: "alice", email: "alice@example.com", phone: "+447777777777", password: "password")
/// A token to test token based login with.
static var pendingLoginToken = "000000"
// MARK: - Configuration
/// The client's internal configuration.
var config: Config
/// The homeserver's URL string.
var homeserver: String! { homeserverURL.absoluteString }
/// Unused: The identity server.
var identityServer: String!
/// Unused: The credentials.
var credentials: MXCredentials!
/// Unused: The type of content to accept for responses.
var acceptableContentTypes: Set<String>!
// MARK: - Private
/// The URL used to create the client with.
private var homeserverURL: URL
/// Unused: A callback for handling an unrecognized certificate.
private var unrecognizedCertificateHandler: MXHTTPClientOnUnrecognizedCertificate?
/// The credentials for a pending account creation.
private var newAccount: (username: String, password: String)?
/// The stages completed in the registration flow.
private var completedStages: Set<String> = []
// MARK: - Setup
/// Creates a new mock client.
/// - Parameters:
/// - homeServer: See `MockAuthenticationRestClient.Config` for various URLs that can be used.
/// - handler: Unused.
required init(homeServer: URL, unrecognizedCertificateHandler handler: MXHTTPClientOnUnrecognizedCertificate?) {
self.config = Config(url: homeServer)
self.homeserverURL = homeServer
self.unrecognizedCertificateHandler = handler
}
// MARK: - Login
var loginFallbackURL: URL {
homeserverURL.appendingPathComponent("_matrix/static/client/login")
}
func wellKnown() async throws -> MXWellKnown {
try MXWellKnown(fromJSON: config.wellKnownJSON())
}
func getLoginSession() async throws -> MXAuthenticationSession {
try MXAuthenticationSession(fromJSON: config.loginSessionJSON())
}
func login(parameters: LoginParameters) async throws -> MXCredentials {
if let passwordParameters = parameters as? LoginPasswordParameters {
return try login(passwordParameters: passwordParameters)
} else if let tokenParameters = parameters as? LoginTokenParameters {
return try login(tokenParameters: tokenParameters)
} else {
throw MockError.unhandled
}
}
/// Checks login against the `registeredAccount` and returns credentials if valid.
private func login(passwordParameters: LoginPasswordParameters) throws -> MXCredentials {
switch passwordParameters.id {
case .user(let username):
guard username == Self.registeredAccount.username else { throw MockError.invalidCredentials }
case .thirdParty(medium: let medium, address: let address):
guard medium == .email, address == Self.registeredAccount.email else { throw MockError.invalidCredentials }
case .phone(country: let country, phone: let phone):
guard "+\(country)\(phone)" == Self.registeredAccount.phone else { throw MockError.invalidCredentials }
}
guard passwordParameters.password == Self.registeredAccount.password else { throw MockError.invalidCredentials }
return makeCredentials()
}
/// Checks login against the `pendingLoginToken` and returns credentials if valid.
private func login(tokenParameters: LoginTokenParameters) throws -> MXCredentials {
guard tokenParameters.token == Self.pendingLoginToken else { throw MockError.invalidCredentials }
return makeCredentials()
}
/// Mock credentials for the registered account.
private func makeCredentials() -> MXCredentials {
MXCredentials(homeServer: homeserver,
userId: "@\(Self.registeredAccount.username):\(config.baseURL)",
accessToken: "1234")
}
func login(parameters: [String : Any]) async throws -> MXCredentials {
throw MockError.unhandled
}
// MARK: - Registration
var registerFallbackURL: URL {
homeserverURL.appendingPathComponent("_matrix/static/client/register")
}
func getRegisterSession() async throws -> MXAuthenticationSession {
try MXAuthenticationSession(fromJSON: config.registerSessionJSON())
}
func isUsernameAvailable(_ username: String) async throws -> Bool {
username != Self.registeredAccount.username
}
func register(parameters: RegistrationParameters) async throws -> MXLoginResponse {
guard let supportedStages = config.supportedStages else { throw MockError.registrationDisabled }
let success = attemptStage(with: parameters)
guard success, completedStages == supportedStages, let newAccount = newAccount else {
var errorResponse = try config.registerSessionJSON()
errorResponse["completed"] = Array(completedStages)
let nsError = NSError(domain: "mock",
code: 401,
userInfo: [MXHTTPClientErrorResponseDataKey: errorResponse])
throw nsError
}
let response = MXLoginResponse()
response.accessToken = "1234"
response.homeserver = homeserver
response.userId = "@\(newAccount.username):\(config.baseURL)"
return response
}
/// Returns a boolean indicating whether the stage was completed or not.
private func attemptStage(with parameters: RegistrationParameters) -> Bool {
if let username = parameters.username, let password = parameters.password {
newAccount = (username: username, password: password)
return true
}
guard newAccount != nil else { return false }
guard let auth = parameters.auth else { return false }
completedStages.insert(auth.type)
return true
}
func register(parameters: [String : Any]) async throws -> MXLoginResponse {
throw MockError.unhandled
}
func requestTokenDuringRegistration(for threePID: RegisterThreePID, clientSecret: String, sendAttempt: UInt) async throws -> RegistrationThreePIDTokenResponse {
throw MockError.unhandled
}
// MARK: - Forgot password
func forgetPassword(for email: String, clientSecret: String, sendAttempt: UInt) async throws -> String {
throw MockError.unhandled
}
func resetPassword(parameters: CheckResetPasswordParameters) async throws {
throw MockError.unhandled
}
func resetPassword(parameters: [String : Any]) async throws {
throw MockError.unhandled
}
}

View File

@@ -0,0 +1,127 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Represents a homeserver configuration used for the mock authentication client.
extension MockAuthenticationRestClient {
enum Config: String {
/// A homeserver that mimics matrix.org with both passwords and SSO.
/// Create the client using https://matrix.org for this configuration.
case matrix
/// A homeserver that supports login and registration using a password.
/// Create the client using https://example.com for this configuration.
case basic
/// A homeserver that only supports login using a password and has registration disabled.
/// This configuration doesn't returns a well-known response.
/// Create the client using https://private.com for this configuration.
case loginOnly
/// A homeserver the only supports login via SSO and has registration disabled.
/// This configuration has a custom identity server configured.
/// Create the client using https://company.com for this configuration.
case ssoOnly
/// The client if configured to use an unknown address.
/// Create the client using any other address for this configuration.
case unknown
init(url: URL) {
switch url.absoluteString {
case "https://matrix.org", "https://matrix-client.matrix.org":
self = .matrix
case "https://example.com", "https://matrix.example.com":
self = .basic
case "https://private.com":
self = .loginOnly
case "https://company.com", "https://matrix.company.com":
self = .ssoOnly
default:
self = .unknown
}
}
/// The baseURL for the homeserver.
var baseURL: String {
switch self {
case .matrix:
return "matrix.org"
case .basic:
return "example.com"
case .loginOnly:
return "private.com"
case .ssoOnly:
return "company.com"
case .unknown:
return ""
}
}
/// The supported stages when performing interactive registration.
var supportedStages: Set<String>? {
switch self {
case .matrix:
return [kMXLoginFlowTypeRecaptcha, kMXLoginFlowTypeTerms, kMXLoginFlowTypeEmailIdentity]
case .basic:
return [kMXLoginFlowTypeDummy]
case .loginOnly, .ssoOnly, .unknown:
return nil
}
}
/// Returns the well-known JSON for this configuration
func wellKnownJSON() throws -> [AnyHashable: Any] {
try fixtureJSON(named: "wellknown")
}
/// Returns the login session JSON for this configuration
func loginSessionJSON() throws -> [AnyHashable: Any] {
try fixtureJSON(named: "loginsession")
}
/// Returns the register session JSON for this configuration
func registerSessionJSON() throws -> [AnyHashable: Any] {
switch self {
case .matrix, .basic:
return try fixtureJSON(named: "registersession")
case .loginOnly, .ssoOnly:
throw MockError.registrationDisabled
case .unknown:
throw MockError.unhandled
}
}
/// Loads a JSON fixture for this configuration.
/// - Parameter fileName: The file name of the fixture without the configuration prefix.
private func fixtureJSON(named fileName: String) throws -> [AnyHashable: Any] {
let fileName = "\(rawValue)-\(fileName)"
let data = try fixtureData(named: fileName)
guard let jsonDictionary = try JSONSerialization.jsonObject(with: data) as? [AnyHashable: Any] else { throw MockError.fixture }
return jsonDictionary
}
/// Loads the raw data for a fixture from disk.
/// - Parameter fileName: The file name of the fixture as stored in the bundle.
private func fixtureData(named fileName: String) throws -> Data {
let bundle = Bundle(for: MockAuthenticationRestClient.self)
guard let url = bundle.url(forResource: fileName, withExtension: "json") else { throw MockError.fixture }
return try Data(contentsOf: url)
}
}
}

View File

@@ -0,0 +1,28 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
@testable import Riot
struct MockSessionCreator: SessionCreatorProtocol {
/// Returns a basic session created from the supplied credentials. This prevents the app from setting up the account during tests.
func createSession(credentials: MXCredentials, client: AuthenticationRestClient) -> MXSession {
let client = MXRestClient(credentials: credentials,
unauthenticatedHandler: { _,_,_,_ in }) // The handler is expected if credentials are set.
return MXSession(matrixRestClient: client)
}
}