Started resolving more TODOs and fixing up unit tests

Updated the Dependencies to lazily create their fallback instances (optimisation to avoid unnecessary initialisations)
Reworked the Atomic type again to have a more consistent behaviour across types
Fixed the build issues with the unit tests (haven't fixed broken tests yet)
pull/592/head
Morgan Pretty 3 years ago
parent 3a75639285
commit bdf5b3bc1b

@ -15,8 +15,8 @@ public final class OpenGroupAPI: NSObject {
// MARK: - Polling State // MARK: - Polling State
private static var hasPerformedInitialPoll: AtomicDict<String, Bool> = AtomicDict() private static var hasPerformedInitialPoll: Atomic<[String: Bool]> = Atomic([:])
private static var timeSinceLastPoll: AtomicDict<String, TimeInterval> = AtomicDict() private static var timeSinceLastPoll: Atomic<[String: TimeInterval]> = Atomic([:])
private static var lastPollTime: Atomic<TimeInterval> = Atomic(.greatestFiniteMagnitude) private static var lastPollTime: Atomic<TimeInterval> = Atomic(.greatestFiniteMagnitude)
private static let timeSinceLastOpen: Atomic<TimeInterval> = { private static let timeSinceLastOpen: Atomic<TimeInterval> = {
@ -27,7 +27,7 @@ public final class OpenGroupAPI: NSObject {
// TODO: Remove these // TODO: Remove these
private static var legacyAuthTokenPromises: AtomicDict<String, Promise<String>> = AtomicDict() private static var legacyAuthTokenPromises: Atomic<[String: Promise<String>]> = Atomic([:])
private static var legacyHasUpdatedLastOpenDate = false private static var legacyHasUpdatedLastOpenDate = false
private static var legacyGroupImagePromises: [String: Promise<Data>] = [:] private static var legacyGroupImagePromises: [String: Promise<Data>] = [:]
@ -43,14 +43,14 @@ public final class OpenGroupAPI: NSObject {
/// - Inbox for the server /// - Inbox for the server
public static func poll(_ server: String, using dependencies: Dependencies = Dependencies()) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> { public static func poll(_ server: String, using dependencies: Dependencies = Dependencies()) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> {
// Store a local copy of the cached state for this server // Store a local copy of the cached state for this server
let hadPerformedInitialPoll: Bool = (hasPerformedInitialPoll[server] == true) let hadPerformedInitialPoll: Bool = (hasPerformedInitialPoll.wrappedValue[server] == true)
let originalTimeSinceLastPoll: TimeInterval = (timeSinceLastPoll[server] ?? min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue)) let originalTimeSinceLastPoll: TimeInterval = (timeSinceLastPoll.wrappedValue[server] ?? min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue))
let maybeLastInboxMessageId: Int64? = dependencies.storage.getOpenGroupInboxLatestMessageId(for: server) let maybeLastInboxMessageId: Int64? = dependencies.storage.getOpenGroupInboxLatestMessageId(for: server)
let lastInboxMessageId: Int64 = (maybeLastInboxMessageId ?? 0) let lastInboxMessageId: Int64 = (maybeLastInboxMessageId ?? 0)
// Update the cached state for this server // Update the cached state for this server
hasPerformedInitialPoll.wrappedValue[server] = true hasPerformedInitialPoll.mutate { $0[server] = true }
lastPollTime.wrappedValue = min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue) lastPollTime.mutate { $0 = min($0, timeSinceLastOpen.wrappedValue)}
UserDefaults.standard[.lastOpen] = Date() UserDefaults.standard[.lastOpen] = Date()
// Generate the requests // Generate the requests
@ -58,13 +58,13 @@ public final class OpenGroupAPI: NSObject {
BatchRequestInfo( BatchRequestInfo(
request: Request<NoBody>( request: Request<NoBody>(
server: server, server: server,
endpoint: .capabilities, endpoint: .capabilities
queryParameters: [:] // TODO: Add any requirements '.required'
), ),
responseType: Capabilities.self responseType: Capabilities.self
) )
] ]
.appending( .appending(
// Per-room requests
dependencies.storage.getAllOpenGroups().values dependencies.storage.getAllOpenGroups().values
.filter { $0.server == server.lowercased() } // Note: The `OpenGroup` type converts to lowercase in init .filter { $0.server == server.lowercased() } // Note: The `OpenGroup` type converts to lowercase in init
.flatMap { openGroup -> [BatchRequestInfoType] in .flatMap { openGroup -> [BatchRequestInfoType] in

@ -4,42 +4,94 @@ import Foundation
import Sodium import Sodium
import SessionSnodeKit import SessionSnodeKit
// MARK: - Dependencies
extension OpenGroupAPI { extension OpenGroupAPI {
public struct Dependencies { public class Dependencies {
let api: OnionRequestAPIType.Type private var _api: OnionRequestAPIType.Type?
let storage: SessionMessagingKitStorageProtocol var api: OnionRequestAPIType.Type {
let sodium: SodiumType get { getValueSettingIfNull(&_api) { OnionRequestAPI.self } }
let aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType set { _api = newValue }
let sign: SignType }
let genericHash: GenericHashType
let ed25519: Ed25519Type.Type private var _storage: SessionMessagingKitStorageProtocol?
let nonceGenerator16: NonceGenerator16ByteType var storage: SessionMessagingKitStorageProtocol {
let nonceGenerator24: NonceGenerator24ByteType get { getValueSettingIfNull(&_storage) { SNMessagingKitConfiguration.shared.storage } }
let date: Date set { _storage = newValue }
}
private var _sodium: SodiumType?
var sodium: SodiumType {
get { getValueSettingIfNull(&_sodium) { Sodium() } }
set { _sodium = newValue }
}
private var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType?
var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {
get { getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } }
set { _aeadXChaCha20Poly1305Ietf = newValue }
}
private var _sign: SignType?
var sign: SignType {
get { getValueSettingIfNull(&_sign) { sodium.getSign() } }
set { _sign = newValue }
}
private var _genericHash: GenericHashType?
var genericHash: GenericHashType {
get { getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } }
set { _genericHash = newValue }
}
private var _ed25519: Ed25519Type.Type?
var ed25519: Ed25519Type.Type {
get { getValueSettingIfNull(&_ed25519) { Ed25519.self } }
set { _ed25519 = newValue }
}
private var _nonceGenerator16: NonceGenerator16ByteType?
var nonceGenerator16: NonceGenerator16ByteType {
get { getValueSettingIfNull(&_nonceGenerator16) { NonceGenerator16Byte() } }
set { _nonceGenerator16 = newValue }
}
private var _nonceGenerator24: NonceGenerator24ByteType?
var nonceGenerator24: NonceGenerator24ByteType {
get { getValueSettingIfNull(&_nonceGenerator24) { NonceGenerator24Byte() } }
set { _nonceGenerator24 = newValue }
}
private var _date: Date?
var date: Date {
get { getValueSettingIfNull(&_date) { Date() } }
set { _date = newValue }
}
// MARK: - Initialization
public init( public init(
api: OnionRequestAPIType.Type = OnionRequestAPI.self, api: OnionRequestAPIType.Type? = nil,
storage: SessionMessagingKitStorageProtocol = SNMessagingKitConfiguration.shared.storage, storage: SessionMessagingKitStorageProtocol? = nil,
// TODO: Shift the next 3 to be abstracted behind a single "signing" class? sodium: SodiumType? = nil,
sodium: SodiumType = Sodium(),
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil, aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
sign: SignType? = nil, sign: SignType? = nil,
genericHash: GenericHashType? = nil, genericHash: GenericHashType? = nil,
ed25519: Ed25519Type.Type = Ed25519.self, ed25519: Ed25519Type.Type? = nil,
nonceGenerator16: NonceGenerator16ByteType = NonceGenerator16Byte(), nonceGenerator16: NonceGenerator16ByteType? = nil,
nonceGenerator24: NonceGenerator24ByteType = NonceGenerator24Byte(), nonceGenerator24: NonceGenerator24ByteType? = nil,
date: Date = Date() date: Date? = nil
) { ) {
self.api = api _api = api
self.storage = storage _storage = storage
self.sodium = sodium _sodium = sodium
self.aeadXChaCha20Poly1305Ietf = (aeadXChaCha20Poly1305Ietf ?? sodium.getAeadXChaCha20Poly1305Ietf()) _aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf
self.sign = (sign ?? sodium.getSign()) _sign = sign
self.genericHash = (genericHash ?? sodium.getGenericHash()) _genericHash = genericHash
self.ed25519 = ed25519 _ed25519 = ed25519
self.nonceGenerator16 = nonceGenerator16 _nonceGenerator16 = nonceGenerator16
self.nonceGenerator24 = nonceGenerator24 _nonceGenerator24 = nonceGenerator24
self.date = date _date = date
} }
// MARK: - Convenience // MARK: - Convenience
@ -57,17 +109,29 @@ extension OpenGroupAPI {
date: Date? = nil date: Date? = nil
) -> Dependencies { ) -> Dependencies {
return Dependencies( return Dependencies(
api: (api ?? self.api), api: (api ?? self._api),
storage: (storage ?? self.storage), storage: (storage ?? self._storage),
sodium: (sodium ?? self.sodium), sodium: (sodium ?? self._sodium),
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self.aeadXChaCha20Poly1305Ietf), aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
sign: (sign ?? self.sign), sign: (sign ?? self._sign),
genericHash: (genericHash ?? self.genericHash), genericHash: (genericHash ?? self._genericHash),
ed25519: (ed25519 ?? self.ed25519), ed25519: (ed25519 ?? self._ed25519),
nonceGenerator16: (nonceGenerator16 ?? self.nonceGenerator16), nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
nonceGenerator24: (nonceGenerator24 ?? self.nonceGenerator24), nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
date: (date ?? self.date) date: (date ?? self._date)
) )
} }
} }
} }
// MARK: - Convenience
fileprivate func getValueSettingIfNull<T>(_ maybeValue: inout T?, _ valueGenerator: () -> T) -> T {
guard let value: T = maybeValue else {
let value: T = valueGenerator()
maybeValue = value
return value
}
return value
}

@ -2,94 +2,45 @@
import Foundation import Foundation
/// See https://www.donnywals.com/why-your-atomic-property-wrapper-doesnt-work-for-collection-types/
/// for more information about the below types
protocol UnsupportedType {}
extension Array: UnsupportedType {}
extension Set: UnsupportedType {}
extension Dictionary: UnsupportedType {}
// MARK: - Atomic<Value> // MARK: - Atomic<Value>
/// The `Atomic<Value>` wrapper is a generic wrapper providing a thread-safe way to get and set a value /// The `Atomic<Value>` wrapper is a generic wrapper providing a thread-safe way to get and set a value
///
/// A write-up on the need for this class and it's approach can be found here:
/// https://www.vadimbulavin.com/swift-atomic-properties-with-property-wrappers/
/// there is also another approach which can be taken but it requires separate types for collections and results in
/// a somewhat inconsistent interface between different `Atomic` wrappers
@propertyWrapper @propertyWrapper
struct Atomic<Value> { public class Atomic<Value> {
private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global()) private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)")
private var value: Value private var value: Value
init(_ initialValue: Value) { /// In order to change the value you **must** use the `mutate` function
if initialValue is UnsupportedType { preconditionFailure("Use the appropriate Aromic... type for collections") } public var wrappedValue: Value {
return queue.sync { return value }
self.value = initialValue
} }
var wrappedValue: Value { /// For more information see https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#projections
get { return queue.sync { return value } } public var projectedValue: Atomic<Value> {
set { return queue.sync { value = newValue } } return self
} }
}
extension Atomic where Value: CustomDebugStringConvertible { // MARK: - Initialization
var debugDescription: String {
return value.debugDescription
}
}
// MARK: - AtomicArray<Value>
/// The `AtomicArray<Value>` wrapper is a generic wrapper providing a thread-safe way to get and set an array or one of it's values
///
/// Note: This is a class rather than a struct as you need to modify a reference rather than a copy for the concurrency to work
@propertyWrapper
class AtomicArray<Value>: CustomDebugStringConvertible {
private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global())
private var value: [Value]
init(_ initialValue: [Value] = []) { init(_ initialValue: Value) {
self.value = initialValue self.value = initialValue
} }
var wrappedValue: [Value] { // MARK: - Functions
get { return queue.sync { return value } }
set { return queue.sync { value = newValue } }
}
subscript(index: Int) -> Value { func mutate(_ mutation: (inout Value) -> Void) {
get { queue.sync { value[index] }} return queue.sync {
set { queue.async(flags: .barrier) { self.value[index] = newValue } } mutation(&value)
} }
public var debugDescription: String {
return value.debugDescription
} }
} }
// MARK: - AtomicDict<Key, Value> extension Atomic where Value: CustomDebugStringConvertible {
/// The `AtomicDict<Key, Value>` wrapper is a generic wrapper providing a thread-safe way to get and set a dictionaries or one of it's values
///
/// Note: This is a class rather than a struct as you need to modify a reference rather than a copy for the concurrency to work
@propertyWrapper
class AtomicDict<Key: Hashable, Value>: CustomDebugStringConvertible {
private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global())
private var value: [Key: Value]
init(_ initialValue: [Key: Value] = [:]) {
self.value = initialValue
}
var wrappedValue: [Key: Value] {
get { return queue.sync { return value } }
set { return queue.sync { value = newValue } }
}
subscript(key: Key) -> Value? {
get { queue.sync { value[key] }}
set { queue.async(flags: .barrier) { self.value[key] = newValue } }
}
var debugDescription: String { var debugDescription: String {
return value.debugDescription return value.debugDescription
} }

@ -21,10 +21,18 @@ class OpenGroupAPITests: XCTestCase {
} }
} }
struct TestNonceGenerator: NonceGenerator16ByteType { struct TestNonce16Generator: NonceGenerator16ByteType {
var NonceBytes: Int = 16
func nonce() -> Array<UInt8> { return Data(base64Encoded: "pK6YRtQApl4NhECGizF0Cg==")!.bytes } func nonce() -> Array<UInt8> { return Data(base64Encoded: "pK6YRtQApl4NhECGizF0Cg==")!.bytes }
} }
struct TestNonce24Generator: NonceGenerator24ByteType {
var NonceBytes: Int = 24
func nonce() -> Array<UInt8> { return Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes }
}
class TestApi: OnionRequestAPIType { class TestApi: OnionRequestAPIType {
struct RequestData: Codable { struct RequestData: Codable {
let urlString: String? let urlString: String?
@ -89,7 +97,8 @@ class OpenGroupAPITests: XCTestCase {
sign: testSign, sign: testSign,
genericHash: testGenericHash, genericHash: testGenericHash,
ed25519: TestEd25519.self, ed25519: TestEd25519.self,
nonceGenerator: TestNonceGenerator(), nonceGenerator16: TestNonce16Generator(),
nonceGenerator24: TestNonce24Generator(),
date: Date(timeIntervalSince1970: 1234567890) date: Date(timeIntervalSince1970: 1234567890)
) )
@ -142,21 +151,35 @@ class OpenGroupAPITests: XCTestCase {
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil) body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil),
failedToParseBody: false
) )
), ),
try! JSONEncoder().encode( try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: "{}".data(using: .utf8)!) body: try! JSONDecoder().decode(
OpenGroupAPI.RoomPollInfo.self,
from: """
{
\"token\":\"test\",
\"active_users\":1,
\"read\":true,
\"write\":true,
\"upload\":true
}
""".data(using: .utf8)!
),
failedToParseBody: false
) )
), ),
try! JSONEncoder().encode( try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: [OpenGroupAPI.Message]() body: [OpenGroupAPI.Message](),
failedToParseBody: false
) )
) )
] ]
@ -166,7 +189,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -197,7 +220,7 @@ class OpenGroupAPITests: XCTestCase {
} }
func testPollReturnsAnErrorWhenGivenNoData() throws { func testPollReturnsAnErrorWhenGivenNoData() throws {
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -220,7 +243,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -243,7 +266,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -266,7 +289,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -291,14 +314,27 @@ class OpenGroupAPITests: XCTestCase {
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil) body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil),
failedToParseBody: false
) )
), ),
try! JSONEncoder().encode( try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: "{}".data(using: .utf8)!) body: try! JSONDecoder().decode(
OpenGroupAPI.RoomPollInfo.self,
from: """
{
\"token\":\"test\",
\"active_users\":1,
\"read\":true,
\"write\":true,
\"upload\":true
}
""".data(using: .utf8)!
),
failedToParseBody: false
) )
) )
] ]
@ -308,7 +344,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -333,21 +369,24 @@ class OpenGroupAPITests: XCTestCase {
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""),
failedToParseBody: false
) )
), ),
try! JSONEncoder().encode( try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""),
failedToParseBody: false
) )
), ),
try! JSONEncoder().encode( try! JSONEncoder().encode(
OpenGroupAPI.BatchSubResponse( OpenGroupAPI.BatchSubResponse(
code: 200, code: 200,
headers: [:], headers: [:],
body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""),
failedToParseBody: false
) )
) )
] ]
@ -357,7 +396,7 @@ class OpenGroupAPITests: XCTestCase {
} }
dependencies = dependencies.with(api: LocalTestApi.self) dependencies = dependencies.with(api: LocalTestApi.self)
var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil
var error: Error? = nil var error: Error? = nil
OpenGroupAPI.poll("testServer", using: dependencies) OpenGroupAPI.poll("testServer", using: dependencies)
@ -566,6 +605,7 @@ class OpenGroupAPITests: XCTestCase {
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf } func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf }
func getSign() -> SignType { return Sodium().sign } func getSign() -> SignType { return Sodium().sign }
func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return nil }
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
return nil return nil
} }
@ -573,7 +613,14 @@ class OpenGroupAPITests: XCTestCase {
return nil return nil
} }
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil } func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? { return nil }
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
return nil
}
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
return false
}
} }
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
name: "testServer", name: "testServer",
@ -604,6 +651,7 @@ class OpenGroupAPITests: XCTestCase {
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf } func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf }
func getSign() -> SignType { return Sodium().sign } func getSign() -> SignType { return Sodium().sign }
func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return nil }
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
return Box.KeyPair( return Box.KeyPair(
publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes, publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes,
@ -614,7 +662,14 @@ class OpenGroupAPITests: XCTestCase {
return nil return nil
} }
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil } func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? { return nil }
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
return nil
}
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
return false
}
} }
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
name: "testServer", name: "testServer",
@ -641,8 +696,11 @@ class OpenGroupAPITests: XCTestCase {
func testItFailsToSignIfUnblindedAndTheSignatureDoesNotGetGenerated() throws { func testItFailsToSignIfUnblindedAndTheSignatureDoesNotGetGenerated() throws {
class InvalidSign: SignType { class InvalidSign: SignType {
var PublicKeyBytes: Int = 32
func signature(message: Bytes, secretKey: Bytes) -> Bytes? { return nil } func signature(message: Bytes, secretKey: Bytes) -> Bytes? { return nil }
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { return false } func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { return false }
func toX25519(ed25519PublicKey: Bytes) -> Bytes? { return nil }
} }
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
name: "testServer", name: "testServer",

@ -7,10 +7,14 @@ import Sodium
@testable import SessionMessagingKit @testable import SessionMessagingKit
class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable { class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable {
var KeyBytes: Int = 32
var ABytes: Int = 16
// MARK: - Mockable // MARK: - Mockable
enum DataKey: Hashable { enum DataKey: Hashable {
case encrypt case encrypt
case decrypt
} }
typealias Key = DataKey typealias Key = DataKey
@ -19,7 +23,11 @@ class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable {
// MARK: - SignType // MARK: - SignType
func encrypt(message: Bytes, secretKey: Aead.XChaCha20Poly1305Ietf.Key, additionalData: Bytes?) -> (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)? { func encrypt(message: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? {
return (mockData[.encrypt] as? (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)) return (mockData[.encrypt] as? Bytes)
}
func decrypt(authenticatedCipherText: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? {
return (mockData[.decrypt] as? Bytes)
} }
} }

@ -7,11 +7,14 @@ import Sodium
@testable import SessionMessagingKit @testable import SessionMessagingKit
class TestSign: SignType, Mockable { class TestSign: SignType, Mockable {
var PublicKeyBytes: Int = 32
// MARK: - Mockable // MARK: - Mockable
enum DataKey: Hashable { enum DataKey: Hashable {
case signature case signature
case verify case verify
case toX25519
} }
typealias Key = DataKey typealias Key = DataKey
@ -27,4 +30,8 @@ class TestSign: SignType, Mockable {
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool {
return (mockData[.verify] as! Bool) return (mockData[.verify] as! Bool)
} }
func toX25519(ed25519PublicKey: Bytes) -> Bytes? {
return (mockData[.toX25519] as? Bytes)
}
} }

@ -13,9 +13,12 @@ class TestSodium: SodiumType, Mockable {
case genericHash case genericHash
case aeadXChaCha20Poly1305Ietf case aeadXChaCha20Poly1305Ietf
case sign case sign
case blindingFactor
case blindedKeyPair case blindedKeyPair
case sogsSignature case sogsSignature
case sharedEdSecret case combinedKeys
case sharedBlindedEncryptionKey
case sessionIdMatches
} }
typealias Key = DataKey typealias Key = DataKey
@ -32,6 +35,8 @@ class TestSodium: SodiumType, Mockable {
func getSign() -> SignType { return (mockData[.sign] as! SignType) } func getSign() -> SignType { return (mockData[.sign] as! SignType) }
func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return (mockData[.blindingFactor] as? Bytes) }
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
return (mockData[.blindedKeyPair] as? Box.KeyPair) return (mockData[.blindedKeyPair] as? Box.KeyPair)
} }
@ -40,7 +45,15 @@ class TestSodium: SodiumType, Mockable {
return (mockData[.sogsSignature] as? Bytes) return (mockData[.sogsSignature] as? Bytes)
} }
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? {
return (mockData[.sharedEdSecret] as? Bytes) return (mockData[.combinedKeys] as? Bytes)
}
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
return (mockData[.sharedBlindedEncryptionKey] as? Bytes)
}
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
return ((mockData[.sessionIdMatches] as? Bool) ?? false)
} }
} }

