// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. import Foundation import Combine import Quick import Nimble @testable import SessionUtilitiesKit class PreparedRequestSpec: QuickSpec { override class func spec() { // MARK: Configuration @TestState var dependencies: Dependencies! = Dependencies() @TestState var urlRequest: URLRequest? @TestState var preparedRequest: Network.PreparedRequest! @TestState var request: Request! @TestState var responseInfo: ResponseInfoType! = Network.ResponseInfo(code: 200, headers: [:]) // MARK: - a PreparedRequest describe("a PreparedRequest") { // 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: .endpoint, queryParameters: [:], headers: [ "TestCustomHeader": "TestCustom", HTTPHeader.testHeader: "Test" ], x25519PublicKey: "", body: nil ) preparedRequest = Network.PreparedRequest( request: request, urlRequest: try! request.generateUrlRequest(using: dependencies), responseType: TestType.self, timeout: 10 ) expect(preparedRequest.request.url?.absoluteString).to(equal("testServer/endpoint")) expect(preparedRequest.request.httpMethod).to(equal("POST")) expect(preparedRequest.request.allHTTPHeaderFields).to(equal([ "TestCustomHeader": "TestCustom", HTTPHeader.testHeader: "Test" ])) } // MARK: ---- does not strip excluded subrequest headers it("does not strip excluded subrequest headers") { request = Request( method: .post, server: "testServer", endpoint: .endpoint, queryParameters: [:], headers: [ "TestCustomHeader": "TestCustom", HTTPHeader.testHeader: "Test" ], x25519PublicKey: "", body: nil ) preparedRequest = Network.PreparedRequest( request: request, urlRequest: try! request.generateUrlRequest(using: dependencies), responseType: TestType.self, timeout: 10 ) expect(TestEndpoint.excludedSubRequestHeaders).to(equal([HTTPHeader.testHeader])) expect(preparedRequest.request.allHTTPHeaderFields?.keys).to(contain([HTTPHeader.testHeader])) } } } // MARK: - a Decodable describe("a Decodable") { // MARK: -- decodes correctly it("decodes correctly") { let jsonData: Data = "{\"stringValue\":\"testValue\"}".data(using: .utf8)! let result: TestType? = try? TestType.decoded(from: jsonData) expect(result).to(equal(TestType(stringValue: "testValue"))) } } // MARK: - a (ResponseInfoType, Data?) Publisher describe("a (ResponseInfoType, Data?) Publisher") { // MARK: -- decodes valid data correctly it("decodes valid data correctly") { let jsonData: Data = "{\"stringValue\":\"testValue\"}".data(using: .utf8)! var result: (info: ResponseInfoType, response: TestType)? Just((responseInfo, jsonData)) .setFailureType(to: Error.self) .eraseToAnyPublisher() .decoded(as: TestType.self) .sinkUntilComplete( receiveValue: { result = $0 } ) expect(result).toNot(beNil()) expect(result?.response).to(equal(TestType(stringValue: "testValue"))) } // MARK: -- fails if there is no data it("fails if there is no data") { var error: Error? Just((responseInfo, nil)) .setFailureType(to: Error.self) .eraseToAnyPublisher() .decoded(as: [Int].self) .mapError { error.setting(to: $0) } .sinkUntilComplete() expect(error).to(matchError(NetworkError.parsingFailed)) } // MARK: -- fails if the data is not JSON it("fails if the data is not JSON") { var error: Error? Just((responseInfo, Data([1, 2, 3]))) .setFailureType(to: Error.self) .eraseToAnyPublisher() .decoded(as: [Int].self) .mapError { error.setting(to: $0) } .sinkUntilComplete() expect(error).to(matchError(NetworkError.parsingFailed)) } // MARK: -- fails if the data is not a JSON array it("fails if the data is not a JSON array") { var error: Error? Just((responseInfo, "{}".data(using: .utf8))) .setFailureType(to: Error.self) .eraseToAnyPublisher() .decoded(as: [Int].self) .mapError { error.setting(to: $0) } .sinkUntilComplete() expect(error).to(matchError(NetworkError.parsingFailed)) } } } } // MARK: - Test Types fileprivate extension HTTPHeader { static let testHeader: HTTPHeader = "TestHeader" } fileprivate enum TestEndpoint: EndpointType { case endpoint static var name: String { "TestEndpoint" } static var batchRequestVariant: Network.BatchRequest.Child.Variant { .storageServer } static var excludedSubRequestHeaders: [HTTPHeader] { [.testHeader] } var path: String { return "endpoint" } } fileprivate struct TestType: Codable, Equatable { let stringValue: String }