Updated to the latest version of the libSession-utl (contacts size)

Fixed some broken unit tests (still some broken ones)
pull/856/head
Morgan Pretty 2 years ago
parent 14174e3fbd
commit e3a080dd5b

@ -155,7 +155,7 @@ internal extension SessionUtil {
.bytes
.map { CChar(bitPattern: $0) }
var contact: contacts_contact = contacts_contact()
guard contacts_get_or_create(conf, &contact, &sessionId) else {
guard contacts_get_or_construct(conf, &contact, &sessionId) else {
SNLog("Unable to upsert contact from Config Message")
return
}

@ -67,7 +67,7 @@ bool contacts_get(const config_object* conf, contacts_contact* contact, const ch
///
/// This is the method that should usually be used to create or update a contact, followed by
/// setting fields in the contact, and then giving it to contacts_set().
bool contacts_get_or_create(
bool contacts_get_or_construct(
const config_object* conf, contacts_contact* contact, const char* session_id)
__attribute__((warn_unused_result));
@ -79,7 +79,7 @@ void contacts_set(config_object* conf, const contacts_contact* contact);
// is simple enough; for example to update `approved` and leave everything else unchanged:
//
// contacts_contact c;
// if (contacts_get_or_create(conf, &c, some_session_id)) {
// if (contacts_get_or_construct(conf, &c, some_session_id)) {
// const char* new_nickname = "Joe";
// c.approved = new_nickname;
// contacts_set_or_create(conf, &c);
@ -92,6 +92,9 @@ void contacts_set(config_object* conf, const contacts_contact* contact);
/// iteration; see details below.
bool contacts_erase(config_object* conf, const char* session_id);
/// Returns the number of contacts.
size_t contacts_size(const config_object* conf);
/// Functions for iterating through the entire contact list, in sorted order. Intended use is:
///
/// contacts_contact c;

@ -84,16 +84,17 @@ class Contacts : public ConfigBase {
/// Similar to get(), but if the session ID does not exist this returns a filled-out
/// contact_info containing the session_id (all other fields will be empty/defaulted). This is
/// intended to be combined with `set` to set-or-create a record. Note that this does not add
/// the session id to the contact list when called: that requires also calling `set` with this
/// value.
contact_info get_or_create(std::string_view pubkey_hex) const;
/// intended to be combined with `set` to set-or-create a record.
///
/// NB: calling this does *not* add the session id to the contact list when called: that
/// requires also calling `set` with this value.
contact_info get_or_construct(std::string_view pubkey_hex) const;
/// Sets or updates multiple contact info values at once with the given info. The usual use is
/// to access the current info, change anything desired, then pass it back into set_contact,
/// e.g.:
///
/// auto c = contacts.get_or_create(pubkey);
/// auto c = contacts.get_or_construct(pubkey);
/// c.name = "Session User 42";
/// c.nickname = "BFF";
/// contacts.set(c);
@ -119,6 +120,12 @@ class Contacts : public ConfigBase {
/// example.
iterator erase(iterator it);
/// Returns the number of contacts.
size_t size() const;
/// Returns true if the contact list is empty.
bool empty() const { return size() == 0; }
/// Iterators for iterating through all contacts. Typically you access this implicit via a for
/// loop over the `Contacts` object:
///

@ -38,8 +38,10 @@ class ConfigContactsSpec: QuickSpec {
let contactPtr: UnsafeMutablePointer<contacts_contact>? = nil
expect(contacts_get(conf, contactPtr, &definitelyRealId)).to(beFalse())
expect(contacts_size(conf)).to(equal(0))
var contact2: contacts_contact = contacts_contact()
expect(contacts_get_or_create(conf, &contact2, &definitelyRealId)).to(beTrue())
expect(contacts_get_or_construct(conf, &contact2, &definitelyRealId)).to(beTrue())
expect(contact2.name).to(beNil())
expect(contact2.nickname).to(beNil())
expect(contact2.approved).to(beFalse())
@ -155,7 +157,7 @@ class ConfigContactsSpec: QuickSpec {
.bytes
.map { CChar(bitPattern: $0) }
var contact5: contacts_contact = contacts_contact()
expect(contacts_get_or_create(conf2, &contact5, &anotherId)).to(beTrue())
expect(contacts_get_or_construct(conf2, &contact5, &anotherId)).to(beTrue())
expect(contact5.name).to(beNil())
expect(contact5.nickname).to(beNil())
expect(contact5.approved).to(beFalse())
@ -193,6 +195,8 @@ class ConfigContactsSpec: QuickSpec {
// Iterate through and make sure we got everything we expected
var sessionIds: [String] = []
var nicknames: [String] = []
expect(contacts_size(conf)).to(equal(2))
var contact6: contacts_contact = contacts_contact()
let contactIterator: UnsafeMutablePointer<contacts_iterator> = contacts_iterator_new(conf)
while !contacts_iterator_done(contactIterator, &contact6) {
@ -211,6 +215,7 @@ class ConfigContactsSpec: QuickSpec {
contacts_iterator_free(contactIterator) // Need to free the iterator
expect(sessionIds.count).to(equal(2))
expect(sessionIds.count).to(equal(contacts_size(conf)))
expect(sessionIds.first).to(equal(String(cString: definitelyRealId.nullTerminated())))
expect(sessionIds.last).to(equal(String(cString: anotherId.nullTerminated())))
expect(nicknames.first).to(equal("Joey"))
@ -233,7 +238,7 @@ class ConfigContactsSpec: QuickSpec {
.map { CChar(bitPattern: $0) }
let profileKey7: [UInt8] = "qwerty".bytes
var contact7: contacts_contact = contacts_contact()
expect(contacts_get_or_create(conf2, &contact7, &thirdId)).to(beTrue())
expect(contacts_get_or_construct(conf2, &contact7, &thirdId)).to(beTrue())
nickname7.withUnsafeBufferPointer { contact7.nickname = $0.baseAddress }
contact7.approved = true
contact7.approved_me = true
@ -297,6 +302,8 @@ class ConfigContactsSpec: QuickSpec {
// Validate the changes
var sessionIds2: [String] = []
var nicknames2: [String] = []
expect(contacts_size(conf)).to(equal(2))
var contact8: contacts_contact = contacts_contact()
let contactIterator2: UnsafeMutablePointer<contacts_iterator> = contacts_iterator_new(conf)
while !contacts_iterator_done(contactIterator2, &contact8) {

@ -27,9 +27,9 @@ class OpenGroupAPISpec: QuickSpec {
var dependencies: SMKDependencies!
var response: (ResponseInfoType, Codable)? = nil
var pollResponse: [OpenGroupAPI.Endpoint: (ResponseInfoType, Codable?)]?
var pollResponse: (info: ResponseInfoType, data: [OpenGroupAPI.Endpoint: Codable])?
var error: Error?
describe("an OpenGroupAPI") {
// MARK: - Configuration
@ -205,14 +205,16 @@ class OpenGroupAPISpec: QuickSpec {
expect(error?.localizedDescription).to(beNil())
// Validate the response data
expect(pollResponse?.values).to(haveCount(3))
expect(pollResponse?.keys).to(contain(.capabilities))
expect(pollResponse?.keys).to(contain(.roomPollInfo("testRoom", 0)))
expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom")))
expect(pollResponse?[.capabilities]?.0).to(beAKindOf(TestOnionRequestAPI.ResponseInfo.self))
expect(pollResponse?.data.count).to(equal(3))
expect(pollResponse?.data.keys).to(contain(.capabilities))
expect(pollResponse?.data.keys).to(contain(.roomPollInfo("testRoom", 0)))
expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom")))
expect(pollResponse?.data[.capabilities])
.to(beAKindOf(TestOnionRequestAPI.ResponseInfo.self))
// Validate request data
let requestData: TestOnionRequestAPI.RequestData? = (pollResponse?[.capabilities]?.0 as? TestOnionRequestAPI.ResponseInfo)?.requestData
let requestData: TestOnionRequestAPI.RequestData? = (pollResponse?.data[.capabilities] as? TestOnionRequestAPI.ResponseInfo)?.requestData
expect(requestData?.urlString).to(equal("testserver/batch"))
expect(requestData?.httpMethod).to(equal("POST"))
expect(requestData?.publicKey).to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
@ -241,7 +243,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom")))
expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom")))
}
it("retrieves recent messages if there was a last message and it has not performed the initial poll and the last message was too long ago") {
@ -272,7 +274,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.roomMessagesRecent("testRoom")))
expect(pollResponse?.data.keys).to(contain(.roomMessagesRecent("testRoom")))
}
it("retrieves recent messages if there was a last message and it has performed an initial poll but it was not too long ago") {
@ -303,7 +305,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123)))
expect(pollResponse?.data.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123)))
}
it("retrieves recent messages if there was a last message and there has already been a poll this session") {
@ -334,7 +336,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123)))
expect(pollResponse?.data.keys).to(contain(.roomMessagesSince("testRoom", seqNo: 123)))
}
context("when unblinded") {
@ -370,8 +372,8 @@ class OpenGroupAPISpec: QuickSpec {
expect(error?.localizedDescription).to(beNil())
// Validate the response data
expect(pollResponse?.keys).toNot(contain(.inbox))
expect(pollResponse?.keys).toNot(contain(.outbox))
expect(pollResponse?.data.keys).toNot(contain(.inbox))
expect(pollResponse?.data.keys).toNot(contain(.outbox))
}
}
@ -471,8 +473,8 @@ class OpenGroupAPISpec: QuickSpec {
expect(error?.localizedDescription).to(beNil())
// Validate the response data
expect(pollResponse?.keys).to(contain(.inbox))
expect(pollResponse?.keys).to(contain(.outbox))
expect(pollResponse?.data.keys).to(contain(.inbox))
expect(pollResponse?.data.keys).to(contain(.outbox))
}
it("retrieves recent inbox messages if there was no last message") {
@ -498,7 +500,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.inbox))
expect(pollResponse?.data.keys).to(contain(.inbox))
}
it("retrieves inbox messages since the last message if there was one") {
@ -529,7 +531,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.inboxSince(id: 124)))
expect(pollResponse?.data.keys).to(contain(.inboxSince(id: 124)))
}
it("retrieves recent outbox messages if there was no last message") {
@ -555,7 +557,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.outbox))
expect(pollResponse?.data.keys).to(contain(.outbox))
}
it("retrieves outbox messages since the last message if there was one") {
@ -586,7 +588,7 @@ class OpenGroupAPISpec: QuickSpec {
timeout: .milliseconds(100)
)
expect(error?.localizedDescription).to(beNil())
expect(pollResponse?.keys).to(contain(.outboxSince(id: 125)))
expect(pollResponse?.data.keys).to(contain(.outboxSince(id: 125)))
}
}
}
@ -650,9 +652,9 @@ class OpenGroupAPISpec: QuickSpec {
)
expect(error?.localizedDescription).to(beNil())
let capabilitiesResponse: HTTP.BatchSubResponse<OpenGroupAPI.Capabilities>? = (pollResponse?[.capabilities]?.1 as? HTTP.BatchSubResponse<OpenGroupAPI.Capabilities>)
let pollInfoResponse: HTTP.BatchSubResponse<OpenGroupAPI.RoomPollInfo>? = (pollResponse?[.roomPollInfo("testRoom", 0)]?.1 as? HTTP.BatchSubResponse<OpenGroupAPI.RoomPollInfo>)
let messagesResponse: HTTP.BatchSubResponse<[Failable<OpenGroupAPI.Message>]>? = (pollResponse?[.roomMessagesRecent("testRoom")]?.1 as? HTTP.BatchSubResponse<[Failable<OpenGroupAPI.Message>]>)
let capabilitiesResponse: HTTP.BatchSubResponse<OpenGroupAPI.Capabilities>? = (pollResponse?.data[.capabilities] as? HTTP.BatchSubResponse<OpenGroupAPI.Capabilities>)
let pollInfoResponse: HTTP.BatchSubResponse<OpenGroupAPI.RoomPollInfo>? = (pollResponse?.data[.roomPollInfo("testRoom", 0)] as? HTTP.BatchSubResponse<OpenGroupAPI.RoomPollInfo>)
let messagesResponse: HTTP.BatchSubResponse<[Failable<OpenGroupAPI.Message>]>? = (pollResponse?.data[.roomMessagesRecent("testRoom")] as? HTTP.BatchSubResponse<[Failable<OpenGroupAPI.Message>]>)
expect(capabilitiesResponse?.failedToParseBody).to(beFalse())
expect(pollInfoResponse?.failedToParseBody).to(beTrue())
expect(messagesResponse?.failedToParseBody).to(beTrue())

