288 lines
9.4 KiB
Swift
288 lines
9.4 KiB
Swift
//
|
|
// Copyright 2022-2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Please see LICENSE in the repository root for full details.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import Element
|
|
@testable import MatrixSDK
|
|
|
|
private let currentDeviceId = "deviceId"
|
|
private let unverifiedDeviceId = "unverifiedDeviceId"
|
|
private let currentUserId = "userId"
|
|
|
|
class UserSessionsOverviewServiceTests: XCTestCase {
|
|
func testInitialSessionUnverified() {
|
|
let dataProvider = MockUserSessionsDataProvider(mode: .currentSessionUnverified)
|
|
let service = UserSessionsOverviewService(dataProvider: dataProvider)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .unverified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
XCTAssertFalse(service.unverifiedSessions.isEmpty)
|
|
XCTAssertTrue(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.linkDeviceEnabled)
|
|
|
|
XCTAssertEqual(service.sessionForIdentifier(currentDeviceId), service.currentSession)
|
|
}
|
|
|
|
func testInitialSessionVerified() {
|
|
let dataProvider = MockUserSessionsDataProvider(mode: .currentSessionVerified)
|
|
let service = UserSessionsOverviewService(dataProvider: dataProvider)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .verified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
XCTAssertTrue(service.unverifiedSessions.isEmpty)
|
|
XCTAssertTrue(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.linkDeviceEnabled)
|
|
}
|
|
|
|
func testWithAllSessionsVerified() {
|
|
let service = setupServiceWithMode(.allOtherSessionsValid)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .verified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
|
|
XCTAssertTrue(service.unverifiedSessions.isEmpty)
|
|
XCTAssertTrue(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.otherSessions.isEmpty)
|
|
XCTAssertTrue(service.linkDeviceEnabled)
|
|
}
|
|
|
|
func testWithSomeUnverifiedSessions() {
|
|
let service = setupServiceWithMode(.someUnverifiedSessions)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .verified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
|
|
XCTAssertFalse(service.unverifiedSessions.isEmpty)
|
|
XCTAssertTrue(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.otherSessions.isEmpty)
|
|
XCTAssertTrue(service.linkDeviceEnabled)
|
|
}
|
|
|
|
func testWithSomeInactiveSessions() {
|
|
let service = setupServiceWithMode(.someInactiveSessions)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .verified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
|
|
XCTAssertTrue(service.unverifiedSessions.isEmpty)
|
|
XCTAssertFalse(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.otherSessions.isEmpty)
|
|
XCTAssertTrue(service.linkDeviceEnabled)
|
|
}
|
|
|
|
func testWithSomeUnverifiedAndInactiveSessions() {
|
|
let service = setupServiceWithMode(.someUnverifiedAndInactiveSessions)
|
|
|
|
XCTAssertNotNil(service.currentSession)
|
|
XCTAssertEqual(service.currentSession?.verificationState, .verified)
|
|
XCTAssertTrue(service.currentSession?.isActive ?? false)
|
|
|
|
XCTAssertFalse(service.unverifiedSessions.isEmpty)
|
|
XCTAssertFalse(service.inactiveSessions.isEmpty)
|
|
XCTAssertFalse(service.otherSessions.isEmpty)
|
|
XCTAssertTrue(service.linkDeviceEnabled)
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func setupServiceWithMode(_ mode: MockUserSessionsDataProvider.Mode) -> UserSessionsOverviewServiceProtocol {
|
|
let dataProvider = MockUserSessionsDataProvider(mode: mode)
|
|
let service = UserSessionsOverviewService(dataProvider: dataProvider)
|
|
|
|
let expectation = expectation(description: "Wait for service update")
|
|
service.updateOverviewData { _ in
|
|
expectation.fulfill()
|
|
}
|
|
|
|
waitForExpectations(timeout: 1.0)
|
|
|
|
return service
|
|
}
|
|
}
|
|
|
|
private extension UserSessionsOverviewServiceProtocol {
|
|
var unverifiedSessions: [UserSessionInfo] { overviewDataPublisher.value.unverifiedSessions }
|
|
var inactiveSessions: [UserSessionInfo] { overviewDataPublisher.value.inactiveSessions }
|
|
var linkDeviceEnabled: Bool { overviewDataPublisher.value.linkDeviceEnabled }
|
|
}
|
|
|
|
private class MockUserSessionsDataProvider: UserSessionsDataProviderProtocol {
|
|
enum Mode {
|
|
case currentSessionUnverified
|
|
case currentSessionVerified
|
|
case allOtherSessionsValid
|
|
case someUnverifiedSessions
|
|
case someInactiveSessions
|
|
case someUnverifiedAndInactiveSessions
|
|
}
|
|
|
|
private let mode: Mode
|
|
|
|
var myDeviceId = currentDeviceId
|
|
|
|
var myUserId: String? = currentUserId
|
|
|
|
var activeAccounts: [MXKAccount] {
|
|
[MockAccount()]
|
|
}
|
|
|
|
init(mode: Mode) {
|
|
self.mode = mode
|
|
}
|
|
|
|
func devices(completion: @escaping (MXResponse<[MXDevice]>) -> Void) {
|
|
DispatchQueue.main.async {
|
|
switch self.mode {
|
|
case .currentSessionUnverified:
|
|
return
|
|
case .currentSessionVerified:
|
|
return
|
|
case .allOtherSessionsValid:
|
|
completion(.success(self.verifiedSessions))
|
|
case .someUnverifiedSessions:
|
|
completion(.success(self.verifiedSessions + self.unverifiedSessions))
|
|
case .someInactiveSessions:
|
|
completion(.success(self.verifiedSessions + self.inactiveSessions))
|
|
case .someUnverifiedAndInactiveSessions:
|
|
completion(.success(self.verifiedSessions + self.unverifiedSessions + self.inactiveSessions))
|
|
}
|
|
}
|
|
}
|
|
|
|
func device(withDeviceId deviceId: String, ofUser userId: String) -> MXDeviceInfo? {
|
|
guard deviceId == currentDeviceId else {
|
|
return MockDeviceInfo(deviceID: deviceId,
|
|
verified: deviceId != unverifiedDeviceId)
|
|
}
|
|
|
|
switch mode {
|
|
case .currentSessionUnverified:
|
|
return MockDeviceInfo(deviceID: deviceId, verified: false)
|
|
default:
|
|
return MockDeviceInfo(deviceID: deviceId, verified: true)
|
|
}
|
|
}
|
|
|
|
func verificationState(for deviceInfo: MXDeviceInfo?) -> UserSessionInfo.VerificationState {
|
|
guard let deviceInfo = deviceInfo else { return .unknown }
|
|
|
|
if let currentSession = device(withDeviceId: currentDeviceId, ofUser: currentUserId),
|
|
!currentSession.trustLevel.isVerified {
|
|
// When the current session is unverified we can't determine verification for other sessions.
|
|
return deviceInfo.deviceId == currentDeviceId ? .unverified : .unknown
|
|
}
|
|
|
|
return deviceInfo.trustLevel.isVerified ? .verified : .unverified
|
|
}
|
|
|
|
func accountData(for eventType: String) -> [AnyHashable : Any]? {
|
|
[:]
|
|
}
|
|
|
|
func qrLoginAvailable() async throws -> Bool {
|
|
true
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
var verifiedSessions: [MXDevice] {
|
|
[MockDevice(identifier: currentDeviceId, sessionActive: true),
|
|
MockDevice(identifier: UUID().uuidString, sessionActive: true)]
|
|
}
|
|
|
|
var unverifiedSessions: [MXDevice] {
|
|
[MockDevice(identifier: unverifiedDeviceId, sessionActive: true)]
|
|
}
|
|
|
|
var inactiveSessions: [MXDevice] {
|
|
[MockDevice(identifier: UUID().uuidString, sessionActive: false)]
|
|
}
|
|
}
|
|
|
|
private class MockAccount: MXKAccount {
|
|
override var device: MXDevice? {
|
|
MockDevice(identifier: currentDeviceId, sessionActive: true)
|
|
}
|
|
}
|
|
|
|
private class MockDevice: MXDevice {
|
|
private let identifier: String
|
|
private let sessionActive: Bool
|
|
|
|
init(identifier: String, sessionActive: Bool) {
|
|
self.identifier = identifier
|
|
self.sessionActive = sessionActive
|
|
super.init()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError()
|
|
}
|
|
|
|
override var deviceId: String {
|
|
get {
|
|
identifier
|
|
}
|
|
set {
|
|
|
|
}
|
|
}
|
|
|
|
override var lastSeenTs: UInt64 {
|
|
get {
|
|
if sessionActive {
|
|
return UInt64(Date().timeIntervalSince1970 * 1000)
|
|
} else {
|
|
let ninetyDays: Double = 90 * 86400
|
|
return UInt64((Date().timeIntervalSince1970 - ninetyDays) * 1000)
|
|
}
|
|
}
|
|
set {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
private class MockDeviceInfo: MXDeviceInfo {
|
|
private let verified: Bool
|
|
|
|
init(deviceID: String, verified: Bool) {
|
|
self.verified = verified
|
|
super.init(deviceId: deviceID)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError()
|
|
}
|
|
|
|
override var trustLevel: MXDeviceTrustLevel! {
|
|
MockDeviceTrustLevel(verified: verified)
|
|
}
|
|
}
|
|
|
|
private class MockDeviceTrustLevel: MXDeviceTrustLevel {
|
|
private let verified: Bool
|
|
|
|
init(verified: Bool) {
|
|
self.verified = verified
|
|
super.init()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError()
|
|
}
|
|
|
|
override var isVerified: Bool {
|
|
verified
|
|
}
|
|
}
|