mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
16 KiB
Swift
294 lines
16 KiB
Swift
2 years ago
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||
|
|
||
|
import Foundation
|
||
|
import Sodium
|
||
|
import SessionUtil
|
||
|
import SessionUtilitiesKit
|
||
|
|
||
|
import Quick
|
||
|
import Nimble
|
||
|
|
||
|
/// This spec is designed to replicate the initial test cases for the libSession-util to ensure the behaviour matches
|
||
2 years ago
|
class ConfigContactsSpec {
|
||
2 years ago
|
// MARK: - Spec
|
||
|
|
||
2 years ago
|
static func spec() {
|
||
2 years ago
|
it("generates Contact configs correctly") {
|
||
|
let seed: Data = Data(hex: "0123456789abcdef0123456789abcdef")
|
||
2 years ago
|
|
||
2 years ago
|
// FIXME: Would be good to move these into the libSession-util instead of using Sodium separately
|
||
|
let identity = try! Identity.generate(from: seed)
|
||
|
var edSK: [UInt8] = identity.ed25519KeyPair.secretKey
|
||
|
expect(edSK.toHexString().suffix(64))
|
||
|
.to(equal("4cb76fdc6d32278e3f83dbf608360ecc6b65727934b85d2fb86862ff98c46ab7"))
|
||
|
expect(identity.x25519KeyPair.publicKey.toHexString())
|
||
|
.to(equal("d2ad010eeb72d72e561d9de7bd7b6989af77dcabffa03a5111a6c859ae5c3a72"))
|
||
|
expect(String(edSK.toHexString().prefix(32))).to(equal(seed.toHexString()))
|
||
2 years ago
|
|
||
2 years ago
|
// Initialize a brand new, empty config because we have no dump data to deal with.
|
||
|
let error: UnsafeMutablePointer<CChar>? = nil
|
||
|
var conf: UnsafeMutablePointer<config_object>? = nil
|
||
|
expect(contacts_init(&conf, &edSK, nil, 0, error)).to(equal(0))
|
||
|
error?.deallocate()
|
||
|
|
||
|
// Empty contacts shouldn't have an existing contact
|
||
2 years ago
|
let definitelyRealId: String = "050000000000000000000000000000000000000000000000000000000000000000"
|
||
2 years ago
|
var cDefinitelyRealId: [CChar] = definitelyRealId.cArray
|
||
2 years ago
|
let contactPtr: UnsafeMutablePointer<contacts_contact>? = nil
|
||
2 years ago
|
expect(contacts_get(conf, contactPtr, &cDefinitelyRealId)).to(beFalse())
|
||
2 years ago
|
|
||
2 years ago
|
expect(contacts_size(conf)).to(equal(0))
|
||
|
|
||
2 years ago
|
var contact2: contacts_contact = contacts_contact()
|
||
2 years ago
|
expect(contacts_get_or_construct(conf, &contact2, &cDefinitelyRealId)).to(beTrue())
|
||
|
expect(String(libSessionVal: contact2.name)).to(beEmpty())
|
||
|
expect(String(libSessionVal: contact2.nickname)).to(beEmpty())
|
||
2 years ago
|
expect(contact2.approved).to(beFalse())
|
||
|
expect(contact2.approved_me).to(beFalse())
|
||
|
expect(contact2.blocked).to(beFalse())
|
||
|
expect(contact2.profile_pic).toNot(beNil()) // Creates an empty instance apparently
|
||
2 years ago
|
expect(String(libSessionVal: contact2.profile_pic.url)).to(beEmpty())
|
||
2 years ago
|
|
||
2 years ago
|
expect(config_needs_push(conf)).to(beFalse())
|
||
|
expect(config_needs_dump(conf)).to(beFalse())
|
||
|
|
||
2 years ago
|
let pushData1: UnsafeMutablePointer<config_push_data> = config_push(conf)
|
||
|
expect(pushData1.pointee.seqno).to(equal(0))
|
||
|
pushData1.deallocate()
|
||
2 years ago
|
|
||
|
// Update the contact data
|
||
2 years ago
|
contact2.name = "Joe".toLibSession()
|
||
|
contact2.nickname = "Joey".toLibSession()
|
||
2 years ago
|
contact2.approved = true
|
||
|
contact2.approved_me = true
|
||
|
|
||
|
// Update the contact
|
||
|
contacts_set(conf, &contact2)
|
||
|
|
||
|
// Ensure the contact details were updated
|
||
|
var contact3: contacts_contact = contacts_contact()
|
||
2 years ago
|
expect(contacts_get(conf, &contact3, &cDefinitelyRealId)).to(beTrue())
|
||
2 years ago
|
expect(String(libSessionVal: contact3.name)).to(equal("Joe"))
|
||
|
expect(String(libSessionVal: contact3.nickname)).to(equal("Joey"))
|
||
2 years ago
|
expect(contact3.approved).to(beTrue())
|
||
|
expect(contact3.approved_me).to(beTrue())
|
||
|
expect(contact3.profile_pic).toNot(beNil()) // Creates an empty instance apparently
|
||
2 years ago
|
expect(String(libSessionVal: contact3.profile_pic.url)).to(beEmpty())
|
||
2 years ago
|
expect(contact3.blocked).to(beFalse())
|
||
2 years ago
|
expect(String(libSessionVal: contact3.session_id)).to(equal(definitelyRealId))
|
||
2 years ago
|
|
||
|
// Since we've made changes, we should need to push new config to the swarm, *and* should need
|
||
|
// to dump the updated state:
|
||
|
expect(config_needs_push(conf)).to(beTrue())
|
||
|
expect(config_needs_dump(conf)).to(beTrue())
|
||
|
|
||
|
// incremented since we made changes (this only increments once between
|
||
|
// dumps; even though we changed multiple fields here).
|
||
2 years ago
|
let pushData2: UnsafeMutablePointer<config_push_data> = config_push(conf)
|
||
|
|
||
|
// incremented since we made changes (this only increments once between
|
||
|
// dumps; even though we changed multiple fields here).
|
||
|
expect(pushData2.pointee.seqno).to(equal(1))
|
||
2 years ago
|
|
||
|
// Pretend we uploaded it
|
||
2 years ago
|
let fakeHash1: String = "fakehash1"
|
||
|
var cFakeHash1: [CChar] = fakeHash1.cArray
|
||
|
config_confirm_pushed(conf, pushData2.pointee.seqno, &cFakeHash1)
|
||
2 years ago
|
expect(config_needs_push(conf)).to(beFalse())
|
||
|
expect(config_needs_dump(conf)).to(beTrue())
|
||
2 years ago
|
pushData2.deallocate()
|
||
2 years ago
|
|
||
|
// NB: Not going to check encrypted data and decryption here because that's general (not
|
||
|
// specific to contacts) and is covered already in the user profile tests.
|
||
|
var dump1: UnsafeMutablePointer<UInt8>? = nil
|
||
|
var dump1Len: Int = 0
|
||
|
config_dump(conf, &dump1, &dump1Len)
|
||
|
|
||
|
let error2: UnsafeMutablePointer<CChar>? = nil
|
||
|
var conf2: UnsafeMutablePointer<config_object>? = nil
|
||
|
expect(contacts_init(&conf2, &edSK, dump1, dump1Len, error2)).to(equal(0))
|
||
2 years ago
|
error2?.deallocate()
|
||
2 years ago
|
dump1?.deallocate()
|
||
|
|
||
|
expect(config_needs_push(conf2)).to(beFalse())
|
||
|
expect(config_needs_dump(conf2)).to(beFalse())
|
||
|
|
||
2 years ago
|
let pushData3: UnsafeMutablePointer<config_push_data> = config_push(conf2)
|
||
|
expect(pushData3.pointee.seqno).to(equal(1))
|
||
|
pushData3.deallocate()
|
||
2 years ago
|
|
||
|
// Because we just called dump() above, to load up contacts2
|
||
|
expect(config_needs_dump(conf)).to(beFalse())
|
||
|
|
||
|
// Ensure the contact details were updated
|
||
|
var contact4: contacts_contact = contacts_contact()
|
||
2 years ago
|
expect(contacts_get(conf2, &contact4, &cDefinitelyRealId)).to(beTrue())
|
||
2 years ago
|
expect(String(libSessionVal: contact4.name)).to(equal("Joe"))
|
||
|
expect(String(libSessionVal: contact4.nickname)).to(equal("Joey"))
|
||
2 years ago
|
expect(contact4.approved).to(beTrue())
|
||
|
expect(contact4.approved_me).to(beTrue())
|
||
|
expect(contact4.profile_pic).toNot(beNil()) // Creates an empty instance apparently
|
||
2 years ago
|
expect(String(libSessionVal: contact4.profile_pic.url)).to(beEmpty())
|
||
2 years ago
|
expect(contact4.blocked).to(beFalse())
|
||
|
|
||
2 years ago
|
let anotherId: String = "051111111111111111111111111111111111111111111111111111111111111111"
|
||
2 years ago
|
var cAnotherId: [CChar] = anotherId.cArray
|
||
2 years ago
|
var contact5: contacts_contact = contacts_contact()
|
||
2 years ago
|
expect(contacts_get_or_construct(conf2, &contact5, &cAnotherId)).to(beTrue())
|
||
|
expect(String(libSessionVal: contact5.name)).to(beEmpty())
|
||
|
expect(String(libSessionVal: contact5.nickname)).to(beEmpty())
|
||
2 years ago
|
expect(contact5.approved).to(beFalse())
|
||
|
expect(contact5.approved_me).to(beFalse())
|
||
|
expect(contact5.profile_pic).toNot(beNil()) // Creates an empty instance apparently
|
||
2 years ago
|
expect(String(libSessionVal: contact5.profile_pic.url)).to(beEmpty())
|
||
2 years ago
|
expect(contact5.blocked).to(beFalse())
|
||
|
|
||
|
// We're not setting any fields, but we should still keep a record of the session id
|
||
|
contacts_set(conf2, &contact5)
|
||
|
expect(config_needs_push(conf2)).to(beTrue())
|
||
|
|
||
2 years ago
|
let pushData4: UnsafeMutablePointer<config_push_data> = config_push(conf2)
|
||
|
expect(pushData4.pointee.seqno).to(equal(2))
|
||
2 years ago
|
|
||
|
// Check the merging
|
||
2 years ago
|
let fakeHash2: String = "fakehash2"
|
||
|
var cFakeHash2: [CChar] = fakeHash2.cArray
|
||
|
var mergeHashes: [UnsafePointer<CChar>?] = [cFakeHash2].unsafeCopy()
|
||
|
var mergeData: [UnsafePointer<UInt8>?] = [UnsafePointer(pushData4.pointee.config)]
|
||
|
var mergeSize: [Int] = [pushData4.pointee.config_len]
|
||
|
expect(config_merge(conf, &mergeHashes, &mergeData, &mergeSize, 1)).to(equal(1))
|
||
|
config_confirm_pushed(conf2, pushData4.pointee.seqno, &cFakeHash2)
|
||
|
mergeHashes.forEach { $0?.deallocate() }
|
||
|
pushData4.deallocate()
|
||
2 years ago
|
|
||
|
expect(config_needs_push(conf)).to(beFalse())
|
||
|
|
||
2 years ago
|
let pushData5: UnsafeMutablePointer<config_push_data> = config_push(conf)
|
||
|
expect(pushData5.pointee.seqno).to(equal(2))
|
||
|
pushData5.deallocate()
|
||
|
|
||
2 years ago
|
// Iterate through and make sure we got everything we expected
|
||
|
var sessionIds: [String] = []
|
||
|
var nicknames: [String] = []
|
||
2 years ago
|
expect(contacts_size(conf)).to(equal(2))
|
||
|
|
||
2 years ago
|
var contact6: contacts_contact = contacts_contact()
|
||
|
let contactIterator: UnsafeMutablePointer<contacts_iterator> = contacts_iterator_new(conf)
|
||
|
while !contacts_iterator_done(contactIterator, &contact6) {
|
||
2 years ago
|
sessionIds.append(String(libSessionVal: contact6.session_id))
|
||
2 years ago
|
nicknames.append(String(libSessionVal: contact6.nickname, nullIfEmpty: true) ?? "(N/A)")
|
||
2 years ago
|
contacts_iterator_advance(contactIterator)
|
||
|
}
|
||
|
contacts_iterator_free(contactIterator) // Need to free the iterator
|
||
|
|
||
|
expect(sessionIds.count).to(equal(2))
|
||
2 years ago
|
expect(sessionIds.count).to(equal(contacts_size(conf)))
|
||
2 years ago
|
expect(sessionIds.first).to(equal(definitelyRealId))
|
||
|
expect(sessionIds.last).to(equal(anotherId))
|
||
2 years ago
|
expect(nicknames.first).to(equal("Joey"))
|
||
|
expect(nicknames.last).to(equal("(N/A)"))
|
||
2 years ago
|
|
||
2 years ago
|
// Conflict! Oh no!
|
||
2 years ago
|
|
||
2 years ago
|
// On client 1 delete a contact:
|
||
|
contacts_erase(conf, definitelyRealId)
|
||
|
|
||
|
// Client 2 adds a new friend:
|
||
2 years ago
|
let thirdId: String = "052222222222222222222222222222222222222222222222222222222222222222"
|
||
2 years ago
|
var cThirdId: [CChar] = thirdId.cArray
|
||
2 years ago
|
var contact7: contacts_contact = contacts_contact()
|
||
2 years ago
|
expect(contacts_get_or_construct(conf2, &contact7, &cThirdId)).to(beTrue())
|
||
|
contact7.nickname = "Nickname 3".toLibSession()
|
||
2 years ago
|
contact7.approved = true
|
||
|
contact7.approved_me = true
|
||
2 years ago
|
contact7.profile_pic.url = "http://example.com/huge.bmp".toLibSession()
|
||
|
contact7.profile_pic.key = "qwerty78901234567890123456789012".data(using: .utf8)!.toLibSession()
|
||
2 years ago
|
contacts_set(conf2, &contact7)
|
||
|
|
||
|
expect(config_needs_push(conf)).to(beTrue())
|
||
|
expect(config_needs_push(conf2)).to(beTrue())
|
||
|
|
||
2 years ago
|
let pushData6: UnsafeMutablePointer<config_push_data> = config_push(conf)
|
||
|
expect(pushData6.pointee.seqno).to(equal(3))
|
||
|
|
||
|
let pushData7: UnsafeMutablePointer<config_push_data> = config_push(conf2)
|
||
|
expect(pushData7.pointee.seqno).to(equal(3))
|
||
|
|
||
|
let pushData6Str: String = String(pointer: pushData6.pointee.config, length: pushData6.pointee.config_len, encoding: .ascii)!
|
||
|
let pushData7Str: String = String(pointer: pushData7.pointee.config, length: pushData7.pointee.config_len, encoding: .ascii)!
|
||
|
expect(pushData6Str).toNot(equal(pushData7Str))
|
||
|
expect([String](pointer: pushData6.pointee.obsolete, count: pushData6.pointee.obsolete_len))
|
||
|
.to(equal([fakeHash2]))
|
||
|
expect([String](pointer: pushData7.pointee.obsolete, count: pushData7.pointee.obsolete_len))
|
||
|
.to(equal([fakeHash2]))
|
||
|
|
||
|
let fakeHash3a: String = "fakehash3a"
|
||
|
var cFakeHash3a: [CChar] = fakeHash3a.cArray
|
||
|
let fakeHash3b: String = "fakehash3b"
|
||
|
var cFakeHash3b: [CChar] = fakeHash3b.cArray
|
||
|
config_confirm_pushed(conf, pushData6.pointee.seqno, &cFakeHash3a)
|
||
|
config_confirm_pushed(conf2, pushData7.pointee.seqno, &cFakeHash3b)
|
||
|
|
||
|
var mergeHashes2: [UnsafePointer<CChar>?] = [cFakeHash3b].unsafeCopy()
|
||
|
var mergeData2: [UnsafePointer<UInt8>?] = [UnsafePointer(pushData7.pointee.config)]
|
||
|
var mergeSize2: [Int] = [pushData7.pointee.config_len]
|
||
|
expect(config_merge(conf, &mergeHashes2, &mergeData2, &mergeSize2, 1)).to(equal(1))
|
||
2 years ago
|
expect(config_needs_push(conf)).to(beTrue())
|
||
|
|
||
2 years ago
|
var mergeHashes3: [UnsafePointer<CChar>?] = [cFakeHash3a].unsafeCopy()
|
||
|
var mergeData3: [UnsafePointer<UInt8>?] = [UnsafePointer(pushData6.pointee.config)]
|
||
|
var mergeSize3: [Int] = [pushData6.pointee.config_len]
|
||
|
expect(config_merge(conf2, &mergeHashes3, &mergeData3, &mergeSize3, 1)).to(equal(1))
|
||
2 years ago
|
expect(config_needs_push(conf2)).to(beTrue())
|
||
2 years ago
|
mergeHashes2.forEach { $0?.deallocate() }
|
||
|
mergeHashes3.forEach { $0?.deallocate() }
|
||
|
pushData6.deallocate()
|
||
|
pushData7.deallocate()
|
||
|
|
||
|
let pushData8: UnsafeMutablePointer<config_push_data> = config_push(conf)
|
||
|
expect(pushData8.pointee.seqno).to(equal(4))
|
||
|
|
||
|
let pushData9: UnsafeMutablePointer<config_push_data> = config_push(conf2)
|
||
|
expect(pushData9.pointee.seqno).to(equal(pushData8.pointee.seqno))
|
||
|
|
||
|
let pushData8Str: String = String(pointer: pushData8.pointee.config, length: pushData8.pointee.config_len, encoding: .ascii)!
|
||
|
let pushData9Str: String = String(pointer: pushData9.pointee.config, length: pushData9.pointee.config_len, encoding: .ascii)!
|
||
|
expect(pushData8Str).to(equal(pushData9Str))
|
||
|
expect([String](pointer: pushData8.pointee.obsolete, count: pushData8.pointee.obsolete_len))
|
||
|
.to(equal([fakeHash3b, fakeHash3a]))
|
||
|
expect([String](pointer: pushData9.pointee.obsolete, count: pushData9.pointee.obsolete_len))
|
||
|
.to(equal([fakeHash3a, fakeHash3b]))
|
||
|
|
||
|
let fakeHash4: String = "fakeHash4"
|
||
|
var cFakeHash4: [CChar] = fakeHash4.cArray
|
||
|
config_confirm_pushed(conf, pushData8.pointee.seqno, &cFakeHash4)
|
||
|
config_confirm_pushed(conf2, pushData9.pointee.seqno, &cFakeHash4)
|
||
|
pushData8.deallocate()
|
||
|
pushData9.deallocate()
|
||
2 years ago
|
|
||
|
expect(config_needs_push(conf)).to(beFalse())
|
||
|
expect(config_needs_push(conf2)).to(beFalse())
|
||
|
|
||
|
// Validate the changes
|
||
|
var sessionIds2: [String] = []
|
||
|
var nicknames2: [String] = []
|
||
2 years ago
|
expect(contacts_size(conf)).to(equal(2))
|
||
2 years ago
|
|
||
2 years ago
|
var contact8: contacts_contact = contacts_contact()
|
||
|
let contactIterator2: UnsafeMutablePointer<contacts_iterator> = contacts_iterator_new(conf)
|
||
|
while !contacts_iterator_done(contactIterator2, &contact8) {
|
||
2 years ago
|
sessionIds2.append(String(libSessionVal: contact8.session_id))
|
||
2 years ago
|
nicknames2.append(String(libSessionVal: contact8.nickname, nullIfEmpty: true) ?? "(N/A)")
|
||
2 years ago
|
contacts_iterator_advance(contactIterator2)
|
||
|
}
|
||
|
contacts_iterator_free(contactIterator2) // Need to free the iterator
|
||
|
|
||
|
expect(sessionIds2.count).to(equal(2))
|
||
2 years ago
|
expect(sessionIds2.first).to(equal(anotherId))
|
||
|
expect(sessionIds2.last).to(equal(thirdId))
|
||
2 years ago
|
expect(nicknames2.first).to(equal("(N/A)"))
|
||
|
expect(nicknames2.last).to(equal("Nickname 3"))
|
||
|
}
|
||
|
}
|
||
|
}
|