adding project file

This commit is contained in:
Ali Fakih 2020-03-09 17:28:43 +02:00
parent c78b302f99
commit 351517b0c3
16 changed files with 933 additions and 10 deletions

View File

@ -1,3 +0,0 @@
struct AFNetworks {
var text = "Hello, World!"
}

View File

@ -0,0 +1,66 @@
//
// DataTransferService.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public enum DataTransferError: Error {
case noResponse
case paring(Error)
case networkFailure(NetworkError)
case resolvedNetworkFailure(Error)
}
// MARK: - Error Resolver
public protocol DataTransferErrorResolver {
func resolve(error: NetworkError) -> Error
}
// MARK: - Error Logger
public protocol DataTransferErrorLogger {
func log(error: Error)
}
// MARK: - Response Decoder
public protocol ResponseDecoder {
func decode<T: Decodable>(_ data: Data) throws -> T
}
// MARK: - Data Transfer Service
public protocol DataTransferService {
typealias CompletionHandler<T> = (Result<T, Error>) -> Void
@discardableResult
func request<T: Decodable, E: ResponseRequestable>(with endpoint: E, completion: @escaping CompletionHandler<T>) -> NetworkCancellable? where E.Response == T
}
// MARK: - Response Decoders
public class JSONResponseDecoder: ResponseDecoder {
private let jsonDecoder = JSONDecoder()
public init() { }
public func decode<T: Decodable>(_ data: Data) throws -> T {
return try jsonDecoder.decode(T.self, from: data)
}
}
public class RawDataResponseDecoder: ResponseDecoder {
public init() { }
enum CodingKeys: String, CodingKey {
case `default` = ""
}
public func decode<T: Decodable>(_ data: Data) throws -> T {
if T.self is Data.Type, let data = data as? T {
return data
} else {
let context = DecodingError.Context(codingPath: [CodingKeys.default], debugDescription: "Expected Data type")
throw Swift.DecodingError.typeMismatch(T.self, context)
}
}
}

View File

@ -0,0 +1,20 @@
//
// DefaultDataTransferErrorLogger.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public final class DefaultDataTransferErrorLogger: DataTransferErrorLogger {
public init() { }
public func log(error: Error) {
#if DEBUG
print("-------------")
print("error: \(error)")
#endif
}
}

View File

@ -0,0 +1,16 @@
//
// DefaultDataTransferErrorResolver.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public class DefaultDataTransferErrorResolver: DataTransferErrorResolver {
public init() { }
public func resolve(error: NetworkError) -> Error {
return error
}
}

View File

@ -0,0 +1,57 @@
//
// DefaultDataTransferService.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public final class DefaultDataTransferService {
private let networkService: NetworkService
private let errorResolver: DataTransferErrorResolver
private let errorLogger: DataTransferErrorLogger
public init(networkService: NetworkService,
errorResolver: DataTransferErrorResolver = DefaultDataTransferErrorResolver(),
errorLogger: DataTransferErrorLogger = DefaultDataTransferErrorLogger()) {
self.networkService = networkService
self.errorResolver = errorResolver
self.errorLogger = errorLogger
}
}
extension DefaultDataTransferService: DataTransferService {
public func request<T: Decodable, E: ResponseRequestable>(with endpoint: E, completion: @escaping CompletionHandler<T>) -> NetworkCancellable? where E.Response == T {
return self.networkService.request(endpoint: endpoint) { result in
switch result {
case .success(let data):
let result: Result<T, Error> = self.decode(data: data, decoder: endpoint.responseDecoder)
DispatchQueue.main.async { return completion(result) }
case .failure(let nError):
self.errorLogger.log(error: nError)
let error = self.resolve(networkError: nError)
DispatchQueue.main.async { return completion(.failure(error))}
}
}
}
private func decode<T: Decodable> (data: Data?, decoder: ResponseDecoder) -> Result<T, Error> {
do {
guard let data = data else { return .failure(DataTransferError.noResponse)}
let result: T = try decoder.decode(data)
return .success(result)
} catch {
self.errorLogger.log(error: error)
return .failure(error)
}
}
private func resolve(networkError error: NetworkError) -> DataTransferError {
let resolvedError = self.errorResolver.resolve(error: error)
return resolvedError is NetworkError ? .networkFailure(error) : .resolvedNetworkFailure(resolvedError)
}
}

View File

