| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- //
- // AuthenticationInterceptorTests.swift
- //
- // Copyright (c) 2020 Alamofire Software Foundation (http://alamofire.org/)
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- @testable import Alamofire
- import Foundation
- import XCTest
- final class AuthenticationInterceptorTestCase: BaseTestCase {
- // MARK: - Helper Types
- struct TestCredential: AuthenticationCredential {
- let accessToken: String
- let refreshToken: String
- let userID: String
- let expiration: Date
- let requiresRefresh: Bool
- init(accessToken: String = "a0",
- refreshToken: String = "r0",
- userID: String = "u0",
- expiration: Date = Date(),
- requiresRefresh: Bool = false) {
- self.accessToken = accessToken
- self.refreshToken = refreshToken
- self.userID = userID
- self.expiration = expiration
- self.requiresRefresh = requiresRefresh
- }
- }
- enum TestAuthError: Error {
- case refreshNetworkFailure
- }
- final class TestAuthenticator: Authenticator {
- private(set) var applyCount = 0
- private(set) var refreshCount = 0
- private(set) var didRequestFailDueToAuthErrorCount = 0
- private(set) var isRequestAuthenticatedWithCredentialCount = 0
- let shouldRefreshAsynchronously: Bool
- let refreshResult: Result<TestCredential, Error>?
- let lock = NSLock()
- init(shouldRefreshAsynchronously: Bool = true, refreshResult: Result<TestCredential, Error>? = nil) {
- self.shouldRefreshAsynchronously = shouldRefreshAsynchronously
- self.refreshResult = refreshResult
- }
- func apply(_ credential: TestCredential, to urlRequest: inout URLRequest) {
- lock.lock(); defer { lock.unlock() }
- applyCount += 1
- urlRequest.headers.add(.authorization(credential.accessToken))
- }
- func refresh(_ credential: TestCredential,
- for session: Session,
- completion: @escaping (Result<TestCredential, Error>) -> Void) {
- lock.lock()
- refreshCount += 1
- let result = refreshResult ?? .success(
- TestCredential(accessToken: "a\(refreshCount)",
- refreshToken: "a\(refreshCount)",
- userID: "u1",
- expiration: Date())
- )
- if shouldRefreshAsynchronously {
- // The 10 ms delay here is important to allow multiple requests to queue up while refreshing.
- DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + 0.01) { completion(result) }
- lock.unlock()
- } else {
- lock.unlock()
- completion(result)
- }
- }
- func didRequest(_ urlRequest: URLRequest,
- with response: HTTPURLResponse,
- failDueToAuthenticationError error: Error)
- -> Bool {
- lock.lock(); defer { lock.unlock() }
- didRequestFailDueToAuthErrorCount += 1
- return response.statusCode == 401
- }
- func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: TestCredential) -> Bool {
- lock.lock(); defer { lock.unlock() }
- isRequestAuthenticatedWithCredentialCount += 1
- return urlRequest.headers["Authorization"] == credential.accessToken
- }
- }
- final class PathAdapter: RequestAdapter {
- var paths: [String]
- init(paths: [String]) {
- self.paths = paths
- }
- func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
- var request = urlRequest
- var urlComponents = URLComponents(url: request.url!, resolvingAgainstBaseURL: false)!
- urlComponents.path = paths.removeFirst()
- request.url = urlComponents.url
- completion(.success(request))
- }
- }
- // MARK: - Tests - Adapt
- func testThatInterceptorCanAdaptURLRequest() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a0")
- XCTAssertEqual(response?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorQueuesAdaptOperationWhenRefreshing() {
- // Given
- let credential = TestCredential(requiresRefresh: true)
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = Session()
- let expect = expectation(description: "both requests should complete")
- expect.expectedFulfillmentCount = 2
- var response1: AFDataResponse<Data?>?
- var response2: AFDataResponse<Data?>?
- // When
- let request1 = session.request(.status(200), interceptor: interceptor).validate().response {
- response1 = $0
- expect.fulfill()
- }
- let request2 = session.request(.status(202), interceptor: interceptor).validate().response {
- response2 = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response1?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response2?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response1?.result.isSuccess, true)
- XCTAssertEqual(response2?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 2)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request1.retryCount, 0)
- XCTAssertEqual(request2.retryCount, 0)
- }
- func testThatInterceptorThrowsMissingCredentialErrorWhenCredentialIsNil() {
- // Given
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers.count, 0)
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isRequestAdaptationError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.underlyingError as? AuthenticationError, .missingCredential)
- XCTAssertEqual(authenticator.applyCount, 0)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorRethrowsRefreshErrorFromAdapt() {
- // Given
- let credential = TestCredential(requiresRefresh: true)
- let authenticator = TestAuthenticator(refreshResult: .failure(TestAuthError.refreshNetworkFailure))
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers.count, 0)
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isRequestAdaptationError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.underlyingError as? TestAuthError, .refreshNetworkFailure)
- if case let .requestRetryFailed(_, originalError) = response?.result.failure {
- XCTAssertEqual(originalError.asAFError?.isResponseValidationError, true)
- XCTAssertEqual(originalError.asAFError?.responseCode, 401)
- }
- XCTAssertEqual(authenticator.applyCount, 0)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- // MARK: - Tests - Retry
- // If we not using swift-corelibs-foundation where URLRequest to /invalid/path is a fatal error.
- #if !canImport(FoundationNetworking)
- func testThatInterceptorDoesNotRetryWithoutResponse() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let urlRequest = URLRequest(url: URL(string: "/invalid/path")!)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(urlRequest, interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a0")
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isSessionTaskError, true)
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- #endif
- func testThatInterceptorDoesNotRetryWhenRequestDoesNotFailDueToAuthError() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.status(500), interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a0")
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isResponseValidationError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.responseCode, 500)
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 1)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorThrowsMissingCredentialErrorWhenCredentialIsNilAndRequestShouldBeRetried() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = stored(Session())
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.status(401), interceptor: interceptor)
- .validate {
- interceptor.credential = nil
- }
- .response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a0")
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isRequestRetryError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.underlyingError as? AuthenticationError, .missingCredential)
- if case let .requestRetryFailed(_, originalError) = response?.result.failure {
- XCTAssertEqual(originalError.asAFError?.isResponseValidationError, true)
- XCTAssertEqual(originalError.asAFError?.responseCode, 401)
- }
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 1)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorRetriesRequestThatFailedWithOutdatedCredential() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = stored(Session())
- let pathAdapter = PathAdapter(paths: ["/status/401", "/status/200"])
- let compositeInterceptor = Interceptor(adapters: [pathAdapter, interceptor], retriers: [interceptor])
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: compositeInterceptor)
- .validate {
- interceptor.credential = TestCredential(accessToken: "a1",
- refreshToken: "r1",
- userID: "u0",
- expiration: Date(),
- requiresRefresh: false)
- }
- .response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 2)
- XCTAssertEqual(authenticator.refreshCount, 0)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 1)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 1)
- XCTAssertEqual(request.retryCount, 1)
- }
- // Produces double lock reported in https://github.com/Alamofire/Alamofire/issues/3294#issuecomment-703241558
- func testThatInterceptorDoesNotDeadlockWhenAuthenticatorCallsRefreshCompletionSynchronouslyOnCallingQueue() {
- // Given
- let credential = TestCredential(requiresRefresh: true)
- let authenticator = TestAuthenticator(shouldRefreshAsynchronously: false)
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let eventMonitor = ClosureEventMonitor()
- eventMonitor.requestDidCreateTask = { _, _ in
- interceptor.credential = TestCredential(accessToken: "a1",
- refreshToken: "r1",
- userID: "u0",
- expiration: Date(),
- requiresRefresh: false)
- }
- let session = Session(eventMonitors: [eventMonitor])
- let pathAdapter = PathAdapter(paths: ["/status/200"])
- let compositeInterceptor = Interceptor(adapters: [pathAdapter, interceptor], retriers: [interceptor])
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: compositeInterceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 0)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 0)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorRetriesRequestAfterRefresh() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let pathAdapter = PathAdapter(paths: ["/status/401", "/status/200"])
- let compositeInterceptor = Interceptor(adapters: [pathAdapter, interceptor], retriers: [interceptor])
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: compositeInterceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 2)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 1)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 1)
- XCTAssertEqual(request.retryCount, 1)
- }
- func testThatInterceptorRethrowsRefreshErrorFromRetry() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator(refreshResult: .failure(TestAuthError.refreshNetworkFailure))
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.status(401), interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a0")
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isRequestRetryError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.underlyingError as? TestAuthError, .refreshNetworkFailure)
- if case let .requestRetryFailed(_, originalError) = response?.result.failure {
- XCTAssertEqual(originalError.asAFError?.isResponseValidationError, true)
- XCTAssertEqual(originalError.asAFError?.responseCode, 401)
- }
- XCTAssertEqual(authenticator.applyCount, 1)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 1)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 1)
- XCTAssertEqual(request.retryCount, 0)
- }
- func testThatInterceptorTriggersRefreshWithMultipleParallelRequestsReturning401Responses() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let requestCount = 6
- let session = stored(Session())
- let expect = expectation(description: "both requests should complete")
- expect.expectedFulfillmentCount = requestCount
- var requests: [Int: Request] = [:]
- var responses: [Int: AFDataResponse<Data?>] = [:]
- for index in 0..<requestCount {
- let pathAdapter = PathAdapter(paths: ["/status/401", "/status/20\(index)"])
- let compositeInterceptor = Interceptor(adapters: [pathAdapter, interceptor], retriers: [interceptor])
- // When
- let request = session.request(.default, interceptor: compositeInterceptor).validate().response {
- responses[index] = $0
- expect.fulfill()
- }
- requests[index] = request
- }
- waitForExpectations(timeout: timeout)
- // Then
- for index in 0..<requestCount {
- let response = responses[index]
- XCTAssertEqual(response?.request?.headers["Authorization"], "a1")
- XCTAssertEqual(response?.result.isSuccess, true)
- let request = requests[index]
- XCTAssertEqual(request?.retryCount, 1)
- }
- XCTAssertEqual(authenticator.applyCount, 12)
- XCTAssertEqual(authenticator.refreshCount, 1)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 6)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 6)
- }
- // MARK: - Tests - Excessive Refresh
- func testThatInterceptorIgnoresExcessiveRefreshWhenRefreshWindowIsNil() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator, credential: credential)
- let pathAdapter = PathAdapter(paths: ["/status/401",
- "/status/401",
- "/status/401",
- "/status/401",
- "/status/401",
- "/status/200"])
- let compositeInterceptor = Interceptor(adapters: [pathAdapter, interceptor], retriers: [interceptor])
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.default, interceptor: compositeInterceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a5")
- XCTAssertEqual(response?.result.isSuccess, true)
- XCTAssertEqual(authenticator.applyCount, 6)
- XCTAssertEqual(authenticator.refreshCount, 5)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 5)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 5)
- XCTAssertEqual(request.retryCount, 5)
- }
- func testThatInterceptorThrowsExcessiveRefreshErrorWhenExcessiveRefreshOccurs() {
- // Given
- let credential = TestCredential()
- let authenticator = TestAuthenticator()
- let interceptor = AuthenticationInterceptor(authenticator: authenticator,
- credential: credential,
- refreshWindow: .init(interval: 30, maximumAttempts: 2))
- let session = Session()
- let expect = expectation(description: "request should complete")
- var response: AFDataResponse<Data?>?
- // When
- let request = session.request(.status(401), interceptor: interceptor).validate().response {
- response = $0
- expect.fulfill()
- }
- waitForExpectations(timeout: timeout)
- // Then
- XCTAssertEqual(response?.request?.headers["Authorization"], "a2")
- XCTAssertEqual(response?.result.isFailure, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.isRequestRetryError, true)
- XCTAssertEqual(response?.result.failure?.asAFError?.underlyingError as? AuthenticationError, .excessiveRefresh)
- if case let .requestRetryFailed(_, originalError) = response?.result.failure {
- XCTAssertEqual(originalError.asAFError?.isResponseValidationError, true)
- XCTAssertEqual(originalError.asAFError?.responseCode, 401)
- }
- XCTAssertEqual(authenticator.applyCount, 3)
- XCTAssertEqual(authenticator.refreshCount, 2)
- XCTAssertEqual(authenticator.didRequestFailDueToAuthErrorCount, 3)
- XCTAssertEqual(authenticator.isRequestAuthenticatedWithCredentialCount, 3)
- XCTAssertEqual(request.retryCount, 2)
- }
- }
|