@ -18,6 +18,8 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
case openGroupServer case openGroupServer
case openGroupImage case openGroupImage
case openGroupUserCount case openGroupUserCount
case openGroupSequenceNumber
case openGroupLatestMessageId
} }
typealias Key = DataKey typealias Key = DataKey
@ -47,6 +49,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
func getUserED25519KeyPair() -> Box.KeyPair? { return (mockData[.userEdKeyPair] as? Box.KeyPair) } func getUserED25519KeyPair() -> Box.KeyPair? { return (mockData[.userEdKeyPair] as? Box.KeyPair) }
func getUser() -> Contact? { return nil } func getUser() -> Contact? { return nil }
func getAllContacts() -> Set<Contact> { return Set() } func getAllContacts() -> Set<Contact> { return Set() }
func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set<Contact> { return Set() }
// MARK: - Closed Groups // MARK: - Closed Groups
@ -66,12 +69,6 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) {} func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) {}
func isJobCanceled(_ job: Job) -> Bool { return true } func isJobCanceled(_ job: Job) -> Bool { return true }
// MARK: - Authorization
func getAuthToken(for room: String, on server: String) -> String? { return nil }
func setAuthToken(for room: String, on server: String, to newValue: String, using transaction: Any) {}
func removeAuthToken(for room: String, on server: String, using transaction: Any) {}
// MARK: - Open Groups // MARK: - Open Groups
func getAllOpenGroups() -> [String: OpenGroup] { return (mockData[.allOpenGroups] as! [String: OpenGroup]) } func getAllOpenGroups() -> [String: OpenGroup] { return (mockData[.allOpenGroups] as! [String: OpenGroup]) }
@ -96,6 +93,40 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
mockData[.openGroupUserCount] = newValue mockData[.openGroupUserCount] = newValue
} }
func getOpenGroupSequenceNumber(for room: String, on server: String) -> Int64? {
let data: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
return data["\(server).\(room)"]
}
func setOpenGroupSequenceNumber(for room: String, on server: String, to newValue: Int64, using transaction: Any) {
var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
updatedData["\(server).\(room)"] = newValue
mockData[.openGroupSequenceNumber] = updatedData
}
func removeOpenGroupSequenceNumber(for room: String, on server: String, using transaction: Any) {
var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
updatedData["\(server).\(room)"] = nil
mockData[.openGroupSequenceNumber] = updatedData
}
func getOpenGroupInboxLatestMessageId(for server: String) -> Int64? {
let data: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:])
return data[server]
}
func setOpenGroupInboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) {
var updatedData: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:])
updatedData[server] = newValue
mockData[.openGroupLatestMessageId] = updatedData
}
func removeOpenGroupInboxLatestMessageId(for server: String, using transaction: Any) {
var updatedData: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:])
updatedData[server] = nil
mockData[.openGroupLatestMessageId] = updatedData
}
// MARK: - Open Group Public Keys // MARK: - Open Group Public Keys
func getOpenGroupPublicKey(for server: String) -> String? { func getOpenGroupPublicKey(for server: String) -> String? {
@ -108,20 +139,11 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {} func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {}
// MARK: - Last Message Server ID
func getLastMessageServerID(for room: String, on server: String) -> Int64? { return nil }
func setLastMessageServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any) {}
func removeLastMessageServerID(for room: String, on server: String, using transaction: Any) {}
// MARK: - Last Deletion Server ID
func getLastDeletionServerID(for room: String, on server: String) -> Int64? { return nil }
func setLastDeletionServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any) {}
func removeLastDeletionServerID(for room: String, on server: String, using transaction: Any) {}
// MARK: - Message Handling // MARK: - Message Handling
func getAllMessageRequestThreads() -> [String: TSContactThread] { return [:] }
func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread] { return [:] }
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] { return [] } func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] { return [] }
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {} func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {}
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil } func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil }

Loading…
Cancel
Save