-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feat]: Rename MockResponseProvider to StoredResponseProvider
- Loading branch information
Showing
6 changed files
with
201 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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 characters
This file contains 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 characters
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// | ||
// StoredResponseProvider.swift | ||
// | ||
// | ||
// Created by Matej Molnár on 04.01.2023. | ||
// | ||
|
||
import Foundation | ||
|
||
// necessary for NSDataAsset import | ||
#if os(macOS) | ||
import AppKit | ||
#else | ||
import UIKit | ||
#endif | ||
|
||
/// A response provider which creates responses for requests from corresponding data files stored in Assets. | ||
open class StoredResponseProvider: ResponseProviding { | ||
private let bundle: Bundle | ||
private let sessionId: String | ||
private let requestCounter = Counter() | ||
private lazy var decoder = JSONDecoder() | ||
|
||
/// Creates MockResponseProvider instance. | ||
/// - Parameters: | ||
/// - bundle: A bundle which includes the assets file. | ||
/// - sessionId: An ID of a session, which data should be read. | ||
public init(with bundle: Bundle, sessionId: String) { | ||
self.bundle = bundle | ||
self.sessionId = sessionId | ||
} | ||
|
||
/// Creates a ``Response`` for a given `URLRequest` based on data from a corresponding file stored in Assets. | ||
/// - Parameter request: URL request. | ||
public func response(for request: URLRequest) async throws -> Response { | ||
guard let model = try? await loadModel(for: request) else { | ||
throw NetworkError.underlying(error: StoredResponseProviderError.unableToLoadAssetData) | ||
} | ||
|
||
guard | ||
let statusCode = model.statusCode, | ||
let url = request.url, | ||
let httpResponse = HTTPURLResponse( | ||
url: url, | ||
statusCode: statusCode, | ||
httpVersion: nil, | ||
headerFields: model.responseHeaders | ||
) | ||
else { | ||
throw NetworkError.underlying(error: StoredResponseProviderError.unableToConstructResponse) | ||
} | ||
|
||
return Response(model.responseBody ?? Data(), httpResponse) | ||
} | ||
} | ||
|
||
// MARK: Private helper functions | ||
|
||
private extension StoredResponseProvider { | ||
/// Loads a corresponding file from Assets for a given ``URLRequest`` and decodes the data to `EndpointRequestStorageModel`. | ||
func loadModel(for request: URLRequest) async throws -> EndpointRequestStorageModel? { | ||
// counting from 0, check storage request processing | ||
let count = await requestCounter.count(for: request.identifier) | ||
|
||
if let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count)", bundle: bundle)?.data { | ||
// store info about next indexed api call | ||
await requestCounter.increment(for: request.identifier) | ||
return try decoder.decode(EndpointRequestStorageModel.self, from: data) | ||
} | ||
|
||
// return previous response, if no more stored indexed api calls | ||
// swiftlint:disable:next empty_count | ||
if count > 0, let data = NSDataAsset(name: "\(sessionId)_\(request.identifier)_\(count - 1)", bundle: bundle)?.data { | ||
return try decoder.decode(EndpointRequestStorageModel.self, from: data) | ||
} | ||
|
||
return nil | ||
} | ||
} |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// StoredResponseProvider.swift | ||
// | ||
// | ||
// Created by Matej Molnár on 04.01.2023. | ||
// | ||
|
||
import Foundation | ||
|
||
/// An error that occurs during loading a ``Response`` from assets by `StoredResponseProvider`. | ||
enum StoredResponseProviderError: Error { | ||
/// An indication that there was a problem with loading or decoding data from assets. | ||
case unableToLoadAssetData | ||
/// An indication that it was not possible to construct a `Response` from the loaded data. | ||
case unableToConstructResponse | ||
} |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,6 @@ | |
// Created by Jan Kodeš on 23.01.2024. | ||
// | ||
|
||
import Foundation | ||
import Foundation | ||
import XCTest | ||
|
||
|
104 changes: 104 additions & 0 deletions
104
Tests/NetworkingTests/StoredResponseProviderTests.swift
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// | ||
// StoredResponseProviderTests.swift | ||
// | ||
// | ||
// Created by Matej Molnár on 05.01.2023. | ||
// | ||
|
||
@testable import Networking | ||
import XCTest | ||
|
||
final class StoredResponseProviderTests: XCTestCase { | ||
// swiftlint:disable:next force_unwrapping | ||
private lazy var mockUrlRequest = URLRequest(url: URL(string: "https://reqres.in/api/users?page=2")!) | ||
private let mockSessionId = "2023-01-04T16:15:29Z" | ||
|
||
private let mockHeaderFields = [ | ||
"Server": "cloudflare", | ||
"Etag": "W/\"406-ut0vzoCuidvyMf8arZpMpJ6ZRDw\"", | ||
"x-powered-by": "Express", | ||
"nel": "{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}", | ||
"Content-Encoding": "br", | ||
"Vary": "Accept-Encoding", | ||
// swiftlint:disable:next line_length | ||
"report-to": "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=5XGHUrnfYDsl7guBAx0nFk7LTbUgOLjp5%2BGMkSPetC5OrW6fKlUc1NBBtOKHKe9yWrcbXkF4TQe8jsv1c4KggYW1q4pYf5G2rQvA8XACg1znl6MbWiNj1w2wOg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}", | ||
"Content-Type": "application/json; charset=utf-8", | ||
"cf-cache-status": "HIT", | ||
"Cache-Control": "max-age=14400", | ||
"Access-Control-Allow-Origin": "*", | ||
"cf-ray": "784545f34d2f27bc-PRG", | ||
"Date": "Wed, 04 Jan 2023 16:15:29 GMT", | ||
"Via": "1.1 vegur", | ||
"Age": "6306" | ||
] | ||
|
||
func testLoadingData() async throws { | ||
let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: mockSessionId) | ||
|
||
// call request multiple times, 6 testing data files | ||
// test reading correct file | ||
for index in 0...10 { | ||
let response = try await storedResponseProvider.response(for: mockUrlRequest) | ||
|
||
XCTAssert(response.response is HTTPURLResponse) | ||
|
||
guard let httpResponse = response.response as? HTTPURLResponse else { | ||
XCTAssert(false, "Wrong response type") | ||
return | ||
} | ||
|
||
guard let headerFields = httpResponse.allHeaderFields as? [String: String] else { | ||
XCTAssert(false, "Wrong response header fields type") | ||
return | ||
} | ||
|
||
XCTAssertEqual(headerFields, mockHeaderFields) | ||
|
||
switch index { | ||
case 3: | ||
XCTAssertEqual(httpResponse.statusCode, 200) | ||
XCTAssertEqual(response.data.count, 0) | ||
case 4: | ||
XCTAssertEqual(httpResponse.statusCode, 400) | ||
XCTAssertEqual(response.data.count, 0) | ||
default: | ||
XCTAssertEqual(httpResponse.statusCode, 200) | ||
XCTAssertEqual(response.data.count, 1030) | ||
} | ||
} | ||
} | ||
|
||
func testUnableToLoadAssetError() async { | ||
let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: "NonexistentSessionId") | ||
|
||
do { | ||
_ = try await storedResponseProvider.response(for: mockUrlRequest) | ||
XCTAssert(false, "function didn't throw an error even though it should have") | ||
} catch { | ||
var correctError = false | ||
if case NetworkError.underlying(error: StoredResponseProviderError.unableToLoadAssetData) = error { | ||
correctError = true | ||
} | ||
XCTAssert(correctError, "function threw an incorrect error") | ||
} | ||
} | ||
|
||
func testUnableToConstructResponseError() async { | ||
let storedResponseProvider = StoredResponseProvider(with: Bundle.module, sessionId: "2023-01-04T16:15:29Z(corrupted)") | ||
|
||
do { | ||
_ = try await storedResponseProvider.response(for: mockUrlRequest) | ||
XCTAssert(false, "function didn't throw an error even though it should have") | ||
} catch { | ||
var correctError = false | ||
if case NetworkError.underlying(error: StoredResponseProviderError.unableToConstructResponse) = error { | ||
correctError = true | ||
} | ||
XCTAssert(correctError, "function threw an incorrect error") | ||
} | ||
} | ||
|
||
static var allTests = [ | ||
("testLoadingData", testLoadingData) | ||
] | ||
} |