diff --git a/Sources/Hermes/Hermes.swift b/Sources/Hermes/Hermes.swift index 0505414..5c8029a 100644 --- a/Sources/Hermes/Hermes.swift +++ b/Sources/Hermes/Hermes.swift @@ -11,6 +11,7 @@ public enum NetworkError: Error { case serverError(String) case decodingError case invalidResponse + case notFound } extension NetworkError: LocalizedError { @@ -19,18 +20,19 @@ extension NetworkError: LocalizedError { case .badRequest: return NSLocalizedString("Unable to perform request", comment: "badRequestError") case .serverError(let errorMessage): - print(errorMessage) return NSLocalizedString(errorMessage, comment: "serverError") case .decodingError: return NSLocalizedString("Unable to decode successfully", comment: "decodingError") case .invalidResponse: return NSLocalizedString("Invalid response", comment: "invalidResponse") + case .notFound: + return NSLocalizedString("Not Found", comment: "notFound") } } } public enum HTTPMethod { - case get([URLQueryItem]) + case get([URLQueryItem]? = nil) case post(Data?) case put(Data?) case patch(Data?) @@ -110,14 +112,25 @@ public struct Hermes { let (data, response) = try await session.data(for: request) - guard let _ = response as? HTTPURLResponse else { + guard let response = response as? HTTPURLResponse else { throw NetworkError.invalidResponse } + if response.statusCode != 200 { + switch response.statusCode { + case 404: + throw NetworkError.notFound + case 500: + throw NetworkError.serverError(response.description) + default: + throw NetworkError.invalidResponse + } + } + guard let result = try? JSONDecoder().decode(resource.modelType, from: data) else { throw NetworkError.decodingError } - + return result } } diff --git a/Tests/HermesTests/HermesTests.swift b/Tests/HermesTests/HermesTests.swift index b1cef3e..8dbd80c 100644 --- a/Tests/HermesTests/HermesTests.swift +++ b/Tests/HermesTests/HermesTests.swift @@ -2,11 +2,126 @@ import XCTest @testable import Hermes final class HermesTests: XCTestCase { - func testExample() throws { - // XCTest Documentation - // https://developer.apple.com/documentation/xctest - - // Defining Test Cases and Test Methods - // https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods + + func testGetRequest() async throws { + let hermes = Hermes() + let queryParams = [URLQueryItem(name: "foo", value: "bar")] + let resource = Resource(url: URL(string: "https://httpbin.org/get")!, method: .get(queryParams), modelType: GetResponse.self) + + let getResponse = try await hermes.load(resource) + + XCTAssertEqual(getResponse.url, "https://httpbin.org/get?foo=bar") + XCTAssertEqual(getResponse.args.count, 1) + XCTAssertGreaterThan(getResponse.headers.count, 0) + } + + func testPostRequest() async throws { + let hermes = Hermes() + let postDatas = try? JSONEncoder().encode(["foo": "bar"]) + let resource = Resource(url: URL(string: "https://httpbin.org/post")!, method: .post(postDatas), modelType: PostResponse.self) + + let postResponse = try await hermes.load(resource) + + XCTAssertEqual(postResponse.url, "https://httpbin.org/post") + XCTAssertEqual(postResponse.args.count, 0) + XCTAssertNotNil(postResponse.json) + XCTAssertEqual(postResponse.json!.count, 1) + XCTAssertGreaterThan(postResponse.headers.count, 0) + } + + func testPutRequest() async throws { + let hermes = Hermes() + let postDatas = try? JSONEncoder().encode(["foo": "bar"]) + let resource = Resource(url: URL(string: "https://httpbin.org/put")!, method: .put(postDatas), modelType: PostResponse.self) + + let putResponse = try await hermes.load(resource) + + XCTAssertEqual(putResponse.url, "https://httpbin.org/put") + XCTAssertEqual(putResponse.args.count, 0) + XCTAssertNotNil(putResponse.json) + XCTAssertEqual(putResponse.json!.count, 1) + XCTAssertGreaterThan(putResponse.headers.count, 0) + } + + func testPatchRequest() async throws { + let hermes = Hermes() + let postDatas = try? JSONEncoder().encode(["foo": "bar"]) + let resource = Resource(url: URL(string: "https://httpbin.org/patch")!, method: .patch(postDatas), modelType: PostResponse.self) + + let patchResponse = try await hermes.load(resource) + + XCTAssertEqual(patchResponse.url, "https://httpbin.org/patch") + XCTAssertEqual(patchResponse.args.count, 0) + XCTAssertNotNil(patchResponse.json) + XCTAssertEqual(patchResponse.json!.count, 1) + XCTAssertGreaterThan(patchResponse.headers.count, 0) + } + + func testDeleteRequest() async throws { + let hermes = Hermes() + let resource = Resource(url: URL(string: "https://httpbin.org/delete")!, method: .delete, modelType: PostResponse.self) + + let deleteResponse = try await hermes.load(resource) + + XCTAssertEqual(deleteResponse.url, "https://httpbin.org/delete") + XCTAssertEqual(deleteResponse.args.count, 0) + XCTAssertNil(deleteResponse.json) + XCTAssertGreaterThan(deleteResponse.headers.count, 0) + } + + func testNotFound() async throws { + let hermes = Hermes() + let resource = Resource(url: URL(string: "https://httpbin.org/status/404")!, method: .get(), modelType: GetResponse.self) + + do { + let _ = try await hermes.load(resource) + } catch NetworkError.notFound { + XCTAssert(true) + } + } + + func testServerError() async throws { + let hermes = Hermes() + let resource = Resource(url: URL(string: "https://httpbin.org/status/500")!, method: .get(), modelType: GetResponse.self) + + do { + let _ = try await hermes.load(resource) + } catch NetworkError.serverError(_) { + XCTAssert(true) + } + } + + func testDecodeError() async throws { + let hermes = Hermes() + let resource = Resource(url: URL(string: "https://httpbin.org/get")!, method: .get(), modelType: DummyResponse.self) + + do { + let _ = try await hermes.load(resource) + } catch NetworkError.decodingError { + XCTAssert(true) + } } } + +struct GetResponse: Codable { + let args: [String: String] + let headers: [String: String] + let origin: String + let url: String +} + +struct PostResponse: Codable { + let args: [String: String] + let data: String + let files: [String: String] + let form: [String: String] + let headers: [String: String] + let json: [String: String]? + let origin: String + let url: String +} + +struct DummyResponse: Codable { + let dumb: Int + let dumber: Double +}