Skip to content

Commit

Permalink
PIA-882: Add unit and integration tests for Login feature on tvOs
Browse files Browse the repository at this point in the history
  • Loading branch information
kp-said-rehouni committed Dec 11, 2023
1 parent 9925103 commit 9eca3a8
Show file tree
Hide file tree
Showing 10 changed files with 727 additions and 0 deletions.
63 changes: 63 additions & 0 deletions PIA VPN-tvOSTests/Login/CheckLoginAvailabilityTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// CheckLoginAvailabilityTests.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 29/11/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import XCTest
@testable import PIA_VPN_tvOS

final class CheckLoginAvailabilityTests: XCTestCase {

func test_checkLoginAvailability_returns_success_when_there_is_no_delay() {
// GIVEN
let sut = CheckLoginAvailability()

// WHEN
let result = sut()

// THEN
guard case .success = result else {
XCTFail("Expected success, got failure")
return
}
}

func test_checkLoginAvailability_returns_success_when_delay_expired() {
// GIVEN
let sut = CheckLoginAvailability()

// WHEN
sut.disableLoginFor(Date().timeIntervalSince1970 - 1)
let result = sut()

// THEN
guard case .success = result else {
XCTFail("Expected success, got failure")
return
}
}

func test_checkLoginAvailability_returns_failure_when_delay_has_not_expired_yet() {
// GIVEN
let date = Date()
let sut = CheckLoginAvailability()

// WHEN
sut.disableLoginFor(date.timeIntervalSince1970 + 10)
let result = sut()

// THEN
guard case .failure(let error) = result else {
XCTFail("Expected success, got failure")
return
}

guard case .throttled = error else {
XCTFail("Expected throttled error, got \(error)")
return
}
}
}
24 changes: 24 additions & 0 deletions PIA VPN-tvOSTests/Login/Helpers/CheckLoginAvailabilityMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// CheckLoginAvailabilityMock.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 29/11/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import Foundation
@testable import PIA_VPN_tvOS

class CheckLoginAvailabilityMock: CheckLoginAvailabilityType {
private let result: Result<Void, LoginError>

init(result: Result<Void, LoginError>) {
self.result = result
}

func disableLoginFor(_ delay: Double) {}

func callAsFunction() -> Result<Void, LoginError> {
return result
}
}
68 changes: 68 additions & 0 deletions PIA VPN-tvOSTests/Login/Helpers/Equatable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Equatable.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 29/11/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import Foundation
@testable import PIA_VPN_tvOS
import PIALibrary

extension LoginStatus: Equatable {
public static func == (lhs: LoginStatus, rhs: LoginStatus) -> Bool {
switch (lhs, rhs) {
case (.none, .none), (.isLogging, .isLogging):
return true
case let (.failed(lhsError), .failed(rhsError)):
return lhsError == rhsError
case let (.succeeded(lhsAccount), .succeeded(rhsAccount)):
return lhsAccount == rhsAccount
default:
return false
}
}
}

extension LoginError: Equatable {
public static func == (lhs: LoginError, rhs: LoginError) -> Bool {
switch (lhs, rhs) {
case (.unauthorized, .unauthorized), (.expired, .expired), (.usernameWrongFormat, .usernameWrongFormat), (.passwordWrongFormat, .passwordWrongFormat):
return true
case let (.throttled(lhsDelay), .throttled(rhsDelay)):
return lhsDelay == rhsDelay
default:
return false
}
}
}

extension UserAccount: Equatable {
public static func == (lhs: PIALibrary.UserAccount, rhs: PIALibrary.UserAccount) -> Bool {
lhs.credentials == rhs.credentials
&& lhs.info == rhs.info
}
}

extension Credentials: Equatable {
public static func == (lhs: Credentials, rhs: Credentials) -> Bool {
lhs.username == rhs.username && lhs.password == rhs.password
}
}

extension AccountInfo: Equatable {
public static func == (lhs: AccountInfo, rhs: AccountInfo) -> Bool {
lhs.email == rhs.email
&& lhs.username == rhs.username
&& lhs.plan == rhs.plan
&& lhs.productId == rhs.productId
&& lhs.isRenewable == rhs.isRenewable
&& lhs.isRecurring == rhs.isRecurring
&& lhs.expirationDate == rhs.expirationDate
&& lhs.canInvite == rhs.canInvite
&& lhs.isExpired == rhs.isExpired
&& lhs.shouldPresentExpirationAlert == rhs.shouldPresentExpirationAlert
&& lhs.renewUrl == rhs.renewUrl
}
}
25 changes: 25 additions & 0 deletions PIA VPN-tvOSTests/Login/Helpers/LoginProviderMock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// LoginProviderMock.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 4/12/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import Foundation
import PIALibrary
@testable import PIA_VPN_tvOS