@ -0,0 +1,137 @@
//
// Endpoint.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public enum HTTPMethod: String {
case delete = "DELETE"
case post = "POST"
case get = "GET"
case put = "PUT"
case head = "HEAD"
}
public enum BodyEncoding {
case jsonSerializationData
case stringEncodingAscii
}
public enum RequestGenerationError: Error {
case components
}
public class Endpoint<R>: ResponseRequestable {
public typealias Response = R
public var path: String
public var isFullPath: Bool
public var method: HTTPMethod
public var headerParameter: [String : String]
public var bodyParameter: [String : Any]
public var queryParameters: [String : Any]
public var bodyEncoding: BodyEncoding
public var responseDecoder: ResponseDecoder
public init(path: String,
isFullPath: Bool = false,
method: HTTPMethod = .get,
headerParameters: [String: String] = [:],
bodyParameters: [String: Any] = [:],
queryParameters: [String: Any] = [:],
bodyEncoding: BodyEncoding = .jsonSerializationData,
responseDecoder: ResponseDecoder = JSONResponseDecoder()) {
self.path = path
self.isFullPath = isFullPath
self.method = method
self.headerParameter = headerParameters
self.bodyParameter = bodyParameters
self.queryParameters = queryParameters
self.bodyEncoding = bodyEncoding
self.responseDecoder = responseDecoder
}
}
// MARK: - Requestable
public protocol Requestable {
var path: String { get }
var isFullPath: Bool { get }
var method: HTTPMethod { get }
var headerParameter: [String: String] { get }
var bodyParameter: [String: Any] { get }
var queryParameters: [String: Any] { get }
var bodyEncoding: BodyEncoding { get }
func urlRequest(with networkConfig: NetworkConfigurable) throws -> URLRequest
}
extension Requestable {
func url(with config: NetworkConfigurable) throws -> URL {
let baseURL = config.baseURL.absoluteString.last != "/" ? config.baseURL.absoluteString + "/" : config.baseURL.absoluteString
let endpoint = isFullPath ? path : baseURL.appending(path)
guard var urlComponents = URLComponents(string: endpoint) else { throw RequestGenerationError.components }
var urlQueryItems = [URLQueryItem]()
queryParameters.forEach {
urlQueryItems.append(URLQueryItem(name: $0.key, value: "\($0.value)"))
}
config.queryParameters.forEach {
urlQueryItems.append(URLQueryItem(name: $0.key, value: $0.value))
}
urlComponents.queryItems = !urlQueryItems.isEmpty ? urlQueryItems : nil
guard let url = urlComponents.url else { throw RequestGenerationError.components }
return url
}
public func urlRequest(with config: NetworkConfigurable) throws -> URLRequest {
let url = try self.url(with: config)
var urlRequest = URLRequest(url: url)
var allHeaders: [String: String] = config.headers
headerParameter.forEach { allHeaders.updateValue($0.key, forKey: $0.value) }
if !bodyParameter.isEmpty {
urlRequest.httpBody = encodeBody(bodyParameters: bodyParameter, bodyEncoding: bodyEncoding)
}
urlRequest.httpMethod = method.rawValue
urlRequest.allHTTPHeaderFields = allHeaders
return urlRequest
}
fileprivate func encodeBody(bodyParameters: [String: Any], bodyEncoding: BodyEncoding) -> Data? {
switch bodyEncoding {
case .jsonSerializationData:
return try? JSONSerialization.data(withJSONObject: bodyParameters)
case .stringEncodingAscii:
return bodyParameters.queryString.data(using: String.Encoding.ascii, allowLossyConversion: true)
}
}
}
// MARK: - ResponseRequestable
public protocol ResponseRequestable: Requestable {
associatedtype Response
var responseDecoder: ResponseDecoder { get }
}
fileprivate extension Dictionary {
var queryString: String {
return self.map { "\($0.key)=\($0.value)" }
.joined(separator: "&")
.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) ?? ""
}
}

View File

@ -0,0 +1,30 @@
//
// NetworkConfig.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
// MARK: - Network Configuration Protocol
public protocol NetworkConfigurable {
var baseURL: URL { get }
var headers: [String: String] { get }
var queryParameters: [String: String] { get }
}
// MARK: - Default Configuration
public struct ApiDataNetworkConfig: NetworkConfigurable {
public var baseURL: URL
public var headers: [String : String]
public var queryParameters: [String : String]
public init(baseURL: URL, headers: [String: String], queryParameters: [String: String]) {
self.baseURL = baseURL
self.headers = headers
self.queryParameters = queryParameters
}
}

View File