@ -56,11 +56,14 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
it("can encrypt correctly") {
let result = try? MessageSender.encryptWithSessionProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: SMKDependencies(storage: mockStorage)
)
let result = mockStorage.write { db in
try? MessageSender.encryptWithSessionProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: SMKDependencies(storage: mockStorage)
)
}
// Note: A Nonce is used for this so we can't compare the exact value when not mocked
expect(result).toNot(beNil())
@ -68,11 +71,14 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
it("returns the correct value when mocked") {
let result = try? MessageSender.encryptWithSessionProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
let result = mockStorage.write { db in
try? MessageSender.encryptWithSessionProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
}
expect(result?.bytes).to(equal([1, 2, 3]))
}
@ -83,51 +89,63 @@ class MessageSenderEncryptionSpec: QuickSpec {
_ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db)
}
expect {
try MessageSender.encryptWithSessionProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
}
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
it("throws an error if the signature generation fails") {
mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn(nil)
expect {
try MessageSender.encryptWithSessionProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
}
.to(throwError(MessageSenderError.signingFailed))
}
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if the encryption fails") {
mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn(nil)
expect {
try MessageSender.encryptWithSessionProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
using: dependencies
)
}
.to(throwError(MessageSenderError.encryptionFailed))
}
.to(throwError(MessageSenderError.encryptionFailed))
}
}
context("when encrypting with the blinded session protocol") {
it("successfully encrypts") {
let result = try? MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
let result = mockStorage.write { db in
try? MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
expect(result?.toHexString())
.to(equal(
@ -138,23 +156,29 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
it("includes a version at the start of the encrypted value") {
let result = try? MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
let result = mockStorage.write { db in
try? MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
expect(result?.toHexString().prefix(2)).to(equal("00"))
}
it("includes the nonce at the end of the encrypted value") {
let maybeResult = try? MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
let maybeResult = mockStorage.write { db in
try? MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
let result: [UInt8] = (maybeResult?.bytes ?? [])
let nonceBytes: [UInt8] = Array(result[max(0, (result.count - 24))..<result.count])
@ -163,15 +187,18 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
it("throws an error if the recipient isn't a blinded id") {
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "05\(TestConstants.publicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
.to(throwError(MessageSenderError.signingFailed))
}
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if there is no ed25519 keyPair") {
@ -180,15 +207,18 @@ class MessageSenderEncryptionSpec: QuickSpec {
_ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db)
}
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
it("throws an error if it fails to generate a blinded keyPair") {
@ -206,15 +236,18 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
.thenReturn(nil)
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
.to(throwError(MessageSenderError.signingFailed))
}
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if it fails to generate an encryption key") {
@ -248,15 +281,18 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
.thenReturn(nil)
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
.to(throwError(MessageSenderError.signingFailed))
}
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if it fails to encrypt") {
@ -267,15 +303,18 @@ class MessageSenderEncryptionSpec: QuickSpec {
.when { $0.encrypt(message: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
.thenReturn(nil)
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
"TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
mockStorage.write { db in
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
db,
plaintext: "TestMessage".data(using: .utf8)!,
for: "15\(TestConstants.blindedPublicKey)",
openGroupPublicKey: TestConstants.serverPublicKey,
using: dependencies
)
}
.to(throwError(MessageSenderError.encryptionFailed))
}
.to(throwError(MessageSenderError.encryptionFailed))
}
}
}

@ -183,11 +183,11 @@ class BatchResponseSpec: QuickSpec {
.sinkUntilComplete(
receiveValue: { result = $0 }
)
expect(result).toNot(beNil())
expect((result?[0].1 as? HTTP.BatchSubResponse<TestType>)?.body)
expect((result?.responses[0] as? HTTP.BatchSubResponse<TestType>)?.body)
.to(equal(testType))
expect((result?[1].1 as? HTTP.BatchSubResponse<TestType2>)?.body)
expect((result?.responses[1] as? HTTP.BatchSubResponse<TestType2>)?.body)
.to(equal(testType2))
}

Loading…
Cancel
Save