Added more unit tests

Removed an unused endpoint
Moved 'Dependencies' into the Utilities folder (also out from being nested within 'OpenGroupAPI' since it can be broader than that)
Finished adding unit tests for the OpenGroupAPI
pull/592/head
Morgan Pretty 3 years ago
parent b6a6f77d4e
commit 65f14cf0a1

@ -805,6 +805,17 @@
FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; };
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
FDC2908727D7047F005DAE71 /* RoomSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908627D7047F005DAE71 /* RoomSpec.swift */; };
FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */; };
FDC2908B27D707F3005DAE71 /* SendMessageRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908A27D707F3005DAE71 /* SendMessageRequestSpec.swift */; };
FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908C27D70905005DAE71 /* UpdateMessageRequestSpec.swift */; };
FDC2908F27D70938005DAE71 /* SendDirectMessageRequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2908E27D70938005DAE71 /* SendDirectMessageRequestSpec.swift */; };
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909027D709CA005DAE71 /* SOGSMessageSpec.swift */; };
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909327D710B4005DAE71 /* SOGSEndpointSpec.swift */; };
FDC2909627D71252005DAE71 /* SOGSErrorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909527D71252005DAE71 /* SOGSErrorSpec.swift */; };
FDC2909827D7129B005DAE71 /* PersonalizationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909727D7129B005DAE71 /* PersonalizationSpec.swift */; };
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909927D71376005DAE71 /* NonceGeneratorSpec.swift */; };
FDC2909C27D713D2005DAE71 /* SodiumProtocolsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909B27D713D2005DAE71 /* SodiumProtocolsSpec.swift */; };
FDC4380927B31D4E00C60D73 /* SOGSError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* SOGSError.swift */; };
FDC4381527B329CE00C60D73 /* NonceGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381427B329CE00C60D73 /* NonceGenerator.swift */; };
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381627B32EC700C60D73 /* Personalization.swift */; };
@ -1940,6 +1951,17 @@
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
FDC2908627D7047F005DAE71 /* RoomSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSpec.swift; sourceTree = "<group>"; };
FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollInfoSpec.swift; sourceTree = "<group>"; };
FDC2908A27D707F3005DAE71 /* SendMessageRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequestSpec.swift; sourceTree = "<group>"; };
FDC2908C27D70905005DAE71 /* UpdateMessageRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMessageRequestSpec.swift; sourceTree = "<group>"; };
FDC2908E27D70938005DAE71 /* SendDirectMessageRequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendDirectMessageRequestSpec.swift; sourceTree = "<group>"; };
FDC2909027D709CA005DAE71 /* SOGSMessageSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSMessageSpec.swift; sourceTree = "<group>"; };
FDC2909327D710B4005DAE71 /* SOGSEndpointSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSEndpointSpec.swift; sourceTree = "<group>"; };
FDC2909527D71252005DAE71 /* SOGSErrorSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSErrorSpec.swift; sourceTree = "<group>"; };
FDC2909727D7129B005DAE71 /* PersonalizationSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalizationSpec.swift; sourceTree = "<group>"; };
FDC2909927D71376005DAE71 /* NonceGeneratorSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGeneratorSpec.swift; sourceTree = "<group>"; };
FDC2909B27D713D2005DAE71 /* SodiumProtocolsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SodiumProtocolsSpec.swift; sourceTree = "<group>"; };
FDC4380827B31D4E00C60D73 /* SOGSError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSError.swift; sourceTree = "<group>"; };
FDC4381427B329CE00C60D73 /* NonceGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGenerator.swift; sourceTree = "<group>"; };
FDC4381627B32EC700C60D73 /* Personalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Personalization.swift; sourceTree = "<group>"; };
@ -3397,6 +3419,7 @@
FDC4383D27B4708600C60D73 /* Atomic.swift */,
FD83B9A927CF149D005E1583 /* ContactUtilities.swift */,
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */,
FDC438C027BB4E6800C60D73 /* Dependencies.swift */,
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
C37F53E8255BA9BB002AEA92 /* Environment.h */,
C37F5402255BA9ED002AEA92 /* Environment.m */,
@ -3865,6 +3888,12 @@
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */,
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */,
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */,
FDC2908627D7047F005DAE71 /* RoomSpec.swift */,
FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */,
FDC2908A27D707F3005DAE71 /* SendMessageRequestSpec.swift */,
FDC2908C27D70905005DAE71 /* UpdateMessageRequestSpec.swift */,
FDC2909027D709CA005DAE71 /* SOGSMessageSpec.swift */,
FDC2908E27D70938005DAE71 /* SendDirectMessageRequestSpec.swift */,
);
path = Models;
sourceTree = "<group>";
@ -3885,6 +3914,18 @@
path = Views;
sourceTree = "<group>";
};
FDC2909227D710A9005DAE71 /* Types */ = {
isa = PBXGroup;
children = (
FDC2909327D710B4005DAE71 /* SOGSEndpointSpec.swift */,
FDC2909527D71252005DAE71 /* SOGSErrorSpec.swift */,
FDC2909727D7129B005DAE71 /* PersonalizationSpec.swift */,
FDC2909927D71376005DAE71 /* NonceGeneratorSpec.swift */,
FDC2909B27D713D2005DAE71 /* SodiumProtocolsSpec.swift */,
);
path = Types;
sourceTree = "<group>";
};
FDC4380727B31D3A00C60D73 /* Types */ = {
isa = PBXGroup;
children = (
@ -3892,7 +3933,6 @@
FDC4380827B31D4E00C60D73 /* SOGSError.swift */,
FDC4381627B32EC700C60D73 /* Personalization.swift */,
FDC4381427B329CE00C60D73 /* NonceGenerator.swift */,
FDC438C027BB4E6800C60D73 /* Dependencies.swift */,
FDC438C227BB512200C60D73 /* SodiumProtocols.swift */,
);
path = Types;
@ -3972,6 +4012,7 @@
isa = PBXGroup;
children = (
FD83B9C127CF33EE005E1583 /* Models */,
FDC2909227D710A9005DAE71 /* Types */,
FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */,
);
path = "Open Groups";
@ -5578,16 +5619,27 @@
buildActionMask = 2147483647;
files = (
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */,
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */,
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */,
FDC2909627D71252005DAE71 /* SOGSErrorSpec.swift in Sources */,
FDC2908727D7047F005DAE71 /* RoomSpec.swift in Sources */,
FDC2909C27D713D2005DAE71 /* SodiumProtocolsSpec.swift in Sources */,
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */,
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */,
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */,
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */,
FDC2909827D7129B005DAE71 /* PersonalizationSpec.swift in Sources */,
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */,
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */,
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */,
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */,
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */,
FDC2908B27D707F3005DAE71 /* SendMessageRequestSpec.swift in Sources */,
FDC2908F27D70938005DAE71 /* SendDirectMessageRequestSpec.swift in Sources */,
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */,
FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */,
FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */,
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
FD83B9D227D59495005E1583 /* TestUserDefaults.swift in Sources */,
);

@ -813,7 +813,8 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self] _ in
let publicKey = message.authorId
guard let openGroup = Storage.shared.getOpenGroup(for: threadID) else { return }
let promise = OpenGroupAPI.userBanAndDeleteAllMessage(publicKey, for: [openGroup.room], on: openGroup.server)
let promise = OpenGroupAPI.userBanAndDeleteAllMessage(publicKey, from: [openGroup.room], on: openGroup.server)
promise.catch(on: DispatchQueue.main) { _ in
OWSAlerts.showErrorAlert(message: NSLocalizedString("context_menu_ban_user_error_alert_message", comment: ""))
}