@ -0,0 +1,72 @@
//
// DefaultNetworkService.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public final class DefaultNetworkService {
private let config: NetworkConfigurable
private let sessionManager: NetworkSessionManager
private let logger: NetworkErrorLogger
public init(config: NetworkConfigurable, sessionManager: NetworkSessionManager, logger: NetworkErrorLogger = DefaultNetworkErrorLogger()) {
self.config = config
self.sessionManager = sessionManager
self.logger = logger
}
private func request(request: URLRequest, completion: @escaping CompletionHandler) -> NetworkCancellable {
let sessionDataTask = sessionManager.request(request) { (data, urlResponse, requestError) in
if let requestError = requestError {
var error: NetworkError
if let response = urlResponse as? HTTPURLResponse {
error = .error(statusCode: response.statusCode, data: data)
} else {
error = self.resolve(error: requestError)
}
self.logger.log(error: error)
completion(.failure(error))
} else {
self.logger.log(responseData: data, response: urlResponse)
completion(.success(data))
}
}
logger.log(request: request)
return sessionDataTask
}
private func resolve(error: Error) -> NetworkError {
let code = URLError.Code(rawValue: (error as NSError).code)
switch code {
case .notConnectedToInternet:
return .notConnected
case .cancelled:
return .cancelled
default:
return .generic(error)
}
}
}
extension DefaultNetworkService: NetworkService {
public func request(endpoint: Requestable, completion: @escaping CompletionHandler) -> NetworkCancellable? {
do {
let urlRequest = try endpoint.urlRequest(with: config)
return self.request(request: urlRequest, completion: completion)
} catch {
completion(.failure(.urlGeneration))
return nil
}
}
}

View File

@ -0,0 +1,24 @@
//
// DefaultSessionManager.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
// MARK: - Default Network Session Manager
// Note: If authorization is needed NetworkSessionManager can be implemented by using,
// for example, Alamofire SessionManager with its RequestAdapter and RequestRetrier.
// And it can be injected into NetworkService instead of default one.
public class DefaultNetworkSessionManager: NetworkSessionManager {
public init() {}
public func request(_ request: URLRequest,
completion: @escaping CompletionHandler) -> NetworkCancellable {
let task = URLSession.shared.dataTask(with: request, completionHandler: completion)
task.resume()
return task
}
}

View File

@ -0,0 +1,44 @@
//
// File.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
public final class DefaultNetworkErrorLogger: NetworkErrorLogger {
public init() { }
public func log(request: URLRequest) {
#if DEBUG
print("-------------")
print("request: \(request.url!)")
print("headers: \(request.allHTTPHeaderFields!)")
print("method: \(request.httpMethod!)")
if let httpBody = request.httpBody, let result = ((try? JSONSerialization.jsonObject(with: httpBody, options: []) as? [String: AnyObject]) as [String: AnyObject]??) {
print("body: \(String(describing: result))")
}
if let httpBody = request.httpBody, let resultString = String(data: httpBody, encoding: .utf8) {
print("body: \(String(describing: resultString))")
}
#endif
}
public func log(responseData data: Data?, response: URLResponse?) {
#if DEBUG
guard let data = data else { return }
if let dataDict = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
print("responseData: \(String(describing: dataDict))")
}
#endif
}
public func log(error: Error) {
#if DEBUG
print("error: \(error)")
#endif
}
}

View File

@ -0,0 +1,60 @@
//
// NetworkService.swift
// KooberMVVM-AF
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
// MARK: - Network Error Enum
public enum NetworkError: Error {
case error(statusCode: Int, data: Data?)
case notConnected
case cancelled
case generic(Error)
case urlGeneration
}
extension NetworkError {
public var isNotFoundError: Bool { return hasStatusCode(404) }
public func hasStatusCode(_ codeError: Int) -> Bool {
switch self {
case let .error(code, _):
return code == codeError
default: return false
}
}
}
// MARK: - Cancellable Protocol
public protocol NetworkCancellable {
func cancel()
}
extension URLSessionTask: NetworkCancellable { }
// MARK: - Network Service Protocol
public protocol NetworkService {
typealias CompletionHandler = (Result<Data?, NetworkError>) -> Void
func request(endpoint: Requestable, completion: @escaping CompletionHandler) -> NetworkCancellable?
}
// MARK: - Network Session Manager Protocol
public protocol NetworkSessionManager {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
func request(_ urlRequest: URLRequest, completion: @escaping CompletionHandler) -> NetworkCancellable
}
// MARK: - Network Error Logger
public protocol NetworkErrorLogger {
func log(request: URLRequest)
func log(responseData data: Data?, response: URLResponse?)
func log(error: Error)
}

View File