class LoginProviderMock: LoginProviderType {
private let userResult: UserAccount?
private let errorResult: Error?

init(userResult: UserAccount?, errorResult: Error?) {
self.userResult = userResult
self.errorResult = errorResult
}

func login(with request: LoginRequest, _ callback: LibraryCallback<UserAccount>?) {
callback?(userResult, errorResult)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// LoginWithCredentialsUseCaseMock.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 29/11/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import Foundation
@testable import PIA_VPN_tvOS
import PIALibrary

class LoginWithCredentialsUseCaseMock: LoginWithCredentialsUseCaseType {
private let result: Result<UserAccount, LoginError>

init(result: Result<UserAccount, LoginError>) {
self.result = result
}

func execute(username: String, password: String, completion: @escaping (Result<UserAccount, LoginError>) -> Void) {
completion(result)
}
}
42 changes: 42 additions & 0 deletions PIA VPN-tvOSTests/Login/Helpers/Stubs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Stubs.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 29/11/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import Foundation
import PIALibrary
import account

extension UserAccount {
static func makeStub() -> UserAccount {
let credentials = Credentials(username: "username",
password: "password")
return UserAccount(credentials: credentials,
info: AccountInfo.makeStub())
}
}

extension AccountInfo {
static func makeStub() -> AccountInfo {
let account = AccountInformation(active: true,
canInvite: true,
canceled: true,
daysRemaining: 0,
email: "email",
expirationTime: 0,
expireAlert: true,
expired: true,
needsPayment: true,
plan: "monthly",
productId: "productId",
recurring: true,
renewUrl: "renewUrl",
renewable: true,
username: "username")

return AccountInfo(accountInformation: account)
}
}
56 changes: 56 additions & 0 deletions PIA VPN-tvOSTests/Login/LoginIntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// LoginIntegrationTests.swift
// PIA VPN-tvOSTests
//
// Created by Said Rehouni on 11/12/23.
// Copyright © 2023 Private Internet Access Inc. All rights reserved.
//

import XCTest
import PIALibrary
@testable import PIA_VPN_tvOS

final class LoginIntegrationTests: XCTestCase {

func test_login_succeeds() async throws {
// GIVEN
let userAccount = UserAccount.makeStub()
let loginProviderMock = LoginProviderMock(userResult: userAccount, errorResult: nil)
let loginWithCredentialsUseCase = LoginWithCredentialsUseCase(loginProvider: loginProviderMock,
errorMapper: LoginDomainErrorMapper())

let sut = LoginViewModel(loginWithCredentialsUseCase: loginWithCredentialsUseCase,
checkLoginAvailability: CheckLoginAvailability(),
validateLoginCredentials: ValidateCredentialsFormat(),
errorMapper: LoginPresentableErrorMapper())

XCTAssertEqual(sut.loginStatus, .none)

// WHEN
await sut.login(username: "username", password: "password")

// THEN
XCTAssertEqual(sut.loginStatus, LoginStatus.succeeded(userAccount: userAccount))
}

func test_login_fails() async throws {
// GIVEN
let userAccount = UserAccount.makeStub()
let loginProviderMock = LoginProviderMock(userResult: userAccount, errorResult: ClientError.expired)
let loginWithCredentialsUseCase = LoginWithCredentialsUseCase(loginProvider: loginProviderMock,
errorMapper: LoginDomainErrorMapper())

let sut = LoginViewModel(loginWithCredentialsUseCase: loginWithCredentialsUseCase,
checkLoginAvailability: CheckLoginAvailability(),
validateLoginCredentials: ValidateCredentialsFormat(),
errorMapper: LoginPresentableErrorMapper())

XCTAssertEqual(sut.loginStatus, .none)

// WHEN
await sut.login(username: "username", password: "password")

// THEN
XCTAssertEqual(sut.loginStatus, .failed(error: .expired))
}
}
Loading

0 comments on commit 9eca3a8

Please sign in to comment.