@ -143,13 +143,13 @@ protocol BatchRequestInfoType {
// MARK: - Convenience
public extension Decodable {
static func decoded(from data: Data, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> Self {
static func decoded(from data: Data, using dependencies: Dependencies = Dependencies()) throws -> Self {
return try data.decoded(as: Self.self, using: dependencies)
}
}
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
func decoded(as types: OpenGroupAPI.BatchResponseTypes, on queue: DispatchQueue? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<OpenGroupAPI.BatchResponse> {
func decoded(as types: OpenGroupAPI.BatchResponseTypes, on queue: DispatchQueue? = nil, using dependencies: Dependencies = Dependencies()) -> Promise<OpenGroupAPI.BatchResponse> {
self.map(on: queue) { responseInfo, maybeData -> OpenGroupAPI.BatchResponse in
// Need to split the data into an array of data so each item can be Decoded correctly
guard let data: Data = maybeData else { throw HTTP.Error.parsingFailed }

@ -108,7 +108,7 @@ extension OpenGroupAPI.RoomPollInfo {
defaultWrite: room.defaultWrite,
upload: room.upload,
defaultUpload: room.defaultUpload,
details: nil
details: room
)
}
}

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPI {
public struct Message: Codable {
public struct Message: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case id
case sender = "session_id"
@ -47,7 +47,7 @@ extension OpenGroupAPI.Message {
guard let sender: String = maybeSender, let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else {
throw HTTP.Error.parsingFailed
}
guard let dependencies: OpenGroupAPI.Dependencies = decoder.userInfo[OpenGroupAPI.Dependencies.userInfoKey] as? OpenGroupAPI.Dependencies else {
guard let dependencies: Dependencies = decoder.userInfo[Dependencies.userInfoKey] as? Dependencies else {
throw HTTP.Error.parsingFailed
}

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPI {
public struct SendDirectMessageResponse: Codable {
public struct SendDirectMessageResponse: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case id
case sender

@ -3,7 +3,7 @@
import Foundation
extension OpenGroupAPI {
public struct UserDeleteMessagesResponse: Codable {
public struct UserDeleteMessagesResponse: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case id
case messagesDeleted = "messages_deleted"

@ -812,7 +812,7 @@ public enum OpenGroupAPI {
/// - dependencies: Injected dependencies (used for unit testing)
public static func userDeleteMessages(
_ sessionId: String,
for roomTokens: [String]?,
from roomTokens: [String]?,
on server: String,
using dependencies: Dependencies = Dependencies()
) -> Promise<(OnionRequestResponseInfoType, UserDeleteMessagesResponse)> {
@ -837,7 +837,7 @@ public enum OpenGroupAPI {
/// methods for the documented behaviour of each method
public static func userBanAndDeleteAllMessage(
_ sessionId: String,
for roomTokens: [String]?,
from roomTokens: [String]?,
on server: String,
using dependencies: Dependencies = Dependencies()
) -> Promise<[OnionRequestResponseInfoType]> {

@ -18,7 +18,7 @@ public final class OpenGroupManager: NSObject {
public var timeSinceLastPoll: [String: TimeInterval] = [:]
fileprivate var _timeSinceLastOpen: TimeInterval?
public func getTimeSinceLastOpen(using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> TimeInterval {
public func getTimeSinceLastOpen(using dependencies: Dependencies = Dependencies()) -> TimeInterval {
if let storedTimeSinceLastOpen: TimeInterval = _timeSinceLastOpen {
return storedTimeSinceLastOpen
}
@ -67,7 +67,7 @@ public final class OpenGroupManager: NSObject {
// MARK: - Adding & Removing
public func add(roomToken: String, server: String, publicKey: String, isConfigMessage: Bool, using transaction: YapDatabaseReadWriteTransaction, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<Void> {
public func add(roomToken: String, server: String, publicKey: String, isConfigMessage: Bool, using transaction: YapDatabaseReadWriteTransaction, dependencies: Dependencies = Dependencies()) -> Promise<Void> {
// If we are currently polling for this server and already have a TSGroupThread for this room the do nothing
let groupId: Data = LKGroupUtilities.getEncodedOpenGroupIDAsData("\(server).\(roomToken)")
@ -163,7 +163,7 @@ public final class OpenGroupManager: NSObject {
_ capabilities: OpenGroupAPI.Capabilities,
on server: String,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
dependencies: Dependencies = Dependencies()
) {
let updatedServer: OpenGroupAPI.Server = OpenGroupAPI.Server(
name: server,
@ -179,7 +179,7 @@ public final class OpenGroupManager: NSObject {
for roomToken: String,
on server: String,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(),
dependencies: Dependencies = Dependencies(),
completion: (() -> ())? = nil
) {
OpenGroupManager.handlePollInfo(
@ -199,7 +199,7 @@ public final class OpenGroupManager: NSObject {
for roomToken: String,
on server: String,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies(),
dependencies: Dependencies = Dependencies(),
completion: (() -> ())? = nil
) {
// Create the open group model and get or create the thread
@ -307,7 +307,7 @@ public final class OpenGroupManager: NSObject {
on server: String,
isBackgroundPoll: Bool,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
dependencies: Dependencies = Dependencies()
) {
// Sorting the messages by server ID before importing them fixes an issue where messages
// that quote older messages can't find those older messages
@ -371,7 +371,7 @@ public final class OpenGroupManager: NSObject {
on server: String,
isBackgroundPoll: Bool,
using transaction: YapDatabaseReadWriteTransaction,
dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
dependencies: Dependencies = Dependencies()
) {
// Don't need to do anything if we have no messages (it's a valid case)
guard !messages.isEmpty else { return }
@ -476,10 +476,10 @@ public final class OpenGroupManager: NSObject {
/// This method specifies if the given publicKey is a moderator or an admin within a specified Open Group
@objc(isUserModeratorOrAdmin:forRoom:onServer:)
public static func isUserModeratorOrAdmin(_ publicKey: String, for room: String, on server: String) -> Bool {
return isUserModeratorOrAdmin(publicKey, for: room, on: server, using: OpenGroupAPI.Dependencies())
return isUserModeratorOrAdmin(publicKey, for: room, on: server)
}
public static func isUserModeratorOrAdmin(_ publicKey: String, for room: String, on server: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Bool {
public static func isUserModeratorOrAdmin(_ publicKey: String, for room: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Bool {
let modAndAdminKeys: Set<String> = (OpenGroupManager.shared.cache.moderators[server]?[room] ?? Set())
.union(OpenGroupManager.shared.cache.admins[server]?[room] ?? Set())
@ -527,7 +527,7 @@ public final class OpenGroupManager: NSObject {
}
}
public static func getDefaultRoomsIfNeeded(using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) {
public static func getDefaultRoomsIfNeeded(using dependencies: Dependencies = Dependencies()) {
// Note: If we already have a 'defaultRoomsPromise' then there is no need to get it again
guard OpenGroupManager.shared.cache.defaultRoomsPromise == nil else { return }
@ -572,7 +572,7 @@ public final class OpenGroupManager: NSObject {
_ fileId: UInt64,
for roomToken: String,
on server: String,
using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()
using dependencies: Dependencies = Dependencies()
) -> Promise<Data> {
// Normally the image for a given group is stored with the group thread, so it's only
// fetched once. However, on the join open group screen we show images for groups the

@ -1,148 +0,0 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Sodium
import SessionSnodeKit
import SessionUtilitiesKit
// MARK: - Dependencies
extension OpenGroupAPI {
public class Dependencies {
private var _api: OnionRequestAPIType.Type?
public var api: OnionRequestAPIType.Type {
get { getValueSettingIfNull(&_api) { OnionRequestAPI.self } }
set { _api = newValue }
}
private var _storage: SessionMessagingKitStorageProtocol?
public var storage: SessionMessagingKitStorageProtocol {
get { getValueSettingIfNull(&_storage) { SNMessagingKitConfiguration.shared.storage } }
set { _storage = newValue }
}
private var _sodium: SodiumType?
public var sodium: SodiumType {
get { getValueSettingIfNull(&_sodium) { Sodium() } }
set { _sodium = newValue }
}
private var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType?
public var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {
get { getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } }
set { _aeadXChaCha20Poly1305Ietf = newValue }
}
private var _sign: SignType?
public var sign: SignType {
get { getValueSettingIfNull(&_sign) { sodium.getSign() } }
set { _sign = newValue }
}
private var _genericHash: GenericHashType?
public var genericHash: GenericHashType {
get { getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } }
set { _genericHash = newValue }
}
private var _ed25519: Ed25519Type.Type?
public var ed25519: Ed25519Type.Type {
get { getValueSettingIfNull(&_ed25519) { Ed25519.self } }
set { _ed25519 = newValue }
}
private var _nonceGenerator16: NonceGenerator16ByteType?
public var nonceGenerator16: NonceGenerator16ByteType {
get { getValueSettingIfNull(&_nonceGenerator16) { NonceGenerator16Byte() } }
set { _nonceGenerator16 = newValue }
}
private var _nonceGenerator24: NonceGenerator24ByteType?
public var nonceGenerator24: NonceGenerator24ByteType {
get { getValueSettingIfNull(&_nonceGenerator24) { NonceGenerator24Byte() } }
set { _nonceGenerator24 = newValue }
}
private var _standardUserDefaults: UserDefaultsType?
public var standardUserDefaults: UserDefaultsType {
get { getValueSettingIfNull(&_standardUserDefaults) { UserDefaults.standard } }
set { _standardUserDefaults = newValue }
}
private var _date: Date?
public var date: Date {
get { getValueSettingIfNull(&_date) { Date() } }
set { _date = newValue }
}
// MARK: - Initialization
public init(
api: OnionRequestAPIType.Type? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
sodium: SodiumType? = nil,
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
sign: SignType? = nil,
genericHash: GenericHashType? = nil,
ed25519: Ed25519Type.Type? = nil,
nonceGenerator16: NonceGenerator16ByteType? = nil,
nonceGenerator24: NonceGenerator24ByteType? = nil,
standardUserDefaults: UserDefaultsType? = nil,
date: Date? = nil
) {
_api = api
_storage = storage
_sodium = sodium
_aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf
_sign = sign
_genericHash = genericHash
_ed25519 = ed25519
_nonceGenerator16 = nonceGenerator16
_nonceGenerator24 = nonceGenerator24
_standardUserDefaults = standardUserDefaults
_date = date
}
// MARK: - Convenience
public func with(
api: OnionRequestAPIType.Type? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
sodium: SodiumType? = nil,
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
sign: SignType? = nil,
genericHash: GenericHashType? = nil,
ed25519: Ed25519Type.Type? = nil,
nonceGenerator16: NonceGenerator16ByteType? = nil,
nonceGenerator24: NonceGenerator24ByteType? = nil,
standardUserDefaults: UserDefaultsType? = nil,
date: Date? = nil
) -> Dependencies {
return Dependencies(
api: (api ?? self._api),
storage: (storage ?? self._storage),
sodium: (sodium ?? self._sodium),
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
sign: (sign ?? self._sign),
genericHash: (genericHash ?? self._genericHash),
ed25519: (ed25519 ?? self._ed25519),
nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
standardUserDefaults: (standardUserDefaults ?? self._standardUserDefaults),
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
}

@ -17,13 +17,9 @@ public protocol NonceGenerator24ByteType {
extension OpenGroupAPI {
public class NonceGenerator16Byte: NonceGenerator, NonceGenerator16ByteType {
public var NonceBytes: Int { 16 }
public init() {}
}
public class NonceGenerator24Byte: NonceGenerator, NonceGenerator24ByteType {
public var NonceBytes: Int { 24 }
public init() {}
}
}

@ -49,7 +49,6 @@ extension OpenGroupAPI {
case userBan(String)
case userUnban(String)
case userPermission(String)
case userModerator(String)
case userDeleteMessages(String)
@ -114,7 +113,6 @@ extension OpenGroupAPI {
case .userBan(let sessionId): return "user/\(sessionId)/ban"
case .userUnban(let sessionId): return "user/\(sessionId)/unban"
case .userPermission(let sessionId): return "user/\(sessionId)/permission"
case .userModerator(let sessionId): return "user/\(sessionId)/moderator"
case .userDeleteMessages(let sessionId): return "user/\(sessionId)/deleteMessages"
}

@ -27,7 +27,7 @@ extension MessageReceiver {
return (Data(plaintext), SessionId(.standard, publicKey: senderX25519PublicKey).hexString)
}
internal static func decryptWithSessionBlindingProtocol(data: Data, isOutgoing: Bool, otherBlindedPublicKey: String, with openGroupPublicKey: String, userEd25519KeyPair: Box.KeyPair, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> (plaintext: Data, senderX25519PublicKey: String) {
internal static func decryptWithSessionBlindingProtocol(data: Data, isOutgoing: Bool, otherBlindedPublicKey: String, with openGroupPublicKey: String, userEd25519KeyPair: Box.KeyPair, using dependencies: Dependencies = Dependencies()) throws -> (plaintext: Data, senderX25519PublicKey: String) {
guard let blindedKeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroupPublicKey, edKeyPair: userEd25519KeyPair, genericHash: dependencies.genericHash) else {
throw Error.decryptionFailed
}

@ -24,7 +24,7 @@ extension MessageSender {
return Data(ciphertext)
}
internal static func encryptWithSessionBlindingProtocol(_ plaintext: Data, for recipientBlindedId: String, openGroupPublicKey: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> Data {
internal static func encryptWithSessionBlindingProtocol(_ plaintext: Data, for recipientBlindedId: String, openGroupPublicKey: String, using dependencies: Dependencies = Dependencies()) throws -> Data {
guard SessionId.Prefix(from: recipientBlindedId) == .blinded else { throw Error.signingFailed }
guard let userEd25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserED25519KeyPair() else {
throw Error.noUserED25519KeyPair

@ -281,7 +281,7 @@ public final class MessageSender : NSObject {
// MARK: - Open Groups
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<Void> {
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any, dependencies: Dependencies = Dependencies()) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let transaction = transaction as! YapDatabaseReadWriteTransaction
@ -410,7 +410,7 @@ public final class MessageSender : NSObject {
return promise
}
internal static func sendToOpenGroupInboxDestination(_ destination: Message.Destination, message: Message, using transaction: Any, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<Void> {
internal static func sendToOpenGroupInboxDestination(_ destination: Message.Destination, message: Message, using transaction: Any, dependencies: Dependencies = Dependencies()) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction

@ -49,7 +49,7 @@ public enum ContactUtilities {
}
}
public static func mapping(for blindedId: String, serverPublicKey: String, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> BlindedIdMapping? {
public static func mapping(for blindedId: String, serverPublicKey: String, using dependencies: Dependencies = Dependencies()) -> BlindedIdMapping? {
var result: BlindedIdMapping?
Storage.write { transaction in
@ -59,7 +59,7 @@ public enum ContactUtilities {
return result
}
public static func mapping(for blindedId: String, serverPublicKey: String, using transaction: YapDatabaseReadWriteTransaction, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> BlindedIdMapping? {
public static func mapping(for blindedId: String, serverPublicKey: String, using transaction: YapDatabaseReadWriteTransaction, dependencies: Dependencies = Dependencies()) -> BlindedIdMapping? {
// Unfortunately the whole point of id-blinding is to make it hard to reverse-engineer a standard
// sessionId, as a result in order to see if there is an unblinded contact for this blindedId we
// can only really generate blinded ids for each contact and check if any match

@ -4,15 +4,15 @@ import Foundation
// MARK: - Decoding
extension OpenGroupAPI.Dependencies {
extension Dependencies {
static let userInfoKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "io.oxen.dependencies.codingOptions")!
}
public extension Data {
func decoded<T: Decodable>(as type: T.Type, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> T {
func decoded<T: Decodable>(as type: T.Type, using dependencies: Dependencies = Dependencies()) throws -> T {
do {
let decoder: JSONDecoder = JSONDecoder()
decoder.userInfo = [ OpenGroupAPI.Dependencies.userInfoKey: dependencies ]
decoder.userInfo = [ Dependencies.userInfoKey: dependencies ]
return try decoder.decode(type, from: self)
}

@ -0,0 +1,146 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Sodium
import SessionSnodeKit
import SessionUtilitiesKit
// MARK: - Dependencies
public class Dependencies {
private var _api: OnionRequestAPIType.Type?
public var api: OnionRequestAPIType.Type {
get { getValueSettingIfNull(&_api) { OnionRequestAPI.self } }
set { _api = newValue }
}
private var _storage: SessionMessagingKitStorageProtocol?
public var storage: SessionMessagingKitStorageProtocol {
get { getValueSettingIfNull(&_storage) { SNMessagingKitConfiguration.shared.storage } }
set { _storage = newValue }
}
private var _sodium: SodiumType?
public var sodium: SodiumType {
get { getValueSettingIfNull(&_sodium) { Sodium() } }
set { _sodium = newValue }
}
private var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType?
public var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {
get { getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } }
set { _aeadXChaCha20Poly1305Ietf = newValue }
}
private var _sign: SignType?
public var sign: SignType {
get { getValueSettingIfNull(&_sign) { sodium.getSign() } }
set { _sign = newValue }
}
private var _genericHash: GenericHashType?
public var genericHash: GenericHashType {
get { getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } }
set { _genericHash = newValue }
}
private var _ed25519: Ed25519Type.Type?
public var ed25519: Ed25519Type.Type {
get { getValueSettingIfNull(&_ed25519) { Ed25519.self } }
set { _ed25519 = newValue }
}
private var _nonceGenerator16: NonceGenerator16ByteType?
public var nonceGenerator16: NonceGenerator16ByteType {
get { getValueSettingIfNull(&_nonceGenerator16) { OpenGroupAPI.NonceGenerator16Byte() } }
set { _nonceGenerator16 = newValue }
}
private var _nonceGenerator24: NonceGenerator24ByteType?
public var nonceGenerator24: NonceGenerator24ByteType {
get { getValueSettingIfNull(&_nonceGenerator24) { OpenGroupAPI.NonceGenerator24Byte() } }
set { _nonceGenerator24 = newValue }
}
private var _standardUserDefaults: UserDefaultsType?
public var standardUserDefaults: UserDefaultsType {
get { getValueSettingIfNull(&_standardUserDefaults) { UserDefaults.standard } }
set { _standardUserDefaults = newValue }
}
private var _date: Date?
public var date: Date {
get { getValueSettingIfNull(&_date) { Date() } }
set { _date = newValue }
}
// MARK: - Initialization
public init(
api: OnionRequestAPIType.Type? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
sodium: SodiumType? = nil,
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
sign: SignType? = nil,
genericHash: GenericHashType? = nil,
ed25519: Ed25519Type.Type? = nil,
nonceGenerator16: NonceGenerator16ByteType? = nil,
nonceGenerator24: NonceGenerator24ByteType? = nil,
standardUserDefaults: UserDefaultsType? = nil,
date: Date? = nil
) {
_api = api
_storage = storage
_sodium = sodium
_aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf
_sign = sign
_genericHash = genericHash
_ed25519 = ed25519
_nonceGenerator16 = nonceGenerator16
_nonceGenerator24 = nonceGenerator24
_standardUserDefaults = standardUserDefaults
_date = date
}
// MARK: - Convenience
public func with(
api: OnionRequestAPIType.Type? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
sodium: SodiumType? = nil,
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
sign: SignType? = nil,
genericHash: GenericHashType? = nil,
ed25519: Ed25519Type.Type? = nil,
nonceGenerator16: NonceGenerator16ByteType? = nil,
nonceGenerator24: NonceGenerator24ByteType? = nil,
standardUserDefaults: UserDefaultsType? = nil,
date: Date? = nil
) -> Dependencies {
return Dependencies(
api: (api ?? self._api),
storage: (storage ?? self._storage),
sodium: (sodium ?? self._sodium),
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
sign: (sign ?? self._sign),
genericHash: (genericHash ?? self._genericHash),
ed25519: (ed25519 ?? self._ed25519),
nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
standardUserDefaults: (standardUserDefaults ?? self._standardUserDefaults),
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
}

@ -5,7 +5,7 @@ import PromiseKit
import SessionSnodeKit
extension Promise where T == Data {
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<R> {
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, using dependencies: Dependencies = Dependencies()) -> Promise<R> {
self.map(on: queue) { data -> R in
try data.decoded(as: type, using: dependencies)
}
@ -13,7 +13,7 @@ extension Promise where T == Data {
}
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<(OnionRequestResponseInfoType, R)> {
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, R)> {
self.map(on: queue) { responseInfo, maybeData -> (OnionRequestResponseInfoType, R) in
guard let data: Data = maybeData else { throw HTTP.Error.parsingFailed }

@ -0,0 +1,124 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class RoomPollInfoSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a RoomPollInfo") {
context("when initializing with a room") {
it("copies all the relevant values across") {
let room: OpenGroupAPI.Room = OpenGroupAPI.Room(
token: "testToken",
name: "testName",
description: nil,
infoUpdates: 123,
messageSequence: 0,
created: 0,
activeUsers: 234,
activeUsersCutoff: 0,
imageId: nil,
pinnedMessages: nil,
admin: true,
globalAdmin: true,
admins: [],
hiddenAdmins: nil,
moderator: true,
globalModerator: true,
moderators: [],
hiddenModerators: nil,
read: true,
defaultRead: true,
defaultAccessible: true,
write: true,
defaultWrite: true,
upload: true,
defaultUpload: true
)
let roomPollInfo: OpenGroupAPI.RoomPollInfo = OpenGroupAPI.RoomPollInfo(room: room)
expect(roomPollInfo.token).to(equal(room.token))
expect(roomPollInfo.activeUsers).to(equal(room.activeUsers))
expect(roomPollInfo.admin).to(equal(room.admin))
expect(roomPollInfo.globalAdmin).to(equal(room.globalAdmin))
expect(roomPollInfo.moderator).to(equal(room.moderator))
expect(roomPollInfo.globalModerator).to(equal(room.globalModerator))
expect(roomPollInfo.read).to(equal(room.read))
expect(roomPollInfo.defaultRead).to(equal(room.defaultRead))
expect(roomPollInfo.defaultAccessible).to(equal(room.defaultAccessible))
expect(roomPollInfo.write).to(equal(room.write))
expect(roomPollInfo.defaultWrite).to(equal(room.defaultWrite))
expect(roomPollInfo.upload).to(equal(room.upload))
expect(roomPollInfo.defaultUpload).to(equal(room.defaultUpload))
expect(roomPollInfo.details).to(equal(room))
}
}
context("when decoding") {
it("defaults admin and moderator values to false if omitted") {
let roomPollInfoJson: String = """
{
"token": "testToken",
"active_users": 0,
"read": true,
"default_read": true,
"default_accessible": true,
"write": true,
"default_write": true,
"upload": true,
"default_upload": true,
"details": null
}
"""
let roomData: Data = roomPollInfoJson.data(using: .utf8)!
let result: OpenGroupAPI.RoomPollInfo = try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: roomData)
expect(result.admin).to(beFalse())
expect(result.globalAdmin).to(beFalse())
expect(result.moderator).to(beFalse())
expect(result.globalModerator).to(beFalse())
}
it("sets the admin and moderator values when provided") {
let roomPollInfoJson: String = """
{
"token": "testToken",
"active_users": 0,
"admin": true,
"global_admin": true,
"moderator": true,
"global_moderator": true,
"read": true,
"default_read": true,
"default_accessible": true,
"write": true,
"default_write": true,
"upload": true,
"default_upload": true,
"details": null
}
"""
let roomData: Data = roomPollInfoJson.data(using: .utf8)!
let result: OpenGroupAPI.RoomPollInfo = try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: roomData)
expect(result.admin).to(beTrue())
expect(result.globalAdmin).to(beTrue())
expect(result.moderator).to(beTrue())
expect(result.globalModerator).to(beTrue())
}
}
}
}
}

@ -0,0 +1,100 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class RoomSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a Room") {
context("when decoding") {
it("defaults admin and moderator values to false if omitted") {
let roomJson: String = """
{
"token": "testToken",
"name": "testName",
"description": "testDescription",
"info_updates": 0,
"message_sequence": 0,
"created": 1,
"active_users": 0,
"active_users_cutoff": 0,
"image_id": 0,
"pinned_messages": [],
"admins": [],
"hidden_admins": [],
"moderators": [],
"hidden_moderators": [],
"read": true,
"default_read": true,
"default_accessible": true,
"write": true,
"default_write": true,
"upload": true,
"default_upload": true
}
"""
let roomData: Data = roomJson.data(using: .utf8)!
let result: OpenGroupAPI.Room = try! JSONDecoder().decode(OpenGroupAPI.Room.self, from: roomData)
expect(result.admin).to(beFalse())
expect(result.globalAdmin).to(beFalse())
expect(result.moderator).to(beFalse())
expect(result.globalModerator).to(beFalse())
}
it("sets the admin and moderator values when provided") {
let roomJson: String = """
{
"token": "testToken",
"name": "testName",
"description": "testDescription",
"info_updates": 0,
"message_sequence": 0,
"created": 1,
"active_users": 0,
"active_users_cutoff": 0,
"image_id": 0,
"pinned_messages": [],
"admin": true,
"global_admin": true,
"admins": [],
"hidden_admins": [],
"moderator": true,
"global_moderator": true,
"moderators": [],
"hidden_moderators": [],
"read": true,
"default_read": true,
"default_accessible": true,
"write": true,
"default_write": true,
"upload": true,
"default_upload": true
}
"""
let roomData: Data = roomJson.data(using: .utf8)!
let result: OpenGroupAPI.Room = try! JSONDecoder().decode(OpenGroupAPI.Room.self, from: roomData)
expect(result.admin).to(beTrue())
expect(result.globalAdmin).to(beTrue())
expect(result.moderator).to(beTrue())
expect(result.globalModerator).to(beTrue())
}
}
}
}
}

@ -0,0 +1,253 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SOGSMessageSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a SOGSMessage") {
var messageJson: String!
var messageData: Data!
var decoder: JSONDecoder!
var testSign: TestSign!
var dependencies: Dependencies!
beforeEach {
messageJson = """
{
"id": 123,
"session_id": "05\(TestConstants.publicKey)",
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "VGVzdERhdGE=",
"signature": "VGVzdERhdGE="
}
"""
messageData = messageJson.data(using: .utf8)!
testSign = TestSign()
dependencies = Dependencies(
sign: testSign,
ed25519: TestEd25519.self
)
decoder = JSONDecoder()
decoder.userInfo = [ Dependencies.userInfoKey: dependencies as Any ]
}
context("when decoding") {
it("defaults the whisper values to false") {
messageJson = """
{
"id": 123,
"posted": 234,
"seqno": 345
}
"""
messageData = messageJson.data(using: .utf8)!
let result: OpenGroupAPI.Message? = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
expect(result).toNot(beNil())
expect(result?.whisper).to(beFalse())
expect(result?.whisperMods).to(beFalse())
}
context("and there is no content") {
it("does not need a sender") {
messageJson = """
{
"id": 123,
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false
}
"""
messageData = messageJson.data(using: .utf8)!
let result: OpenGroupAPI.Message? = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
expect(result).toNot(beNil())
expect(result?.sender).to(beNil())
expect(result?.base64EncodedData).to(beNil())
expect(result?.base64EncodedSignature).to(beNil())
}
}
context("and there is content") {
it("errors if there is no sender") {
messageJson = """
{
"id": 123,
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "VGVzdERhdGE=",
"signature": "VGVzdERhdGE="
}
"""
messageData = messageJson.data(using: .utf8)!
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
it("errors if the data is not a base64 encoded string") {
messageJson = """
{
"id": 123,
"session_id": "05\(TestConstants.publicKey)",
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "Test!!!",
"signature": "VGVzdERhdGE="
}
"""
messageData = messageJson.data(using: .utf8)!
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
it("errors if the signature is not a base64 encoded string") {
messageJson = """
{
"id": 123,
"session_id": "05\(TestConstants.publicKey)",
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "VGVzdERhdGE=",
"signature": "Test!!!"
}
"""
messageData = messageJson.data(using: .utf8)!
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
it("errors if the dependencies are not provided to the JSONDecoder") {
decoder = JSONDecoder()
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
it("errors if the session_id value is not valid") {
messageJson = """
{
"id": 123,
"session_id": "TestId",
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "VGVzdERhdGE=",
"signature": "VGVzdERhdGE="
}
"""
messageData = messageJson.data(using: .utf8)!
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
context("that is blinded") {
beforeEach {
messageJson = """
{
"id": 123,
"session_id": "15\(TestConstants.publicKey)",
"posted": 234,
"seqno": 345,
"whisper": false,
"whisper_mods": false,
"data": "VGVzdERhdGE=",
"signature": "VGVzdERhdGE="
}
"""
messageData = messageJson.data(using: .utf8)!
}
it("succeeds if it succeeds verification") {
testSign.mockData[.verify] = true
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.toNot(beNil())
}
it("throws if it fails verification") {
testSign.mockData[.verify] = false
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
}
context("that is unblinded") {
it("succeeds if it succeeds verification") {
TestEd25519.mockData[
.verifySignature(
signature: Data(base64Encoded: "VGVzdERhdGE=")!,
publicKey: Data(hex: TestConstants.publicKey),
data: Data(base64Encoded: "VGVzdERhdGE=")!
)
] = true
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.toNot(beNil())
}
it("throws if it fails verification") {
TestEd25519.mockData[
.verifySignature(
signature: Data(base64Encoded: "VGVzdERhdGE=")!,
publicKey: Data(hex: TestConstants.publicKey),
data: Data(base64Encoded: "VGVzdERhdGE=")!
)
] = false
expect {
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
}
.to(throwError(HTTP.Error.parsingFailed))
}
}
}
}
}
}
}

@ -0,0 +1,29 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SendDirectMessageRequestSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a SendDirectMessageRequest") {
context("when encoding") {
it("encodes the data as a base64 string") {
let request: OpenGroupAPI.SendDirectMessageRequest = OpenGroupAPI.SendDirectMessageRequest(
message: "TestData".data(using: .utf8)!
)
let requestData: Data = try! JSONEncoder().encode(request)
let requestDataString: String = String(data: requestData, encoding: .utf8)!
expect(requestDataString).toNot(contain("TestData"))
expect(requestDataString).to(contain("VGVzdERhdGE="))
}
}
}
}
}

@ -0,0 +1,61 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SendMessageRequestSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a SendMessageRequest") {
context("when initializing") {
it("defaults the optional values to nil") {
let request: OpenGroupAPI.SendMessageRequest = OpenGroupAPI.SendMessageRequest(
data: "TestData".data(using: .utf8)!,
signature: "TestSignature".data(using: .utf8)!
)
expect(request.whisperTo).to(beNil())
expect(request.whisperMods).to(beNil())
expect(request.fileIds).to(beNil())
}
}
context("when encoding") {
it("encodes the data as a base64 string") {
let request: OpenGroupAPI.SendMessageRequest = OpenGroupAPI.SendMessageRequest(
data: "TestData".data(using: .utf8)!,
signature: "TestSignature".data(using: .utf8)!,
whisperTo: nil,
whisperMods: nil,
fileIds: nil
)
let requestData: Data = try! JSONEncoder().encode(request)
let requestDataString: String = String(data: requestData, encoding: .utf8)!
expect(requestDataString).toNot(contain("TestData"))
expect(requestDataString).to(contain("VGVzdERhdGE="))
}
it("encodes the signature as a base64 string") {
let request: OpenGroupAPI.SendMessageRequest = OpenGroupAPI.SendMessageRequest(
data: "TestData".data(using: .utf8)!,
signature: "TestSignature".data(using: .utf8)!,
whisperTo: nil,
whisperMods: nil,
fileIds: nil
)
let requestData: Data = try! JSONEncoder().encode(request)
let requestDataString: String = String(data: requestData, encoding: .utf8)!
expect(requestDataString).toNot(contain("TestSignature"))
expect(requestDataString).to(contain("VGVzdFNpZ25hdHVyZQ=="))
}
}
}
}
}

@ -0,0 +1,44 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class UpdateMessageRequestSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a UpdateMessageRequest") {
context("when encoding") {
it("encodes the data as a base64 string") {
let request: OpenGroupAPI.UpdateMessageRequest = OpenGroupAPI.UpdateMessageRequest(
data: "TestData".data(using: .utf8)!,
signature: "TestSignature".data(using: .utf8)!,
fileIds: nil
)
let requestData: Data = try! JSONEncoder().encode(request)
let requestDataString: String = String(data: requestData, encoding: .utf8)!
expect(requestDataString).toNot(contain("TestData"))
expect(requestDataString).to(contain("VGVzdERhdGE="))
}
it("encodes the signature as a base64 string") {
let request: OpenGroupAPI.UpdateMessageRequest = OpenGroupAPI.UpdateMessageRequest(
data: "TestData".data(using: .utf8)!,
signature: "TestSignature".data(using: .utf8)!,
fileIds: nil
)
let requestData: Data = try! JSONEncoder().encode(request)
let requestDataString: String = String(data: requestData, encoding: .utf8)!
expect(requestDataString).toNot(contain("TestSignature"))
expect(requestDataString).to(contain("VGVzdFNpZ25hdHVyZQ=="))
}
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,26 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class NonceGeneratorSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a NonceGenerator16Byte") {
it("has the correct number of bytes") {
expect(OpenGroupAPI.NonceGenerator16Byte().NonceBytes).to(equal(16))
}
}
describe("a NonceGenerator24Byte") {
it("has the correct number of bytes") {
expect(OpenGroupAPI.NonceGenerator24Byte().NonceBytes).to(equal(24))
}
}
}
}

@ -0,0 +1,23 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class PersonalizationSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a Personalization") {
it("generates bytes correctly") {
expect(OpenGroupAPI.Personalization.sharedKeys.bytes)
.to(equal([115, 111, 103, 115, 46, 115, 104, 97, 114, 101, 100, 95, 107, 101, 121, 115]))
expect(OpenGroupAPI.Personalization.authHeader.bytes)
.to(equal([115, 111, 103, 115, 46, 97, 117, 116, 104, 95, 104, 101, 97, 100, 101, 114]))
}
}
}
}

@ -0,0 +1,67 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SOGSEndpointSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a SOGSEndpoint") {
it("generates the path value correctly") {
// Utility
expect(OpenGroupAPI.Endpoint.onion.path).to(equal("oxen/v4/lsrpc"))
expect(OpenGroupAPI.Endpoint.batch.path).to(equal("batch"))
expect(OpenGroupAPI.Endpoint.sequence.path).to(equal("sequence"))
expect(OpenGroupAPI.Endpoint.capabilities.path).to(equal("capabilities"))
// Rooms
expect(OpenGroupAPI.Endpoint.rooms.path).to(equal("rooms"))
expect(OpenGroupAPI.Endpoint.room("test").path).to(equal("room/test"))
expect(OpenGroupAPI.Endpoint.roomPollInfo("test", 123).path).to(equal("room/test/pollInfo/123"))
// Messages
expect(OpenGroupAPI.Endpoint.roomMessage("test").path).to(equal("room/test/message"))
expect(OpenGroupAPI.Endpoint.roomMessageIndividual("test", id: 123).path).to(equal("room/test/message/123"))
expect(OpenGroupAPI.Endpoint.roomMessagesRecent("test").path).to(equal("room/test/messages/recent"))
expect(OpenGroupAPI.Endpoint.roomMessagesBefore("test", id: 123).path).to(equal("room/test/messages/before/123"))
expect(OpenGroupAPI.Endpoint.roomMessagesSince("test", seqNo: 123).path)
.to(equal("room/test/messages/since/123"))
// Pinning
expect(OpenGroupAPI.Endpoint.roomPinMessage("test", id: 123).path).to(equal("room/test/pin/123"))
expect(OpenGroupAPI.Endpoint.roomUnpinMessage("test", id: 123).path).to(equal("room/test/unpin/123"))
expect(OpenGroupAPI.Endpoint.roomUnpinAll("test").path).to(equal("room/test/unpin/all"))
// Files
expect(OpenGroupAPI.Endpoint.roomFile("test").path).to(equal("room/test/file"))
expect(OpenGroupAPI.Endpoint.roomFileIndividual("test", 123).path).to(equal("room/test/file/123"))
// Inbox/Outbox (Message Requests)
expect(OpenGroupAPI.Endpoint.inbox.path).to(equal("inbox"))
expect(OpenGroupAPI.Endpoint.inboxSince(id: 123).path).to(equal("inbox/since/123"))
expect(OpenGroupAPI.Endpoint.inboxFor(sessionId: "test").path).to(equal("inbox/test"))
expect(OpenGroupAPI.Endpoint.outbox.path).to(equal("outbox"))
expect(OpenGroupAPI.Endpoint.outboxSince(id: 123).path).to(equal("outbox/since/123"))
// Users
expect(OpenGroupAPI.Endpoint.userBan("test").path).to(equal("user/test/ban"))
expect(OpenGroupAPI.Endpoint.userUnban("test").path).to(equal("user/test/unban"))
expect(OpenGroupAPI.Endpoint.userModerator("test").path).to(equal("user/test/moderator"))
expect(OpenGroupAPI.Endpoint.userDeleteMessages("test").path).to(equal("user/test/deleteMessages"))
}
}
}
}

@ -0,0 +1,25 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SOGSErrorSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("a SOGSError") {
it("generates the error description correctly") {
expect(OpenGroupAPI.Error.decryptionFailed.errorDescription)
.to(equal("Couldn't decrypt response."))
expect(OpenGroupAPI.Error.signingFailed.errorDescription)
.to(equal("Couldn't sign message."))
expect(OpenGroupAPI.Error.noPublicKey.errorDescription)
.to(equal("Couldn't find server public key."))
}
}
}
}

@ -0,0 +1,47 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class SodiumProtocolsSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("an AeadXChaCha20Poly1305IetfType") {
let testValue: [UInt8] = [1, 2, 3]
it("provides the default values in it's extensions") {
let testAead: TestAeadXChaCha20Poly1305Ietf = TestAeadXChaCha20Poly1305Ietf()
testAead.mockData[.encrypt] = testValue
testAead.mockData[.decrypt] = testValue
expect(testAead.encrypt(message: [], secretKey: [], nonce: [])).to(equal(testValue))
expect(testAead.encrypt(message: [], secretKey: [], nonce: [], additionalData: nil)).to(equal(testValue))
expect(testAead.decrypt(authenticatedCipherText: [], secretKey: [], nonce: [])).to(equal(testValue))
expect(testAead.decrypt(authenticatedCipherText: [], secretKey: [], nonce: [], additionalData: nil))
.to(equal(testValue))
}
}
describe("a GenericHashType") {
let testValue: [UInt8] = [1, 2, 3]
it("provides the default values in it's extensions") {
let testGenericHash: TestGenericHash = TestGenericHash()
testGenericHash.mockData[.hash] = testValue
testGenericHash.mockData[.hashSaltPersonal] = testValue
expect(testGenericHash.hash(message: [])).to(equal(testValue))
expect(testGenericHash.hash(message: [], key: nil)).to(equal(testValue))
expect(testGenericHash.hashSaltPersonal(message: [], outputLength: 0, salt: [], personal: []))
.to(equal(testValue))
expect(testGenericHash.hashSaltPersonal(message: [], outputLength: 0, key: nil, salt: [], personal: []))
.to(equal(testValue))
}
}
}
}

@ -21,6 +21,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
case openGroupSequenceNumber
case openGroupInboxLatestMessageId
case openGroupOutboxLatestMessageId
case receivedMessageTimestamp
}
typealias Key = DataKey
@ -176,8 +177,14 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
func getAllMessageRequestThreads() -> [String: TSContactThread] { return [:] }
func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread] { return [:] }
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] { return [] }
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {}
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] {
return ((mockData[.receivedMessageTimestamp] as? UInt64).map { [$0] } ?? [])
}
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {
mockData[.receivedMessageTimestamp] = timestamp
}
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil }
func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil }
func persist(_ attachments: [VisibleMessage.Attachment], using transaction: Any) -> [String] { return [] }

Loading…
Cancel
Save