@ -0,0 +1,134 @@
//
// DataTransferServiceTests.swift
// AFNetworkTests
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import XCTest
@testable import AFNetworks
private struct MockModel: Decodable {
let name: String
}
class DataTransferServiceTests: XCTestCase {
private enum DataTransferErrorMock: Error {
case someError
}
func test_whenReceivedValidJsonInResponse_shouldDecodeResponseToDecodableObject() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should decode mock object")
let responseData = #"{"name": "Hello"}"#.data(using: .utf8)
let networkService = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: responseData,
error: nil))
let sut = DefaultDataTransferService(networkService: networkService)
//when
_ = sut.request(with: Endpoint<MockModel>(path: "http://mock.endpoint.com", method: .get)) { result in
do {
let object = try result.get()
XCTAssertEqual(object.name, "Hello")
expectation.fulfill()
} catch {
XCTFail("Failed decoding MockObject")
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenInvalidResponse_shouldNotDecodeObject() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should not decode mock object")
let responseData = #"{"age": 20}"#.data(using: .utf8)
let networkService = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: responseData,
error: nil))
let sut = DefaultDataTransferService(networkService: networkService)
//when
_ = sut.request(with: Endpoint<MockModel>(path: "http://mock.endpoint.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch {
expectation.fulfill()
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenBadRequestReceived_shouldRethrowNetworkError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should throw network error")
let responseData = #"{"invalidStructure": "Nothing"}"#.data(using: .utf8)!
let response = HTTPURLResponse(url: URL(string: "test_url")!,
statusCode: 500,
httpVersion: "1.1",
headerFields: nil)
let networkService = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: response,
data: responseData,
error: DataTransferErrorMock.someError))
let sut = DefaultDataTransferService(networkService: networkService)
//when
_ = sut.request(with: Endpoint<MockModel>(path: "http://mock.endpoint.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
if case DataTransferError.networkFailure(NetworkError.error(statusCode: 500, _)) = error {
expectation.fulfill()
} else {
XCTFail("Wrong error")
}
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenNoDataReceived_shouldThrowNoDataError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should throw no data error")
let response = HTTPURLResponse(url: URL(string: "test_url")!,
statusCode: 200,
httpVersion: "1.1",
headerFields: [:])
let networkService = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: response,
data: nil,
error: nil))
let sut = DefaultDataTransferService(networkService: networkService)
//when
_ = sut.request(with: Endpoint<MockModel>(path: "http://mock.endpoint.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
if case DataTransferError.noResponse = error {
expectation.fulfill()
} else {
XCTFail("Wrong error")
}
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
}

View File

@ -0,0 +1,16 @@
//
// NetworkConfigurableMock.swift
// AFNetwork
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
@testable import AFNetworks
class NetworkConfigurableMock: NetworkConfigurable {
var baseURL: URL = URL(string: "https://mock.test.com")!
var headers: [String: String] = [:]
var queryParameters: [String: String] = [:]
}

View File

@ -0,0 +1,28 @@
//
// NetworkSessionManagerMock.swift
// AFNetwork
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import Foundation
@testable import AFNetworks
struct NetworkSessionManagerMock: NetworkSessionManager {
let response: HTTPURLResponse?
let data: Data?
let error: Error?
func request(_ request: URLRequest,
completion: @escaping CompletionHandler) -> NetworkCancellable {
completion(data, response, error)
if #available(iOS 13, *) {
let session = URLSession.shared
return session.dataTask(with: request)
}else {
return URLSessionDataTask()
}
}
}

View File

