// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import PromiseKit import SessionSnodeKit import SessionUtilitiesKit import Quick import Nimble @testable import SessionMessagingKit import AVFoundation class BatchRequestInfoSpec: QuickSpec { struct TestType: Codable, Equatable { let stringValue: String } // MARK: - Spec override func spec() { // MARK: - BatchSubRequest describe("a BatchSubRequest") { var subRequest: OpenGroupAPI.BatchSubRequest! context("when initializing") { it("sets the headers to nil if there aren't any") { subRequest = OpenGroupAPI.BatchSubRequest( request: Request( server: "testServer", endpoint: .batch ) ) expect(subRequest.headers).to(beNil()) } it("converts the headers to HTTP headers") { subRequest = OpenGroupAPI.BatchSubRequest( request: Request( method: .get, server: "testServer", endpoint: .batch, queryParameters: [:], headers: [.authorization: "testAuth"], body: nil ) ) expect(subRequest.headers).to(equal(["Authorization": "testAuth"])) } } context("when encoding") { it("successfully encodes a string body") { subRequest = OpenGroupAPI.BatchSubRequest( request: Request( method: .get, server: "testServer", endpoint: .batch, queryParameters: [:], headers: [:], body: "testBody" ) ) let subRequestData: Data = try! JSONEncoder().encode(subRequest) let subRequestString: String? = String(data: subRequestData, encoding: .utf8) expect(subRequestString) .to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"b64\":\"testBody\"}")) } it("successfully encodes a byte body") { subRequest = OpenGroupAPI.BatchSubRequest( request: Request<[UInt8], OpenGroupAPI.Endpoint>( method: .get, server: "testServer", endpoint: .batch, queryParameters: [:], headers: [:], body: [1, 2, 3] ) ) let subRequestData: Data = try! JSONEncoder().encode(subRequest) let subRequestString: String? = String(data: subRequestData, encoding: .utf8) expect(subRequestString) .to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"bytes\":[1,2,3]}")) } it("successfully encodes a JSON body") { subRequest = OpenGroupAPI.BatchSubRequest( request: Request( method: .get, server: "testServer", endpoint: .batch, queryParameters: [:], headers: [:], body: TestType(stringValue: "testValue") ) ) let subRequestData: Data = try! JSONEncoder().encode(subRequest) let subRequestString: String? = String(data: subRequestData, encoding: .utf8) expect(subRequestString) .to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"json\":{\"stringValue\":\"testValue\"}}")) } } } // MARK: - BatchSubResponse describe("a BatchSubResponse") { context("when decoding") { it("decodes correctly") { let jsonString: String = """ { "code": 200, "headers": { "testKey": "testValue" }, "body": { "stringValue": "testValue" } } """ let subResponse: OpenGroupAPI.BatchSubResponse? = try? JSONDecoder().decode( OpenGroupAPI.BatchSubResponse.self, from: jsonString.data(using: .utf8)! ) expect(subResponse).toNot(beNil()) expect(subResponse?.body).toNot(beNil()) } it("decodes with invalid body data") { let jsonString: String = """ { "code": 200, "headers": { "testKey": "testValue" }, "body": "Hello!!!" } """ let subResponse: OpenGroupAPI.BatchSubResponse? = try? JSONDecoder().decode( OpenGroupAPI.BatchSubResponse.self, from: jsonString.data(using: .utf8)! ) expect(subResponse).toNot(beNil()) } it("flags invalid body data as invalid") { let jsonString: String = """ { "code": 200, "headers": { "testKey": "testValue" }, "body": "Hello!!!" } """ let subResponse: OpenGroupAPI.BatchSubResponse? = try? JSONDecoder().decode( OpenGroupAPI.BatchSubResponse.self, from: jsonString.data(using: .utf8)! ) expect(subResponse).toNot(beNil()) expect(subResponse?.body).to(beNil()) expect(subResponse?.failedToParseBody).to(beTrue()) } it("does not flag a missing or invalid optional body as invalid") { let jsonString: String = """ { "code": 200, "headers": { "testKey": "testValue" }, } """ let subResponse: OpenGroupAPI.BatchSubResponse? = try? JSONDecoder().decode( OpenGroupAPI.BatchSubResponse.self, from: jsonString.data(using: .utf8)! ) expect(subResponse).toNot(beNil()) expect(subResponse?.body).to(beNil()) expect(subResponse?.failedToParseBody).to(beFalse()) } it("does not flag a NoResponse body as invalid") { let jsonString: String = """ { "code": 200, "headers": { "testKey": "testValue" }, } """ let subResponse: OpenGroupAPI.BatchSubResponse? = try? JSONDecoder().decode( OpenGroupAPI.BatchSubResponse.self, from: jsonString.data(using: .utf8)! ) expect(subResponse).toNot(beNil()) expect(subResponse?.body).to(beNil()) expect(subResponse?.failedToParseBody).to(beFalse()) } } } // MARK: - BatchRequestInfo describe("a BatchRequestInfo") { var request: Request! beforeEach { request = Request( method: .get, server: "testServer", endpoint: .batch, queryParameters: [:], headers: [:], body: TestType(stringValue: "testValue") ) } it("initializes correctly when given a request") { let requestInfo: OpenGroupAPI.BatchRequestInfo = OpenGroupAPI.BatchRequestInfo( request: request ) expect(requestInfo.request).to(equal(request)) expect(requestInfo.responseType == OpenGroupAPI.BatchSubResponse.self).to(beTrue()) } it("initializes correctly when given a request and a response type") { let requestInfo: OpenGroupAPI.BatchRequestInfo = OpenGroupAPI.BatchRequestInfo( request: request, responseType: TestType.self ) expect(requestInfo.request).to(equal(request)) expect(requestInfo.responseType == OpenGroupAPI.BatchSubResponse.self).to(beTrue()) } it("exposes the endpoint correctly") { let requestInfo: OpenGroupAPI.BatchRequestInfo = OpenGroupAPI.BatchRequestInfo( request: request ) expect(requestInfo.endpoint.path).to(equal(request.endpoint.path)) } it("generates a sub request correctly") { let requestInfo: OpenGroupAPI.BatchRequestInfo = OpenGroupAPI.BatchRequestInfo( request: request ) let subRequest: OpenGroupAPI.BatchSubRequest = requestInfo.toSubRequest() expect(subRequest.method).to(equal(request.method)) expect(subRequest.path).to(equal(request.urlPathAndParamsString)) expect(subRequest.headers).to(beNil()) } } // MARK: - Convenience // MARK: --Decodable describe("a Decodable") { 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: - --Promise describe("an (OnionRequestResponseInfoType, Data?) Promise") { var responseInfo: OnionRequestResponseInfoType! var capabilities: OpenGroupAPI.Capabilities! var pinnedMessage: OpenGroupAPI.PinnedMessage! var data: Data! beforeEach { responseInfo = OnionRequestAPI.ResponseInfo(code: 200, headers: [:]) capabilities = OpenGroupAPI.Capabilities(capabilities: [], missing: nil) pinnedMessage = OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 123, pinnedBy: "test") data = """ [\([ try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], body: capabilities, failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], body: pinnedMessage, failedToParseBody: false ) ) ] .map { String(data: $0, encoding: .utf8)! } .joined(separator: ","))] """.data(using: .utf8)! } it("decodes valid data correctly") { let result = Promise.value((responseInfo, data)) .decoded(as: [ OpenGroupAPI.BatchSubResponse.self, OpenGroupAPI.BatchSubResponse.self ]) expect(result.value).toNot(beNil()) expect((result.value?[0].1 as? OpenGroupAPI.BatchSubResponse)?.body) .to(equal(capabilities)) expect((result.value?[1].1 as? OpenGroupAPI.BatchSubResponse)?.body) .to(equal(pinnedMessage)) } it("fails if there is no data") { let result = Promise.value((responseInfo, nil)).decoded(as: []) expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription)) } it("fails if the data is not JSON") { let result = Promise.value((responseInfo, Data([1, 2, 3]))).decoded(as: []) expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription)) } it("fails if the data is not a JSON array") { let result = Promise.value((responseInfo, "{}".data(using: .utf8))).decoded(as: []) expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription)) } it("fails if the JSON array does not have the same number of items as the expected types") { let result = Promise.value((responseInfo, data)) .decoded(as: [ OpenGroupAPI.BatchSubResponse.self, OpenGroupAPI.BatchSubResponse.self, OpenGroupAPI.BatchSubResponse.self ]) expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription)) } it("fails if one of the JSON array values fails to decode") { data = """ [\([ try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], body: capabilities, failedToParseBody: false ) ) ] .map { String(data: $0, encoding: .utf8)! } .joined(separator: ",")),{"test": "test"}] """.data(using: .utf8)! let result = Promise.value((responseInfo, data)) .decoded(as: [ OpenGroupAPI.BatchSubResponse.self, OpenGroupAPI.BatchSubResponse.self ]) expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription)) } } } }