220 lines
7.3 KiB
Swift
220 lines
7.3 KiB
Swift
//
|
|
// FeedlyRefreshAccessTokenOperationTests.swift
|
|
// AccountTests
|
|
//
|
|
// Created by Kiel Gillard on 4/11/19.
|
|
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
@testable import Account
|
|
import RSWeb
|
|
import RSCore
|
|
import Secrets
|
|
|
|
class FeedlyRefreshAccessTokenOperationTests: XCTestCase {
|
|
|
|
private var account: Account!
|
|
private let support = FeedlyTestSupport()
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
account = support.makeTestAccount()
|
|
}
|
|
|
|
override func tearDown() {
|
|
if let account = account {
|
|
support.destroy(account)
|
|
}
|
|
super.tearDown()
|
|
}
|
|
|
|
class TestRefreshTokenService: OAuthAccessTokenRefreshing {
|
|
var mockResult: Result<OAuthAuthorizationGrant, Error>?
|
|
var refreshAccessTokenExpectation: XCTestExpectation?
|
|
var parameterTester: ((String, OAuthAuthorizationClient) -> ())?
|
|
|
|
func refreshAccessToken(with refreshToken: String, client: OAuthAuthorizationClient, completion: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) {
|
|
|
|
guard let result = mockResult else {
|
|
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
|
|
return
|
|
}
|
|
parameterTester?(refreshToken, client)
|
|
DispatchQueue.main.async {
|
|
completion(result)
|
|
self.refreshAccessTokenExpectation?.fulfill()
|
|
}
|
|
}
|
|
}
|
|
|
|
func testCancel() {
|
|
let service = TestRefreshTokenService()
|
|
service.refreshAccessTokenExpectation = expectation(description: "Did Call Refresh")
|
|
service.refreshAccessTokenExpectation?.isInverted = true
|
|
|
|
let client = support.makeMockOAuthClient()
|
|
let refresh = FeedlyRefreshAccessTokenOperation(account: account, service: service, oauthClient: client, log: support.log)
|
|
|
|
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
|
|
let completionExpectation = expectation(description: "Did Finish")
|
|
refresh.completionBlock = { _ in
|
|
completionExpectation.fulfill()
|
|
}
|
|
|
|
MainThreadOperationQueue.shared.add(refresh)
|
|
|
|
MainThreadOperationQueue.shared.cancelOperations([refresh])
|
|
|
|
waitForExpectations(timeout: 1)
|
|
|
|
XCTAssertTrue(refresh.isCanceled)
|
|
}
|
|
|
|
class TestRefreshTokenDelegate: FeedlyOperationDelegate {
|
|
var error: Error?
|
|
var didFailExpectation: XCTestExpectation?
|
|
|
|
func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
|
|
self.error = error
|
|
didFailExpectation?.fulfill()
|
|
}
|
|
}
|
|
|
|
func testMissingRefreshToken() {
|
|
support.removeCredentials(matching: .oauthRefreshToken, from: account)
|
|
|
|
let service = TestRefreshTokenService()
|
|
service.refreshAccessTokenExpectation = expectation(description: "Did Call Refresh")
|
|
service.refreshAccessTokenExpectation?.isInverted = true
|
|
|
|
let client = support.makeMockOAuthClient()
|
|
let refresh = FeedlyRefreshAccessTokenOperation(account: account, service: service, oauthClient: client, log: support.log)
|
|
|
|
let delegate = TestRefreshTokenDelegate()
|
|
delegate.didFailExpectation = expectation(description: "Did Fail")
|
|
refresh.delegate = delegate
|
|
|
|
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
|
|
let completionExpectation = expectation(description: "Did Finish")
|
|
refresh.completionBlock = { _ in
|
|
completionExpectation.fulfill()
|
|
}
|
|
|
|
MainThreadOperationQueue.shared.add(refresh)
|
|
|
|
waitForExpectations(timeout: 1)
|
|
|
|
XCTAssertNotNil(delegate.error, "Should have failed with error.")
|
|
if let error = delegate.error {
|
|
switch error {
|
|
case let error as TransportError:
|
|
switch error {
|
|
case .httpError(status: let status):
|
|
XCTAssertEqual(status, 403, "Expected 403 Forbidden.")
|
|
default:
|
|
XCTFail("Expected 403 Forbidden")
|
|
}
|
|
default:
|
|
XCTFail("Expected \(TransportError.httpError(status: 403))")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testRefreshTokenSuccess() {
|
|
let service = TestRefreshTokenService()
|
|
service.refreshAccessTokenExpectation = expectation(description: "Did Call Refresh")
|
|
|
|
let mockAccessToken = Credentials(type: .oauthAccessToken, username: "Test", secret: UUID().uuidString)
|
|
let mockRefreshToken = Credentials(type: .oauthRefreshToken, username: "Test", secret: UUID().uuidString)
|
|
let grant = OAuthAuthorizationGrant(accessToken: mockAccessToken, refreshToken: mockRefreshToken)
|
|
service.mockResult = .success(grant)
|
|
|
|
let client = support.makeMockOAuthClient()
|
|
service.parameterTester = { serviceRefreshToken, serviceClient in
|
|
if let accountRefreshToken = try! self.account.retrieveCredentials(type: .oauthRefreshToken) {
|
|
XCTAssertEqual(serviceRefreshToken, accountRefreshToken.secret)
|
|
} else {
|
|
XCTFail("Could not verify correct refresh token used.")
|
|
}
|
|
XCTAssertEqual(serviceClient, client)
|
|
}
|
|
|
|
let refresh = FeedlyRefreshAccessTokenOperation(account: account, service: service, oauthClient: client, log: support.log)
|
|
|
|
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
|
|
let completionExpectation = expectation(description: "Did Finish")
|
|
refresh.completionBlock = { _ in
|
|
completionExpectation.fulfill()
|
|
}
|
|
|
|
MainThreadOperationQueue.shared.add(refresh)
|
|
|
|
waitForExpectations(timeout: 1)
|
|
|
|
do {
|
|
let accessToken = try account.retrieveCredentials(type: .oauthAccessToken)
|
|
XCTAssertEqual(accessToken, mockAccessToken)
|
|
|
|
let refreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
|
|
XCTAssertEqual(refreshToken, mockRefreshToken)
|
|
} catch {
|
|
XCTFail("Could not verify refresh and access tokens because \(error).")
|
|
}
|
|
}
|
|
|
|
func testRefreshTokenFailure() {
|
|
let accessTokenBefore: Credentials
|
|
let refreshTokenBefore: Credentials
|
|
|
|
do {
|
|
guard let accessToken = try account.retrieveCredentials(type: .oauthAccessToken),
|
|
let refreshToken = try account.retrieveCredentials(type: .oauthRefreshToken) else {
|
|
XCTFail("Initial refresh and/or access token does not exist.")
|
|
return
|
|
}
|
|
accessTokenBefore = accessToken
|
|
refreshTokenBefore = refreshToken
|
|
} catch {
|
|
XCTFail("Caught error getting initial refresh and access tokens because \(error).")
|
|
return
|
|
}
|
|
|
|
let service = TestRefreshTokenService()
|
|
service.refreshAccessTokenExpectation = expectation(description: "Did Call Refresh")
|
|
service.mockResult = .failure(URLError(.timedOut))
|
|
|
|
let client = support.makeMockOAuthClient()
|
|
service.parameterTester = { serviceRefreshToken, serviceClient in
|
|
if let accountRefreshToken = try! self.account.retrieveCredentials(type: .oauthRefreshToken) {
|
|
XCTAssertEqual(serviceRefreshToken, accountRefreshToken.secret)
|
|
} else {
|
|
XCTFail("Could not verify correct refresh token used.")
|
|
}
|
|
XCTAssertEqual(serviceClient, client)
|
|
}
|
|
|
|
let refresh = FeedlyRefreshAccessTokenOperation(account: account, service: service, oauthClient: client, log: support.log)
|
|
|
|
// If this expectation is not fulfilled, the operation is not calling `didFinish`.
|
|
let completionExpectation = expectation(description: "Did Finish")
|
|
refresh.completionBlock = { _ in
|
|
completionExpectation.fulfill()
|
|
}
|
|
|
|
MainThreadOperationQueue.shared.add(refresh)
|
|
|
|
waitForExpectations(timeout: 1)
|
|
|
|
do {
|
|
let accessToken = try account.retrieveCredentials(type: .oauthAccessToken)
|
|
XCTAssertEqual(accessToken, accessTokenBefore)
|
|
|
|
let refreshToken = try account.retrieveCredentials(type: .oauthRefreshToken)
|
|
XCTAssertEqual(refreshToken, refreshTokenBefore)
|
|
} catch {
|
|
XCTFail("Could not verify refresh and access tokens because \(error).")
|
|
}
|
|
}
|
|
}
|