@ -0,0 +1,229 @@
//
// NetworkServiceTests.swift
// AFNetworkTests
//
// Created by Ali Fakih on 2/24/20.
// Copyright © 2020 Ali Fakih. All rights reserved.
//
import XCTest
@testable import AFNetworks
class NetworkServiceTests: XCTestCase {
// MARK: - Endpoint Mock
private struct EndpointMock: Requestable {
var path: String
var isFullPath: Bool = false
var method: HTTPMethod
var headerParameter: [String : String] = [:]
var bodyParameter: [String : Any] = [:]
var queryParameters: [String : Any] = [:]
var bodyEncoding: BodyEncoding = .stringEncodingAscii
init(path: String, method: HTTPMethod) {
self.path = path
self.method = method
}
}
// MARK: - Network Error Logger
class NetworkErrorLoggerMock: NetworkErrorLogger {
var loggedErrors: [Error] = []
func log(request: URLRequest) { }
func log(responseData data: Data?, response: URLResponse?) { }
func log(error: Error) { loggedErrors.append(error) }
}
private enum NetworkErrorMock: Error {
case someError
}
func test_whenMockDataPassed_shouldReturnProperResponse() {
//GIVEN
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return Correct Data")
let expectedResponseData = "Response data".data(using: .utf8)
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(
response: nil,
data: expectedResponseData,
error: nil))
//WHEN
_ = sut.request(endpoint: EndpointMock(path: "http://mock.test.com", method: .get), completion: { result in
guard let responseData = try? result.get() else {
XCTFail("Should return proper response")
return
}
XCTAssertEqual(responseData, expectedResponseData)
expectation.fulfill()
})
//THEN
wait(for: [expectation], timeout: 0.1)
}
func test_whenErrorWithNSURLErrorCancelledReturned_shouldReturnCancelledError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return hasStatusCode error")
let cancelledError = NSError(domain: "network", code: NSURLErrorCancelled, userInfo: nil)
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: nil,
error: cancelledError as Error))
//when
_ = sut.request(endpoint: EndpointMock(path: "http://mock.test.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
guard case NetworkError.cancelled = error else {
XCTFail("NetworkError.cancelled not found")
return
}
expectation.fulfill()
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenMalformedUrlPassed_shouldReturnUrlGenerationError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return correct data")
let expectedResponseData = "Response data".data(using: .utf8)!
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: expectedResponseData,
error: nil))
//when
_ = sut.request(endpoint: EndpointMock(path: "-;@,?:ą", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should throw url generation error")
} catch let error {
guard case NetworkError.urlGeneration = error else {
XCTFail("Should throw url generation error")
return
}
expectation.fulfill()
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenStatusCodeEqualOrAbove400_shouldReturnhasStatusCodeError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return hasStatusCode error")
let response = HTTPURLResponse(url: URL(string: "test_url")!,
statusCode: 500,
httpVersion: "1.1",
headerFields: [:])
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: response,
data: nil,
error: NetworkErrorMock.someError))
//when
_ = sut.request(endpoint: EndpointMock(path: "http://mock.test.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
if case NetworkError.error(let statusCode, _) = error {
XCTAssertEqual(statusCode, 500)
expectation.fulfill()
}
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenErrorWithNSURLErrorNotConnectedToInternetReturned_shouldReturnNotConnectedError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return hasStatusCode error")
let error = NSError(domain: "network", code: NSURLErrorNotConnectedToInternet, userInfo: nil)
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: nil,
error: error as Error))
//when
_ = sut.request(endpoint: EndpointMock(path: "http://mock.test.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
guard case NetworkError.notConnected = error else {
XCTFail("NetworkError.notConnected not found")
return
}
expectation.fulfill()
}
}
//then
wait(for: [expectation], timeout: 0.1)
}
func test_whenhasStatusCodeUsedWithWrongError_shouldReturnFalse() {
//when
let sut = NetworkError.notConnected
//then
XCTAssertFalse(sut.hasStatusCode(200))
}
func test_whenhasStatusCodeUsed_shouldReturnCorrectStatusCode_() {
//when
let sut = NetworkError.error(statusCode: 400, data: nil)
//then
XCTAssertTrue(sut.hasStatusCode(400))
XCTAssertFalse(sut.hasStatusCode(399))
XCTAssertFalse(sut.hasStatusCode(401))
}
func test_whenErrorWithNSURLErrorNotConnectedToInternetReturned_shouldLogThisError() {
//given
let config = NetworkConfigurableMock()
let expectation = self.expectation(description: "Should return hasStatusCode error")
let error = NSError(domain: "network", code: NSURLErrorNotConnectedToInternet, userInfo: nil)
let networkErrorLogger = NetworkErrorLoggerMock()
let sut = DefaultNetworkService(config: config, sessionManager: NetworkSessionManagerMock(response: nil,
data: nil,
error: error as Error),
logger: networkErrorLogger)
//when
_ = sut.request(endpoint: EndpointMock(path: "http://mock.test.com", method: .get)) { result in
do {
_ = try result.get()
XCTFail("Should not happen")
} catch let error {
guard case NetworkError.notConnected = error else {
XCTFail("NetworkError.notConnected not found")
return
}
expectation.fulfill()
}
}
//then
wait(for: [expectation], timeout: 0.1)
XCTAssertTrue(networkErrorLogger.loggedErrors.contains {
guard case NetworkError.notConnected = $0 else { return false }
return true
})
}
}

View File

@ -1,7 +0,0 @@
import XCTest
import AFNetworksTests
var tests = [XCTestCaseEntry]()
tests += AFNetworksTests.allTests()
XCTMain(tests)