Skip to content

Commit

Permalink
Add support fake header fields (#272)
Browse files Browse the repository at this point in the history
* Initial support for fake header fields

* Add header fields

* Add missing tests
  • Loading branch information
3lvis authored Jul 11, 2023
1 parent e3cb8d9 commit 9cb2445
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 25 deletions.
3 changes: 2 additions & 1 deletion Sources/Networking/FakeRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
struct FakeRequest {
let response: Any?
let responseType: Networking.ResponseType
let headerFields: [String: String]?
let statusCode: Int

static func find(ofType type: Networking.RequestType, forPath path: String, in collection: [Networking.RequestType: [String: FakeRequest]]) throws -> FakeRequest? {
Expand Down Expand Up @@ -45,7 +46,7 @@ struct FakeRequest {
}
if let stringData = responseString.data(using: .utf8) {
let finalJSON = try JSONSerialization.jsonObject(with: stringData, options: [])
return FakeRequest(response: finalJSON, responseType: fakeRequest.responseType, statusCode: fakeRequest.statusCode)
return FakeRequest(response: finalJSON, responseType: fakeRequest.responseType, headerFields: fakeRequest.headerFields, statusCode: fakeRequest.statusCode)
} else {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Networking/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ extension URL {
}

extension HTTPURLResponse {
convenience init(url: URL, statusCode: Int) {
self.init(url: url, statusCode: statusCode, httpVersion: nil, headerFields: nil)!
convenience init(url: URL, headerFields: [String : String]? = nil, statusCode: Int) {
self.init(url: url, statusCode: statusCode, httpVersion: nil, headerFields: headerFields)!
}
}

Expand Down
24 changes: 12 additions & 12 deletions Sources/Networking/Networking+HTTPRequests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public extension Networking {
/// - path: The path for the faked GET request.
/// - response: An `Any` that will be returned when a GET request is made to the specified path.
/// - statusCode: By default it's 200, if you provide any status code that is between 200 and 299 the response object will be returned, otherwise we will return an error containig the provided status code.
func fakeGET(_ path: String, response: Any?, statusCode: Int = 200) {
registerFake(requestType: .get, path: path, response: response, responseType: .json, statusCode: statusCode)
func fakeGET(_ path: String, response: Any?, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .get, path: path, headerFields: headerFields, response: response, responseType: .json, statusCode: statusCode)
}

/// Registers a fake GET request for the specified path using the contents of a file. After registering this, every GET request to the path, will return the contents of the registered file.
Expand Down Expand Up @@ -61,8 +61,8 @@ public extension Networking {
/// - path: The path for the faked PATCH request.
/// - response: An `Any` that will be returned when a PATCH request is made to the specified path.
/// - statusCode: By default it's 200, if you provide any status code that is between 200 and 299 the response object will be returned, otherwise we will return an error containig the provided status code.
func fakePATCH(_ path: String, response: Any?, statusCode: Int = 200) {
registerFake(requestType: .patch, path: path, response: response, responseType: .json, statusCode: statusCode)
func fakePATCH(_ path: String, response: Any?, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .patch, path: path, headerFields: headerFields, response: response, responseType: .json, statusCode: statusCode)
}

/// Registers a fake PATCH request to the specified path using the contents of a file. After registering this, every PATCH request to the path, will return the contents of the registered file.
Expand Down Expand Up @@ -103,8 +103,8 @@ public extension Networking {
/// - path: The path for the faked PUT request.
/// - response: An `Any` that will be returned when a PUT request is made to the specified path.
/// - statusCode: By default it's 200, if you provide any status code that is between 200 and 299 the response object will be returned, otherwise we will return an error containig the provided status code.
func fakePUT(_ path: String, response: Any?, statusCode: Int = 200) {
registerFake(requestType: .put, path: path, response: response, responseType: .json, statusCode: statusCode)
func fakePUT(_ path: String, response: Any?, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .put, path: path, headerFields: headerFields, response: response, responseType: .json, statusCode: statusCode)
}

/// Registers a fake PUT request to the specified path using the contents of a file. After registering this, every PUT request to the path, will return the contents of the registered file.
Expand Down Expand Up @@ -155,8 +155,8 @@ public extension Networking {
/// - path: The path for the faked POST request.
/// - response: An `Any` that will be returned when a POST request is made to the specified path.
/// - statusCode: By default it's 200, if you provide any status code that is between 200 and 299 the response object will be returned, otherwise we will return an error containig the provided status code.
func fakePOST(_ path: String, response: Any?, statusCode: Int = 200) {
registerFake(requestType: .post, path: path, response: response, responseType: .json, statusCode: statusCode)
func fakePOST(_ path: String, response: Any?, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .post, path: path, headerFields: headerFields, response: response, responseType: .json, statusCode: statusCode)
}

/// Registers a fake POST request to the specified path using the contents of a file. After registering this, every POST request to the path, will return the contents of the registered file.
Expand Down Expand Up @@ -197,8 +197,8 @@ public extension Networking {
/// - path: The path for the faked DELETE request.
/// - response: An `Any` that will be returned when a DELETE request is made to the specified path.
/// - statusCode: By default it's 200, if you provide any status code that is between 200 and 299 the response object will be returned, otherwise we will return an error containig the provided status code.
func fakeDELETE(_ path: String, response: Any?, statusCode: Int = 200) {
registerFake(requestType: .delete, path: path, response: response, responseType: .json, statusCode: statusCode)
func fakeDELETE(_ path: String, response: Any?, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .delete, path: path, headerFields: headerFields, response: response, responseType: .json, statusCode: statusCode)
}

/// Registers a fake DELETE request to the specified path using the contents of a file. After registering this, every DELETE request to the path, will return the contents of the registered file.
Expand Down Expand Up @@ -259,8 +259,8 @@ public extension Networking {
/// - path: The path for the faked image download request.
/// - image: An image that will be returned when there's a request to the registered path.
/// - statusCode: The status code to be used when faking the request.
func fakeImageDownload(_ path: String, image: Image?, statusCode: Int = 200) {
registerFake(requestType: .get, path: path, response: image, responseType: .image, statusCode: statusCode)
func fakeImageDownload(_ path: String, image: Image, headerFields: [String: String]? = nil, statusCode: Int = 200) {
registerFake(requestType: .get, path: path, headerFields: headerFields, response: image, responseType: .image, statusCode: statusCode)
}

/// Downloads data from a URL, caching the result.
Expand Down
8 changes: 4 additions & 4 deletions Sources/Networking/Networking+Private.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension Networking {
func registerFake(requestType: RequestType, path: String, fileName: String, bundle: Bundle) {
do {
if let result = try FileManager.json(from: fileName, bundle: bundle) {
registerFake(requestType: requestType, path: path, response: result, responseType: .json, statusCode: 200)
registerFake(requestType: requestType, path: path, headerFields: nil, response: result, responseType: .json, statusCode: 200)
}
} catch ParsingError.notFound {
fatalError("We couldn't find \(fileName), are you sure is there?")
Expand All @@ -52,16 +52,16 @@ extension Networking {
}
}

func registerFake(requestType: RequestType, path: String, response: Any?, responseType: ResponseType, statusCode: Int) {
func registerFake(requestType: RequestType, path: String, headerFields: [String: String]?, response: Any?, responseType: ResponseType, statusCode: Int) {
var requests = fakeRequests[requestType] ?? [String: FakeRequest]()
requests[path] = FakeRequest(response: response, responseType: responseType, statusCode: statusCode)
requests[path] = FakeRequest(response: response, responseType: responseType, headerFields: headerFields, statusCode: statusCode)
fakeRequests[requestType] = requests
}

func handleFakeRequest(_ fakeRequest: FakeRequest, path: String, cacheName: String?, cachingLevel: CachingLevel) throws -> (Any?, HTTPURLResponse, NSError?) {
var error: NSError?
let url = try composedURL(with: path)
let response = HTTPURLResponse(url: url, statusCode: fakeRequest.statusCode)
let response = HTTPURLResponse(url: url, headerFields: fakeRequest.headerFields, statusCode: fakeRequest.statusCode)

if let unauthorizedRequestCallback = unauthorizedRequestCallback, fakeRequest.statusCode == 403 || fakeRequest.statusCode == 401 {
unauthorizedRequestCallback()
Expand Down
106 changes: 100 additions & 6 deletions Tests/NetworkingTests/FakeRequestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class FakeRequestTests: XCTestCase {
}

func testFind() throws {
let request = FakeRequest(response: nil, responseType: .json, statusCode: 200)
let request = FakeRequest(response: nil, responseType: .json, headerFields: nil, statusCode: 200)
let existingRequests = [Networking.RequestType.get: ["/companies": request]]

XCTAssertNil(try FakeRequest.find(ofType: .get, forPath: "/users", in: existingRequests))
Expand All @@ -45,7 +45,7 @@ class FakeRequestTests: XCTestCase {
let json = [
"name": "Name {userID}"
]
let request = FakeRequest(response: json, responseType: .json, statusCode: 200)
let request = FakeRequest(response: json, responseType: .json, headerFields: nil, statusCode: 200)
let existingRequests = [Networking.RequestType.get: ["/users/{userID}": request]]
let result = try FakeRequest.find(ofType: .get, forPath: "/users/10", in: existingRequests)

Expand All @@ -61,7 +61,7 @@ class FakeRequestTests: XCTestCase {
"user": "User {userID}",
"company": "Company {companyID}"
]
let request = FakeRequest(response: json, responseType: .json, statusCode: 200)
let request = FakeRequest(response: json, responseType: .json, headerFields: nil, statusCode: 200)
let existingRequests = [Networking.RequestType.get: ["/users/{userID}/companies/{companyID}": request]]
let result = try FakeRequest.find(ofType: .get, forPath: "/users/10/companies/20", in: existingRequests)

Expand All @@ -79,7 +79,7 @@ class FakeRequestTests: XCTestCase {
"company": "Company {companyID}",
"product": "Product {productID}"
]
let request = FakeRequest(response: json, responseType: .json, statusCode: 200)
let request = FakeRequest(response: json, responseType: .json, headerFields: nil, statusCode: 200)
let existingRequests = [Networking.RequestType.get: ["/users/{userID}/companies/{companyID}/products/{productID}": request]]
let result = try FakeRequest.find(ofType: .get, forPath: "/users/10/companies/20/products/30", in: existingRequests)

Expand All @@ -106,7 +106,7 @@ class FakeRequestTests: XCTestCase {
"resource10": "Resource {resourceID10}",
]

let request = FakeRequest(response: json, responseType: .json, statusCode: 200)
let request = FakeRequest(response: json, responseType: .json, headerFields: nil, statusCode: 200)
let existingRequests = [Networking.RequestType.get: ["resource1/{resourceID1}/resource2/{resourceID2}/resource3/{resourceID3}/resource4/{resourceID4}/resource5/{resourceID5}/resource6/{resourceID6}/resource7/{resourceID7}/resource8/{resourceID8}/resource9/{resourceID9}/resource10/{resourceID10}": request]]
let result = try FakeRequest.find(ofType: .get, forPath: "resource1/1/resource2/2/resource3/3/resource4/4/resource5/5/resource6/6/resource7/7/resource8/8/resource9/9/resource10/10", in: existingRequests)
let expected = [
Expand Down Expand Up @@ -219,6 +219,21 @@ extension FakeRequestTests {
XCTFail(response.error.localizedDescription)
}
}

func testFakeGETUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)

networking.fakeGET("/story", response: nil, headerFields: ["uid": "12345678"])

let result = try await networking.get("/story")
switch result {
case let .success(response):
let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

// POST tests
Expand Down Expand Up @@ -269,6 +284,21 @@ extension FakeRequestTests {
XCTFail(response.error.localizedDescription)
}
}

func testFakePOSTUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)

networking.fakePOST("/story", response: nil, headerFields: ["uid": "12345678"])

let result = try await networking.post("/story")
switch result {
case let .success(response):
let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

// PUT tests
Expand Down Expand Up @@ -319,6 +349,21 @@ extension FakeRequestTests {
XCTFail(response.error.localizedDescription)
}
}

func testFakePUTUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)

networking.fakePUT("/story", response: nil, headerFields: ["uid": "12345678"])

let result = try await networking.put("/story")
switch result {
case let .success(response):
let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

// PATCH tests
Expand Down Expand Up @@ -369,6 +414,21 @@ extension FakeRequestTests {
XCTFail(response.error.localizedDescription)
}
}

func testFakePATCHUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)

networking.fakePATCH("/story", response: nil, headerFields: ["uid": "12345678"])

let result = try await networking.patch("/story")
switch result {
case let .success(response):
let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

// DELETE tests
Expand Down Expand Up @@ -419,6 +479,21 @@ extension FakeRequestTests {
XCTFail(response.error.localizedDescription)
}
}

func testFakeDELETEUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)

networking.fakeDELETE("/story", response: nil, headerFields: ["uid": "12345678"])

let result = try await networking.delete("/story")
switch result {
case let .success(response):
let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

// Image tests
Expand All @@ -440,7 +515,8 @@ extension FakeRequestTests {

func testFakeImageDownloadWithInvalidStatusCode() async throws {
let networking = Networking(baseURL: baseURL)
networking.fakeImageDownload("/image/png", image: nil, statusCode: 401)
let pigImage = Image.find(named: "pig.png", inBundle: .module)
networking.fakeImageDownload("/image/png", image: pigImage, statusCode: 401)
let result = try await networking.downloadImage("/image/png")
switch result {
case .success:
Expand All @@ -449,4 +525,22 @@ extension FakeRequestTests {
XCTAssertEqual(response.error.code, 401)
}
}

func testFakeImageDownloadUsingHeader() async throws {
let networking = Networking(baseURL: baseURL)
let pigImage = Image.find(named: "pig.png", inBundle: .module)
networking.fakeImageDownload("/image/png", image: pigImage, headerFields: ["uid": "12345678"])
let result = try await networking.downloadImage("/image/png")
switch result {
case let .success(response):
let pigImageData = pigImage.pngData()
let imageData = response.image.pngData()
XCTAssertEqual(pigImageData, imageData)

let headers = response.headers
XCTAssertEqual(headers["uid"] as! String, "12345678")
case let .failure(response):
XCTFail(response.error.localizedDescription)
}
}
}

0 comments on commit 9cb2445

Please sign in to comment.