diff --git a/Package.swift b/Package.swift index 8cd7ad2c0..ea9f9f5b3 100644 --- a/Package.swift +++ b/Package.swift @@ -120,10 +120,6 @@ var package = Package( .target(name: "TestCommon") ]), -// .target(name: "AuthFoundationTestCommon", -// dependencies: ["AuthFoundation"], -// path: "Tests/AuthFoundationTestCommon"), - // Abstract API Client .target(name: "APIClient", dependencies: [ diff --git a/Sources/JWT/JSONPayload.swift b/Sources/JWT/JSONPayload.swift deleted file mode 100644 index 078d20fcf..000000000 --- a/Sources/JWT/JSONPayload.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2024-Present, Okta, Inc. and/or its affiliates. All rights reserved. -// The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (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 - -//extension Dictionary { -// public static func decodeJSONObject(from decoder: Decoder) throws -> [String: any Sendable] { -// let container = try decoder.container(keyedBy: JSONCodingKeys.self) -// return try container.decode([String: Any].self) -// } -// -// public static func encodeJSONObject(_ object: [String: any Sendable], to encoder: Encoder) throws { -// var container = encoder.container(keyedBy: JSONCodingKeys.self) -// try object -// .compactMap { (key: String, value: Any) in -// guard let key = JSONCodingKeys(stringValue: key) else { return nil } -// return (key, value) -// } -// .forEach { (key: JSONCodingKeys, value: Any) in -// if let value = value as? Bool { -// try container.encode(value, forKey: key) -// } else if let value = value as? String { -// try container.encode(value, forKey: key) -// } else if let value = value as? Int { -// try container.encode(value, forKey: key) -// } else if let value = value as? Double { -// try container.encode(value, forKey: key) -// } else if let value = value as? [String: String] { -// try container.encode(value, forKey: key) -// } -// } -// } -// -//} diff --git a/Tests/APIClientTestCommon/MockApiRequest.swift b/Tests/APIClientTestCommon/MockApiRequest.swift index 9ef89dc01..39b44d0f4 100644 --- a/Tests/APIClientTestCommon/MockApiRequest.swift +++ b/Tests/APIClientTestCommon/MockApiRequest.swift @@ -17,16 +17,20 @@ import Foundation import FoundationNetworking #endif -//struct MockApiRequest: APIRequest { -// var url: URL -// var cachePolicy: URLRequest.CachePolicy -// -// typealias ResponseType = Token -// -// init(url: URL, -// cachePolicy: URLRequest.CachePolicy = .reloadIgnoringLocalAndRemoteCacheData) -// { -// self.url = url -// self.cachePolicy = cachePolicy -// } -//} +struct SampleResponse: Decodable, Sendable { + let message: String +} + +struct MockApiRequest: APIRequest { + typealias ResponseType = ResponseType + + var url: URL + var cachePolicy: URLRequest.CachePolicy + + init(url: URL, + cachePolicy: URLRequest.CachePolicy = .reloadIgnoringLocalAndRemoteCacheData) + { + self.url = url + self.cachePolicy = cachePolicy + } +} diff --git a/Tests/APIClientTests/APIClientTests.swift b/Tests/APIClientTests/APIClientTests.swift index bbce8794e..deb146de2 100644 --- a/Tests/APIClientTests/APIClientTests.swift +++ b/Tests/APIClientTests/APIClientTests.swift @@ -29,55 +29,59 @@ struct MockApiParsingContext: APIParsingContext { } } -//class APIClientTests: XCTestCase { -// var client: MockApiClient! -// let baseUrl = URL(string: "https://example.okta.com/oauth2/v1/token")! -// var configuration: MockAPIClientConfiguration! -// let urlSession = URLSessionMock() -// let requestId = UUID().uuidString -// -// override func setUpWithError() throws { -// configuration = MockAPIClientConfiguration(baseURL: baseUrl, -// clientId: "clientid", -// scopes: "openid") -// client = MockApiClient(configuration: configuration, -// session: urlSession, -// baseURL: baseUrl) -// } -// -// @MainActor -// func testOverrideRequestResult() throws { -// client = MockApiClient(configuration: configuration, -// session: urlSession, -// baseURL: baseUrl, -// shouldRetry: .doNotRetry) -// -// urlSession.expect("https://example.okta.com/oauth2/v1/token", -// data: try data(filename: "token", -// matching: "APIClientTestCommon"), -// statusCode: 400, -// contentType: "application/json", -// headerFields: ["x-rate-limit-limit": "0", -// "x-rate-limit-remaining": "0", -// "x-rate-limit-reset": "1609459200", -// "Date": "Fri, 09 Sep 2022 02:22:14 GMT", -// "x-okta-request-id": requestId]) -// -// let apiRequest = MockApiRequest(url: baseUrl) -// let context = MockApiParsingContext(result: .success) -// -// let expect = expectation(description: "network request") -// apiRequest.send(to: client, parsing: context, completion: { result in -// switch result { -// case .success(let response): -// XCTAssertEqual(response.statusCode, 400) -// case .failure(_): -// XCTFail("Did not expect the request to fail") -// } -// expect.fulfill() -// }) -// waitForExpectations(timeout: 9.0) { error in -// XCTAssertNil(error) -// } -// } -//} +class APIClientTests: XCTestCase { + var client: MockApiClient! + let baseUrl = URL(string: "https://example.okta.com/oauth2/v1/token")! + var configuration: MockAPIClientConfiguration! + let urlSession = URLSessionMock() + let requestId = UUID().uuidString + + static override func setUp() { + registerMock(bundles: .apiClientTestCommon) + } + + override func setUpWithError() throws { + configuration = MockAPIClientConfiguration(baseURL: baseUrl, + clientId: "clientid", + scopes: "openid") + client = MockApiClient(configuration: configuration, + session: urlSession, + baseURL: baseUrl) + } + + func testOverrideRequestResult() throws { + client = MockApiClient(configuration: configuration, + session: urlSession, + baseURL: baseUrl, + shouldRetry: .doNotRetry) + + urlSession.expect("https://example.okta.com/oauth2/v1/token", + data: data(for: """ + { "message": "Hello world!" } + """), + statusCode: 400, + contentType: "application/json", + headerFields: ["x-rate-limit-limit": "0", + "x-rate-limit-remaining": "0", + "x-rate-limit-reset": "1609459200", + "Date": "Fri, 09 Sep 2022 02:22:14 GMT", + "x-okta-request-id": requestId]) + + let apiRequest: MockApiRequest = MockApiRequest(url: baseUrl) + let context = MockApiParsingContext(result: .success) + + let expect = expectation(description: "network request") + apiRequest.send(to: client, parsing: context, completion: { result in + switch result { + case .success(let response): + XCTAssertEqual(response.statusCode, 400) + case .failure(_): + XCTFail("Did not expect the request to fail") + } + expect.fulfill() + }) + waitForExpectations(timeout: 9.0) { error in + XCTAssertNil(error) + } + } +} diff --git a/Tests/APIClientTests/APIRetryTests.swift b/Tests/APIClientTests/APIRetryTests.swift index 0c2d30b21..55c18ecaa 100644 --- a/Tests/APIClientTests/APIRetryTests.swift +++ b/Tests/APIClientTests/APIRetryTests.swift @@ -45,163 +45,151 @@ final class MockAPIClientConfiguration: APIClientConfiguration, Sendable { } } -//class APIRetryTests: XCTestCase { -// var client: MockApiClient! -// let baseUrl = URL(string: "https://example.okta.com/oauth2/v1/token")! -// var configuration: MockAPIClientConfiguration! -// let urlSession = URLSessionMock() -// var apiRequest: MockApiRequest! -// let requestId = UUID().uuidString -// -// override func setUpWithError() throws { -// configuration = MockAPIClientConfiguration(baseURL: baseUrl, -// clientId: "clientid", -// scopes: "openid") -// client = MockApiClient(configuration: configuration, -// session: urlSession, -// baseURL: baseUrl) -// apiRequest = MockApiRequest(url: baseUrl) -// } -// -// @MainActor -// func testShouldNotRetry() throws { -// client = MockApiClient(configuration: configuration, -// session: urlSession, -// baseURL: baseUrl, -// shouldRetry: .doNotRetry) -// try performRetryRequest(count: 1) -// XCTAssertNil(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"]) -// } -// -// @MainActor -// func testDefaultRetryCount() throws { -// try performRetryRequest(count: 4) -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "3") -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) -// } -// -// @MainActor -// func testApiRetryReturnsSuccessStatusCode() throws { -// try performRetryRequest(count: 1, isSuccess: true) -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "1") -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) -// } -// -// @MainActor -// func testApiRetryUsingMaximumRetryAttempt() throws { -// try performRetryRequest(count: 3, isSuccess: true) -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "3") -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) -// } -// -// @MainActor -// func testCustomRetryCount() throws { -// client = MockApiClient(configuration: configuration, -// session: urlSession, -// baseURL: baseUrl, -// shouldRetry: .retry(maximumCount: 5)) -// try performRetryRequest(count: 6) -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "5") -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) -// } -// -// @MainActor -// func testRetryDelegateDoNotRetry() throws { -// let delegate = APIRetryDelegateRecorder() -// delegate.response = .doNotRetry -// client.delegate = delegate -// -// try performRetryRequest(count: 1, isSuccess: false) -// XCTAssertEqual(delegate.requests.count, 1) -// XCTAssertNil(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"]) -// } -// -// @MainActor -// func testRetryDelegateRetry() throws { -// let delegate = APIRetryDelegateRecorder() -// delegate.response = .retry(maximumCount: 5) -// client.delegate = delegate -// -// try performRetryRequest(count: 5, isSuccess: true) -// XCTAssertEqual(delegate.requests.count, 1) -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "5") -// XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) -// } -// -// func testApiRateLimit() throws { -// let date = "Fri, 09 Sep 2022 02:22:14 GMT" -// let rateLimit = APIRateLimit(with: ["x-rate-limit-limit": "0", -// "x-rate-limit-remaining": "0", -// "x-rate-limit-reset": "1662690193", -// "Date": date]) -// XCTAssertNotNil(rateLimit?.delay) -// XCTAssertEqual(rateLimit?.delay, 59.0) -// } -// -// @MainActor -// func testMissingResetHeader() throws { -// urlSession.expect("https://example.okta.com/oauth2/v1/token", -// data: try data(filename: "token", -// matching: "APIClientTestCommon"), -// statusCode: 429, -// contentType: "application/json", -// headerFields: ["x-rate-limit-limit": "0", -// "x-rate-limit-remaining": "0", -// "x-okta-request-id": requestId]) -// -// let expect = expectation(description: "network request") -// apiRequest.send(to: client, completion: { result in -// guard case .failure(let error) = result else { -// XCTFail() -// return -// } -// XCTAssertEqual(error.localizedDescription, APIClientError.statusCode(429).localizedDescription) -// expect.fulfill() -// }) -// -// waitForExpectations(timeout: 1.0) { error in -// XCTAssertNil(error) -// } -// } -// -// @MainActor -// func performRetryRequest(count: Int, isSuccess: Bool = false) throws { -// let date = "Fri, 09 Sep 2022 02:22:14 GMT" -// for _ in 0..! + let requestId = UUID().uuidString + + override func setUpWithError() throws { + configuration = MockAPIClientConfiguration(baseURL: baseUrl, + clientId: "clientid", + scopes: "openid") + client = MockApiClient(configuration: configuration, + session: urlSession, + baseURL: baseUrl) + apiRequest = MockApiRequest(url: baseUrl) + } + + func testShouldNotRetry() throws { + client = MockApiClient(configuration: configuration, + session: urlSession, + baseURL: baseUrl, + shouldRetry: .doNotRetry) + try performRetryRequest(count: 1) + XCTAssertNil(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"]) + } + + func testDefaultRetryCount() throws { + try performRetryRequest(count: 4) + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "3") + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) + } + + func testApiRetryReturnsSuccessStatusCode() throws { + try performRetryRequest(count: 1, isSuccess: true) + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "1") + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) + } + + func testApiRetryUsingMaximumRetryAttempt() throws { + try performRetryRequest(count: 3, isSuccess: true) + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "3") + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) + } + + func testCustomRetryCount() throws { + client = MockApiClient(configuration: configuration, + session: urlSession, + baseURL: baseUrl, + shouldRetry: .retry(maximumCount: 5)) + try performRetryRequest(count: 6) + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "5") + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) + } + + func testRetryDelegateDoNotRetry() throws { + let delegate = APIRetryDelegateRecorder() + delegate.response = .doNotRetry + client.delegate = delegate + + try performRetryRequest(count: 1, isSuccess: false) + XCTAssertEqual(delegate.requests.count, 1) + XCTAssertNil(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"]) + } + + func testRetryDelegateRetry() throws { + let delegate = APIRetryDelegateRecorder() + delegate.response = .retry(maximumCount: 5) + client.delegate = delegate + + try performRetryRequest(count: 5, isSuccess: true) + XCTAssertEqual(delegate.requests.count, 1) + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-Count"], "5") + XCTAssertEqual(client.request?.allHTTPHeaderFields?["X-Okta-Retry-For"], requestId) + } + + func testApiRateLimit() throws { + let date = "Fri, 09 Sep 2022 02:22:14 GMT" + let rateLimit = APIRateLimit(with: ["x-rate-limit-limit": "0", + "x-rate-limit-remaining": "0", + "x-rate-limit-reset": "1662690193", + "Date": date]) + XCTAssertNotNil(rateLimit?.delay) + XCTAssertEqual(rateLimit?.delay, 59.0) + } + + func testMissingResetHeader() throws { + urlSession.expect("https://example.okta.com/oauth2/v1/token", + data: data(for: "{\"message\": \"Hello World\"}"), + statusCode: 429, + contentType: "application/json", + headerFields: ["x-rate-limit-limit": "0", + "x-rate-limit-remaining": "0", + "x-okta-request-id": requestId]) + + let expect = expectation(description: "network request") + apiRequest.send(to: client, completion: { result in + guard case .failure(let error) = result else { + XCTFail() + return + } + XCTAssertEqual(error.localizedDescription, APIClientError.statusCode(429).localizedDescription) + expect.fulfill() + }) + + waitForExpectations(timeout: 1.0) { error in + XCTAssertNil(error) + } + } + + func performRetryRequest(count: Int, isSuccess: Bool = false) throws { + let date = "Fri, 09 Sep 2022 02:22:14 GMT" + for _ in 0..