// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import Quick import Nimble @testable import SessionUtilitiesKit class RequestSpec: QuickSpec { override class func spec() { // MARK: Configuration @TestState var dependencies: Dependencies! = Dependencies() @TestState var urlRequest: URLRequest? @TestState var request: Request! @TestState var responseInfo: ResponseInfoType! = Network.ResponseInfo(code: 200, headers: [:]) // MARK: - a Request describe("a Request") { // MARK: -- is initialized with the correct default values it("is initialized with the correct default values") { let request: Request = Request( endpoint: .test1, target: Network.ServerTarget( server: "testServer", endpoint: TestEndpoint.test1, queryParameters: [:], x25519PublicKey: "" ) ) expect(request.method.rawValue).to(equal("GET")) expect(request.headers).to(equal([:])) expect(request.body).to(beNil()) } // MARK: -- when generating a URLRequest context("when generating a URLRequest") { // MARK: ---- generates the request correctly it("generates the request correctly") { request = Request( method: .post, server: "testServer", endpoint: .test1, queryParameters: [:], headers: [ "TestCustomHeader": "TestCustom", HTTPHeader.testHeader: "Test" ], x25519PublicKey: "", body: nil ) urlRequest = try? request.generateUrlRequest(using: dependencies) expect(urlRequest?.url?.absoluteString).to(equal("testServer/test1")) expect(urlRequest?.httpMethod).to(equal("POST")) expect(urlRequest?.allHTTPHeaderFields).to(equal([ "TestCustomHeader": "TestCustom", HTTPHeader.testHeader: "Test" ])) } // MARK: ---- sets all the values correctly it("sets all the values correctly") { let request: Request = Request( method: .delete, server: "testServer", endpoint: .test1, headers: [ .authorization: "test" ], x25519PublicKey: "" ) let urlRequest: URLRequest? = try? request.generateUrlRequest(using: dependencies) expect(urlRequest?.httpMethod).to(equal("DELETE")) expect(urlRequest?.allHTTPHeaderFields).to(equal(["Authorization": "test"])) expect(urlRequest?.httpBody).to(beNil()) } // MARK: ---- throws an error if the URL is invalid it("throws an error if the URL is invalid") { let request: Request = Request( server: "ftp:// test Server", endpoint: .testParams("test", 123), x25519PublicKey: "" ) expect { try request.generateUrlRequest(using: dependencies) } .to(throwError(NetworkError.invalidURL)) } // MARK: ---- with a base64 string body context("with a base64 string body") { // MARK: ------ successfully encodes the body it("successfully encodes the body") { let request: Request = Request( server: "testServer", endpoint: .test1, x25519PublicKey: "", body: "TestMessage".data(using: .utf8)!.base64EncodedString() ) let urlRequest: URLRequest? = try? request.generateUrlRequest(using: dependencies) let requestBody: Data? = Data(base64Encoded: urlRequest?.httpBody?.base64EncodedString() ?? "") let requestBodyString: String? = String(data: requestBody ?? Data(), encoding: .utf8) expect(requestBodyString).to(equal("TestMessage")) } // MARK: ------ throws an error if the body is not base64 encoded it("throws an error if the body is not base64 encoded") { let request: Request = Request( server: "testServer", endpoint: .test1, x25519PublicKey: "", body: "TestMessage" ) expect { try request.generateUrlRequest(using: dependencies) } .to(throwError(NetworkError.parsingFailed)) } } // MARK: ---- with a byte body context("with a byte body") { // MARK: ------ successfully encodes the body it("successfully encodes the body") { let request: Request<[UInt8], TestEndpoint> = Request( server: "testServer", endpoint: .test1, x25519PublicKey: "", body: [1, 2, 3] ) let urlRequest: URLRequest? = try? request.generateUrlRequest(using: dependencies) expect(urlRequest?.httpBody?.bytes).to(equal([1, 2, 3])) } } // MARK: ---- with a JSON body context("with a JSON body") { // MARK: ------ successfully encodes the body it("successfully encodes the body") { let request: Request = Request( server: "testServer", endpoint: .test1, x25519PublicKey: "", body: TestType(stringValue: "test") ) let urlRequest: URLRequest? = try? request.generateUrlRequest(using: dependencies) let requestBody: TestType? = try? JSONDecoder(using: dependencies).decode( TestType.self, from: urlRequest?.httpBody ?? Data() ) expect(requestBody).to(equal(TestType(stringValue: "test"))) } // MARK: ------ successfully encodes no body it("successfully encodes no body") { let request: Request = Request( server: "testServer", endpoint: .test1, x25519PublicKey: "", body: nil ) expect { try request.generateUrlRequest(using: dependencies) }.toNot(throwError()) } } } } // MARK: - a HTTP ServerTarget describe("a HTTP ServerTarget") { // MARK: ---- adds a leading forward slash to the endpoint path it("adds a leading forward slash to the endpoint path") { let target: Network.ServerTarget = Network.ServerTarget( server: "testServer", endpoint: TestEndpoint.test1, queryParameters: [:], x25519PublicKey: "" ) expect(target.urlPathAndParamsString).to(equal("/test1")) } // MARK: ---- creates a valid URL with no query parameters it("creates a valid URL with no query parameters") { let target: Network.ServerTarget = Network.ServerTarget( server: "testServer", endpoint: TestEndpoint.test1, queryParameters: [:], x25519PublicKey: "" ) expect(target.urlPathAndParamsString).to(equal("/test1")) } // MARK: ---- creates a valid URL when query parameters are provided it("creates a valid URL when query parameters are provided") { let target: Network.ServerTarget = Network.ServerTarget( server: "testServer", endpoint: TestEndpoint.test1, queryParameters: [ .testParam: "123" ], x25519PublicKey: "" ) expect(target.urlPathAndParamsString).to(equal("/test1?testParam=123")) } } } } // MARK: - Test Types fileprivate extension HTTPHeader { static let testHeader: HTTPHeader = "TestHeader" } fileprivate extension HTTPQueryParam { static let testParam: HTTPQueryParam = "testParam" } fileprivate enum TestEndpoint: EndpointType { case test1 case testParams(String, Int) static var name: String { "TestEndpoint" } static var batchRequestVariant: Network.BatchRequest.Child.Variant { .storageServer } static var excludedSubRequestHeaders: [HTTPHeader] { [] } var path: String { switch self { case .test1: return "test1" case .testParams(let str, let int): return "testParams/\(str)/int/\(int)" } } } fileprivate struct TestType: Codable, Equatable { let stringValue: String }