Created
March 20, 2023 16:00
-
-
Save spicyjack/75fefd4c551607f62ae4ddd4dfdc9f6c to your computer and use it in GitHub Desktop.
Revisions
-
spicyjack created this gist
Mar 20, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,161 @@ // // BitlyURLShortenerTests.swift // // Test URL shortening code // // import XCTVapor @testable import App class BitlyURLShortenerTests: XCTestCase { private var testBitly: BitlyURLShortener! private var spyURLSession: SpyURLSession! override func setUpWithError() throws { guard let bitlyAPIToken = Environment.get("BITLY_API_TOKEN") else { throw ConfigError.missingBitlyAPIToken(msg: "ERROR: missing bit.ly API token in environment") } guard let bitlyGroupGUID = Environment.get("BITLY_GROUP_GUID") else { throw ConfigError.missingBitlyGroupGUID(msg: "ERROR: missing bit.ly group GUID in environment") } spyURLSession = SpyURLSession() testBitly = BitlyURLShortener(session: spyURLSession, token: bitlyAPIToken, groupGUID: bitlyGroupGUID) } func test_shorten_successfulBitlyResponse() throws { let testURL = URL(string: "http://example.com/foo")! var testBitlyResult: BitlyShortenResponse! var testHTTPStatus: HTTPURLResponse! // var testHTTPError: Error! let expectation = XCTestExpectation(description: "URLShorten response") testBitly.shorten(testURL, backhalf: nil, completion: { result in switch result { case .success((let resultData, let resultHTTPStatus)): let decoder = JSONDecoder() testHTTPStatus = resultHTTPStatus testBitlyResult = try? decoder.decode(BitlyShortenResponse.self, from: resultData) // dump("Result data: \(String(describing: testBitlyResult))") // dump("Result HTTP status: \(resultHTTPStatus)") case .failure(let resultError): // testHTTPError = resultError XCTFail("Failed to shorten URL: \(resultError)") } expectation.fulfill() }) let fakeRequest = fakeShortenURLRequest(testURL) spyURLSession.dataTaskArgsCompletionHandler.first?( fakeShortenURLResponse(fakeRequest), successHTTPURLResponse(to: fakeRequest), nil ) wait(for: [expectation], timeout: 0.1) XCTAssertEqual(1, spyURLSession.dataTaskCallCount) XCTAssertTrue(testBitlyResult.link.starts(with: "https://bit.ly")) XCTAssertTrue(testBitlyResult.id.starts(with: "bit.ly")) XCTAssertEqual(testURL.absoluteString, testBitlyResult.long_url) XCTAssertEqual(200, testHTTPStatus.statusCode) } func test_shorten_successfulBitlyAsyncResponse() async throws { let testURL = URL(string: "http://example.com/foo")! var testBitlyResult: BitlyShortenResponse! var testHTTPStatus: HTTPURLResponse! // var testHTTPError: Error! let fakeRequest = fakeShortenURLRequest(testURL) spyURLSession.asyncDataResponse = fakeShortenURLResponse(fakeRequest) spyURLSession.asyncURLResponse = successHTTPURLResponse(to: fakeRequest) let result = try await testBitly.shorten(testURL, backhalf: nil) switch result { case .success((let resultData, let resultHTTPStatus)): let decoder = JSONDecoder() testHTTPStatus = resultHTTPStatus testBitlyResult = try? decoder.decode(BitlyShortenResponse.self, from: resultData) case .failure(let resultError): XCTFail("Failed to shorten URL: \(resultError)") } XCTAssertEqual(1, spyURLSession.dataTaskCallCount) XCTAssertTrue(testBitlyResult.link.starts(with: "https://bit.ly")) XCTAssertTrue(testBitlyResult.id.starts(with: "bit.ly")) XCTAssertEqual(testURL.absoluteString, testBitlyResult.long_url) XCTAssertEqual(200, testHTTPStatus.statusCode) } // MARK: - Testing Helpers private func generateISO8601Date(date: Date = Date()) -> String { let formatter = ISO8601DateFormatter() return formatter.string(from: date) } private func generateCurrentUNIXEpoch(date: Date = Date()) -> String { return "\(Int(Date().timeIntervalSince1970))" } private func generateFakeBitlyID() -> String { // use a common log date value let dateStr = generateCurrentUNIXEpoch() return "bit.ly/\(dateStr)" } func successHTTPURLResponse(to request: URLRequest) -> HTTPURLResponse { return HTTPURLResponse(url: request.url ?? anyURL(), statusCode: 200, httpVersion: "HTTP/1.1", headerFields: [:])! } func anyURL() -> URL { return URL(string: "http://example.com")! } private func fakeShortenURLRequest(_ url: URL) -> URLRequest { let bitlyURL = URL(string: "https://api-ssl.bitly.com/v4/shorten")! var request = URLRequest(url: bitlyURL, cachePolicy: .reloadIgnoringLocalCacheData) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("Bearer fakebitlyapitken", forHTTPHeaderField: "Authorization") let encoder = JSONEncoder() let reqData = BitlyShortenRequest(long_url: url.absoluteString, domain: "bit.ly", group_guid: "fakebitlyguid") let encodedJSON = try? encoder.encode(reqData) request.httpBody = encodedJSON return request } private func fakeShortenURLResponse(_ request: URLRequest) -> Data { let decoder = JSONDecoder() let bitlyRequest = try? decoder.decode(BitlyShortenRequest.self, from: request.httpBody!) let encoder = JSONEncoder() let fakeBitlyID = generateFakeBitlyID() let reqData = BitlyShortenResponse( created_at: generateISO8601Date(), id: fakeBitlyID, link: "https://" + fakeBitlyID, long_url: bitlyRequest?.long_url ?? "", archived: false, custom_bitlinks: [], tags: [], deeplinks: [] ) let encodedJSON = try? encoder.encode(reqData) return encodedJSON ?? Data("{}".utf8) } }