From 153880cf4de80b03e24ced3e10798553d08588f7 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Tue, 21 Jun 2022 17:43:27 +1000 Subject: [PATCH] Fixed a few bugs and continued work on fixing unit tests Fixed a bug where notifications might not work for messages Fixed a bug where auto-playing audio messages wouldn't update the states correctly Fixed a bug where a user wouldn't be able to join an open group with blinding enabled --- Session/Conversations/ConversationVC.swift | 2 +- .../Conversations/ConversationViewModel.swift | 19 +- Session/Home/HomeVC.swift | 16 +- Session/Home/HomeViewModel.swift | 5 +- Session/Notifications/AppNotifications.swift | 12 +- Session/Utilities/MockDataGenerator.swift | 1 - .../Database/LegacyDatabase/SMKLegacy.swift | 4 - .../Migrations/_003_YDBToGRDBMigration.swift | 11 - .../Database/Models/Capability.swift | 2 +- .../Database/Models/SessionThread.swift | 22 +- .../Open Groups/OpenGroupAPI.swift | 14 +- .../Open Groups/OpenGroupManager.swift | 7 +- .../Utilities/OWSAudioPlayer.h | 2 +- .../Open Groups/OpenGroupAPISpec.swift | 1227 +++++++++++------ .../Open Groups/OpenGroupManagerSpec.swift | 1 - .../MessageReceiverDecryptionSpec.swift | 76 +- .../_TestUtilities/MockedExtensions.swift | 4 +- .../NSENotificationPresenter.swift | 10 +- 18 files changed, 894 insertions(+), 541 deletions(-) diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index cac1a3ccc..ce2d93e00 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -1150,7 +1150,7 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers OWSAlerts.showErrorAlert(message: "INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE".localized()) return } - // TODO: Looks like the 'play/pause' icon isn't swapping when it auto-plays to the next item) + cell.dynamicUpdate(with: cellViewModel, playbackInfo: updatedInfo) } }, diff --git a/Session/Conversations/ConversationViewModel.swift b/Session/Conversations/ConversationViewModel.swift index 6b46e6d34..2248cb7cc 100644 --- a/Session/Conversations/ConversationViewModel.swift +++ b/Session/Conversations/ConversationViewModel.swift @@ -511,6 +511,11 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { currentPlayingInteraction.mutate { $0 = viewModel.id } audioPlayer.mutate { [weak self] player in + // Note: We clear the delegate and explicitly set to nil here as when the OWSAudioPlayer + // gets deallocated it triggers state changes which cause UI bugs when auto-playing + player?.delegate = nil + player = nil + let audioPlayer: OWSAudioPlayer = OWSAudioPlayer( mediaUrl: URL(fileURLWithPath: originalFilePath), audioBehavior: .audioMessagePlayback, @@ -543,7 +548,12 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { audioPlayer.wrappedValue?.stop() currentPlayingInteraction.mutate { $0 = nil } - audioPlayer.mutate { $0 = nil } + audioPlayer.mutate { + // Note: We clear the delegate and explicitly set to nil here as when the OWSAudioPlayer + // gets deallocated it triggers state changes which cause UI bugs when auto-playing + $0?.delegate = nil + $0 = nil + } } // MARK: - OWSAudioPlayerDelegate @@ -591,7 +601,12 @@ public class ConversationViewModel: OWSAudioPlayerDelegate { // Clear out the currently playing record currentPlayingInteraction.mutate { $0 = nil } - audioPlayer.mutate { $0 = nil } + audioPlayer.mutate { + // Note: We clear the delegate and explicitly set to nil here as when the OWSAudioPlayer + // gets deallocated it triggers state changes which cause UI bugs when auto-playing + $0?.delegate = nil + $0 = nil + } // If the next interaction is another voice message then autoplay it guard diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index a1c3e00d9..56b8eddfb 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -200,9 +200,6 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve DispatchQueue.global(qos: .utility).sync { let _ = IP2Country.shared.populateCacheIfNeeded() } - - // Get default open group rooms if needed -// OpenGroupManager.getDefaultRoomsIfNeeded() // TODO: Needed??? } override func viewWillAppear(_ animated: Bool) { @@ -411,13 +408,8 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve switch section.model { case .messageRequests: - let hide = UITableViewRowAction(style: .destructive, title: "TXT_HIDE_TITLE".localized()) { [weak self] _, _ in + let hide = UITableViewRowAction(style: .destructive, title: "TXT_HIDE_TITLE".localized()) { _, _ in GRDBStorage.shared.write { db in db[.hasHiddenMessageRequests] = true } - - // Animate the row removal - self?.tableView.beginUpdates() - self?.tableView.deleteRows(at: [indexPath], with: .automatic) - self?.tableView.endUpdates() } hide.backgroundColor = Colors.destructive @@ -443,7 +435,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve title: "TXT_DELETE_TITLE".localized(), style: .destructive ) { _ in - GRDBStorage.shared.write { db in + GRDBStorage.shared.writeAsync { db in switch cellViewModel.threadVariant { case .closedGroup: try MessageSender @@ -477,7 +469,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve "PIN_BUTTON_TEXT".localized() ) ) { _, _ in - GRDBStorage.shared.write { db in + GRDBStorage.shared.writeAsync { db in try SessionThread .filter(id: cellViewModel.threadId) .updateAll(db, SessionThread.Columns.isPinned.set(to: !cellViewModel.threadIsPinned)) @@ -495,7 +487,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve "BLOCK_LIST_BLOCK_BUTTON".localized() ) ) { _, _ in - GRDBStorage.shared.write { db in + GRDBStorage.shared.writeAsync { db in try Contact .filter(id: cellViewModel.threadId) .updateAll( diff --git a/Session/Home/HomeViewModel.swift b/Session/Home/HomeViewModel.swift index 95c3429ee..1115f6499 100644 --- a/Session/Home/HomeViewModel.swift +++ b/Session/Home/HomeViewModel.swift @@ -73,9 +73,8 @@ public class HomeViewModel { let hasViewedSeed: Bool = db[.hasViewedSeed] let userPublicKey: String = getUserHexEncodedPublicKey(db) let unreadMessageRequestCount: Int = try SessionThread - .unreadMessageRequestsCountQuery(userPublicKey: userPublicKey) - .fetchOne(db) - .defaulting(to: 0) + .unreadMessageRequestsQuery(userPublicKey: userPublicKey) + .fetchCount(db) let finalUnreadMessageRequestCount: Int = (db[.hasHiddenMessageRequests] ? 0 : unreadMessageRequestCount) let threads: [SessionThreadViewModel] = try SessionThreadViewModel .homeQuery(userPublicKey: userPublicKey) diff --git a/Session/Notifications/AppNotifications.swift b/Session/Notifications/AppNotifications.swift index 146685c77..92330e838 100644 --- a/Session/Notifications/AppNotifications.swift +++ b/Session/Notifications/AppNotifications.swift @@ -142,10 +142,10 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { } public func notifyUser(_ db: Database, for interaction: Interaction, in thread: SessionThread, isBackgroundPoll: Bool) { - guard Date().timeIntervalSince1970 < (thread.mutedUntilTimestamp ?? 0) else { return } + guard Date().timeIntervalSince1970 > (thread.mutedUntilTimestamp ?? 0) else { return } let userPublicKey: String = getUserHexEncodedPublicKey(db) - let isMessageRequest: Bool = thread.isMessageRequest(db) + let isMessageRequest: Bool = thread.isMessageRequest(db, includeNonVisible: true) // If the thread is a message request and the user hasn't hidden message requests then we need // to check if this is the only message request thread (group threads can't be message requests @@ -153,13 +153,13 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { // notification regardless of how many message requests there are) if thread.variant == .contact { if isMessageRequest && !db[.hasHiddenMessageRequests] { - let numMessageRequestThreads: Int? = (try? SessionThread - .messageRequestsCountQuery(userPublicKey: userPublicKey) - .fetchOne(db)) + let numMessageRequestThreads: Int = (try? SessionThread + .messageRequestsQuery(userPublicKey: userPublicKey, includeNonVisible: true) + .fetchCount(db)) .defaulting(to: 0) // Allow this to show a notification if there are no message requests (ie. this is the first one) - guard (numMessageRequestThreads ?? 0) == 0 else { return } + guard numMessageRequestThreads == 0 else { return } } else if isMessageRequest && db[.hasHiddenMessageRequests] { // If there are other interactions on this thread already then don't show the notification diff --git a/Session/Utilities/MockDataGenerator.swift b/Session/Utilities/MockDataGenerator.swift index 2eed4aeae..9b0b12090 100644 --- a/Session/Utilities/MockDataGenerator.swift +++ b/Session/Utilities/MockDataGenerator.swift @@ -93,7 +93,6 @@ enum MockDataGenerator { let cgRandomSeed: Int = 2222 let ogRandomSeed: Int = 3333 let chunkSize: Int = 1000 // Chunk up the thread writing to prevent memory issues - let openGroupBaseUrl: String = "https://chat.lokinet.dev" let stringContent: [String] = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ".map { String($0) } let wordContent: [String] = ["alias", "consequatur", "aut", "perferendis", "sit", "voluptatem", "accusantium", "doloremque", "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis", "et", "quasi", "architecto", "beatae", "vitae", "dicta", "sunt", "explicabo", "aspernatur", "aut", "odit", "aut", "fugit", "sed", "quia", "consequuntur", "magni", "dolores", "eos", "qui", "ratione", "voluptatem", "sequi", "nesciunt", "neque", "dolorem", "ipsum", "quia", "dolor", "sit", "amet", "consectetur", "adipisci", "velit", "sed", "quia", "non", "numquam", "eius", "modi", "tempora", "incidunt", "ut", "labore", "et", "dolore", "magnam", "aliquam", "quaerat", "voluptatem", "ut", "enim", "ad", "minima", "veniam", "quis", "nostrum", "exercitationem", "ullam", "corporis", "nemo", "enim", "ipsam", "voluptatem", "quia", "voluptas", "sit", "suscipit", "laboriosam", "nisi", "ut", "aliquid", "ex", "ea", "commodi", "consequatur", "quis", "autem", "vel", "eum", "iure", "reprehenderit", "qui", "in", "ea", "voluptate", "velit", "esse", "quam", "nihil", "molestiae", "et", "iusto", "odio", "dignissimos", "ducimus", "qui", "blanditiis", "praesentium", "laudantium", "totam", "rem", "voluptatum", "deleniti", "atque", "corrupti", "quos", "dolores", "et", "quas", "molestias", "excepturi", "sint", "occaecati", "cupiditate", "non", "provident", "sed", "ut", "perspiciatis", "unde", "omnis", "iste", "natus", "error", "similique", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollitia", "animi", "id", "est", "laborum", "et", "dolorum", "fuga", "et", "harum", "quidem", "rerum", "facilis", "est", "et", "expedita", "distinctio", "nam", "libero", "tempore", "cum", "soluta", "nobis", "est", "eligendi", "optio", "cumque", "nihil", "impedit", "quo", "porro", "quisquam", "est", "qui", "minus", "id", "quod", "maxime", "placeat", "facere", "possimus", "omnis", "voluptas", "assumenda", "est", "omnis", "dolor", "repellendus", "temporibus", "autem", "quibusdam", "et", "aut", "consequatur", "vel", "illum", "qui", "dolorem", "eum", "fugiat", "quo", "voluptas", "nulla", "pariatur", "at", "vero", "eos", "et", "accusamus", "officiis", "debitis", "aut", "rerum", "necessitatibus", "saepe", "eveniet", "ut", "et", "voluptates", "repudiandae", "sint", "et", "molestiae", "non", "recusandae", "itaque", "earum", "rerum", "hic", "tenetur", "a", "sapiente", "delectus", "ut", "aut", "reiciendis", "voluptatibus", "maiores", "doloribus", "asperiores", "repellat"] let timestampNow: TimeInterval = Date().timeIntervalSince1970 diff --git a/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift b/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift index 31f49e9ab..25273ebfb 100644 --- a/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift +++ b/SessionMessagingKit/Database/LegacyDatabase/SMKLegacy.swift @@ -21,16 +21,12 @@ public enum SMKLegacy { public static let threadCollection = "TSThread" internal static let disappearingMessagesCollection = "OWSDisappearingMessagesConfiguration" - internal static let closedGroupPublicKeyCollection = "SNClosedGroupPublicKeyCollection" internal static let closedGroupFormationTimestampCollection = "SNClosedGroupFormationTimestampCollection" internal static let closedGroupZombieMembersCollection = "SNClosedGroupZombieMembersCollection" internal static let openGroupCollection = "SNOpenGroupCollection" internal static let openGroupUserCountCollection = "SNOpenGroupUserCountCollection" internal static let openGroupImageCollection = "SNOpenGroupImageCollection" - internal static let openGroupLastMessageServerIDCollection = "SNLastMessageServerIDCollection" - internal static let openGroupLastDeletionServerIDCollection = "SNLastDeletionServerIDCollection" - internal static let openGroupServerIdToUniqueIdLookupCollection = "SNOpenGroupServerIdToUniqueIdLookup" public static let messageDatabaseViewExtensionName = "TSMessageDatabaseViewExtensionName_Monotonic" internal static let interactionCollection = "TSInteraction" diff --git a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift index 965d6f4ae..057bf1269 100644 --- a/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift +++ b/SessionMessagingKit/Database/Migrations/_003_YDBToGRDBMigration.swift @@ -45,8 +45,6 @@ enum _003_YDBToGRDBMigration: Migration { var openGroupInfo: [String: SMKLegacy._OpenGroup] = [:] var openGroupUserCount: [String: Int64] = [:] var openGroupImage: [String: Data] = [:] - var openGroupLastMessageServerId: [String: Int64] = [:] // Optional - var openGroupLastDeletionServerId: [String: Int64] = [:] // Optional var interactions: [String: [SMKLegacy._DBInteraction]] = [:] var attachments: [String: SMKLegacy._Attachment] = [:] @@ -180,8 +178,6 @@ enum _003_YDBToGRDBMigration: Migration { openGroupInfo[thread.uniqueId] = openGroup openGroupUserCount[thread.uniqueId] = ((transaction.object(forKey: openGroup.id, inCollection: SMKLegacy.openGroupUserCountCollection) as? Int64) ?? 0) openGroupImage[thread.uniqueId] = transaction.object(forKey: openGroup.id, inCollection: SMKLegacy.openGroupImageCollection) as? Data - openGroupLastMessageServerId[thread.uniqueId] = transaction.object(forKey: openGroup.id, inCollection: SMKLegacy.openGroupLastMessageServerIDCollection) as? Int64 - openGroupLastDeletionServerId[thread.uniqueId] = transaction.object(forKey: openGroup.id, inCollection: SMKLegacy.openGroupLastDeletionServerIDCollection) as? Int64 } } GRDBStorage.shared.update(progress: 0.04, for: self, in: target) @@ -787,10 +783,6 @@ enum _003_YDBToGRDBMigration: Migration { case .mediaSavedNotification: variant = .infoMediaSavedNotification case .call: variant = .infoCall case .messageRequestAccepted: variant = .infoMessageRequestAccepted - - @unknown default: - SNLog("[Migration Error] Unsupported info message type") - throw StorageError.migrationFailed } default: @@ -1100,8 +1092,6 @@ enum _003_YDBToGRDBMigration: Migration { openGroupInfo = [:] openGroupUserCount = [:] openGroupImage = [:] - openGroupLastMessageServerId = [:] - openGroupLastDeletionServerId = [:] interactions = [:] attachments = [:] @@ -1507,7 +1497,6 @@ enum _003_YDBToGRDBMigration: Migration { return (true, nil) }() - _ = try Attachment( // Note: The legacy attachment object used a UUID string for it's id as well // and saved files using these id's so just used the existing id so we don't diff --git a/SessionMessagingKit/Database/Models/Capability.swift b/SessionMessagingKit/Database/Models/Capability.swift index ef68e2895..60437fa23 100644 --- a/SessionMessagingKit/Database/Models/Capability.swift +++ b/SessionMessagingKit/Database/Models/Capability.swift @@ -38,7 +38,7 @@ public struct Capability: Codable, FetchableRecord, PersistableRecord, TableReco public init(from valueString: String) { let maybeValue: Variant? = Variant.allCases.first { $0.rawValue == valueString } - + self = (maybeValue ?? .unsupported(valueString)) } } diff --git a/SessionMessagingKit/Database/Models/SessionThread.swift b/SessionMessagingKit/Database/Models/SessionThread.swift index b37e0fe9f..5394cc1ae 100644 --- a/SessionMessagingKit/Database/Models/SessionThread.swift +++ b/SessionMessagingKit/Database/Models/SessionThread.swift @@ -169,9 +169,9 @@ public extension SessionThread { return existingThread } - func isMessageRequest(_ db: Database) -> Bool { + func isMessageRequest(_ db: Database, includeNonVisible: Bool = false) -> Bool { return ( - shouldBeVisible && + (includeNonVisible || shouldBeVisible) && variant == .contact && id != getUserHexEncodedPublicKey(db) && // Note to self (try? Contact.fetchOne(db, id: id))?.isApproved != true @@ -182,27 +182,27 @@ public extension SessionThread { // MARK: - Convenience public extension SessionThread { - static func messageRequestsCountQuery(userPublicKey: String) -> SQLRequest { + static func messageRequestsQuery(userPublicKey: String, includeNonVisible: Bool = false) -> SQLRequest { let thread: TypedTableAlias = TypedTableAlias() let contact: TypedTableAlias = TypedTableAlias() return """ - SELECT COUNT(\(thread[.id])) + SELECT \(thread.allColumns()) FROM \(SessionThread.self) LEFT JOIN \(Contact.self) ON \(contact[.id]) = \(thread[.id]) WHERE ( - \(SessionThread.isMessageRequest(userPublicKey: userPublicKey)) + \(SessionThread.isMessageRequest(userPublicKey: userPublicKey, includeNonVisible: includeNonVisible)) ) """ } - static func unreadMessageRequestsCountQuery(userPublicKey: String) -> SQLRequest { + static func unreadMessageRequestsQuery(userPublicKey: String) -> SQLRequest { let thread: TypedTableAlias = TypedTableAlias() let contact: TypedTableAlias = TypedTableAlias() let interaction: TypedTableAlias = TypedTableAlias() return """ - SELECT COUNT(\(thread[.id])) + SELECT \(thread.allColumns()) FROM \(SessionThread.self) JOIN ( SELECT @@ -225,13 +225,17 @@ public extension SessionThread { /// /// **Note:** In order to use this filter you **MUST** have a `joining(required/optional:)` to the /// `SessionThread.contact` association or it won't work - static func isMessageRequest(userPublicKey: String) -> SQLSpecificExpressible { + static func isMessageRequest(userPublicKey: String, includeNonVisible: Bool = false) -> SQLSpecificExpressible { let thread: TypedTableAlias = TypedTableAlias() let contact: TypedTableAlias = TypedTableAlias() + let shouldBeVisibleSQL: SQL = (includeNonVisible ? + SQL(stringLiteral: "true") : + SQL("\(thread[.shouldBeVisible]) = true") + ) return SQL( """ - \(thread[.shouldBeVisible]) = true AND + \(shouldBeVisibleSQL) AND \(SQL("\(thread[.variant]) = \(SessionThread.Variant.contact)")) AND \(SQL("\(thread[.id]) != \(userPublicKey)")) AND IFNULL(\(contact[.isApproved]), false) = false diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index 55b507135..2e9a8588f 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -184,6 +184,7 @@ public enum OpenGroupAPI { _ db: Database, server: String, requests: [BatchRequestInfoType], + authenticated: Bool = true, using dependencies: Dependencies = Dependencies() ) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> { let requestBody: BatchRequest = requests.map { $0.toSubRequest() } @@ -198,6 +199,7 @@ public enum OpenGroupAPI { endpoint: Endpoint.sequence, body: requestBody ), + authenticated: authenticated, using: dependencies ) .decoded(as: responseTypes, on: OpenGroupAPI.workQueue, using: dependencies) @@ -220,7 +222,7 @@ public enum OpenGroupAPI { /// could return: `{"capabilities": ["sogs", "batch"], "missing": ["magic"]}` public static func capabilities( _ db: Database, - on server: String, + server: String, using dependencies: Dependencies = Dependencies() ) -> Promise<(OnionRequestResponseInfoType, Capabilities)> { return OpenGroupAPI @@ -242,7 +244,7 @@ public enum OpenGroupAPI { /// Rooms to which the user does not have access (e.g. because they are banned, or the room has restricted access permissions) are not included public static func rooms( _ db: Database, - for server: String, + server: String, using dependencies: Dependencies = Dependencies() ) -> Promise<(OnionRequestResponseInfoType, [Room])> { return OpenGroupAPI @@ -315,6 +317,7 @@ public enum OpenGroupAPI { _ db: Database, for roomToken: String, on server: String, + authenticated: Bool = true, using dependencies: Dependencies = Dependencies() ) -> Promise<(capabilities: (info: OnionRequestResponseInfoType, data: Capabilities), room: (info: OnionRequestResponseInfoType, data: Room))> { let requestResponseType: [BatchRequestInfoType] = [ @@ -342,6 +345,7 @@ public enum OpenGroupAPI { db, server: server, requests: requestResponseType, + authenticated: authenticated, using: dependencies ) .map { (response: [Endpoint: (OnionRequestResponseInfoType, Codable?)]) -> (capabilities: (OnionRequestResponseInfoType, Capabilities), room: (OnionRequestResponseInfoType, Room)) in @@ -1199,6 +1203,7 @@ public enum OpenGroupAPI { private static func send( _ db: Database, request: Request, + authenticated: Bool = true, using dependencies: Dependencies = Dependencies() ) -> Promise<(OnionRequestResponseInfoType, Data?)> { let urlRequest: URLRequest @@ -1218,6 +1223,11 @@ public enum OpenGroupAPI { guard let publicKey: String = maybePublicKey else { return Promise(error: Error.noPublicKey) } + // If we don't want to authenticate the request then send it immediately + guard authenticated else { + return dependencies.onionApi.sendOnionRequest(urlRequest, to: request.server, with: publicKey) + } + // Attempt to sign the request with the new auth guard let signedRequest: URLRequest = sign(db, request: urlRequest, for: request.server, with: publicKey, using: dependencies) else { return Promise(error: Error.signingFailed) diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index a2e99cac6..de130aacd 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -195,11 +195,16 @@ public final class OpenGroupManager: NSObject { OpenGroupAPI.workQueue.async { dependencies.storage .writeAsync { db in + // Note: The initial request for room info and it's capabilities should NOT be + // authenticated (this is because if the server requires blinding and the auth + // headers aren't blinded it will error - these endpoints do support unauthenticated + // retrieval so doing so prevents the error) OpenGroupAPI .capabilitiesAndRoom( db, for: roomToken, on: server, + authenticated: false, using: dependencies ) } @@ -704,7 +709,7 @@ public final class OpenGroupManager: NSObject { // Try to retrieve the default rooms 8 times attempt(maxRetryCount: 8, recoveringOn: DispatchQueue.main) { dependencies.storage.read { db in - OpenGroupAPI.rooms(db, for: OpenGroupAPI.defaultServer, using: dependencies) + OpenGroupAPI.rooms(db, server: OpenGroupAPI.defaultServer, using: dependencies) } .map { _, data in data } } diff --git a/SessionMessagingKit/Utilities/OWSAudioPlayer.h b/SessionMessagingKit/Utilities/OWSAudioPlayer.h index 63d74acf0..759086b5e 100644 --- a/SessionMessagingKit/Utilities/OWSAudioPlayer.h +++ b/SessionMessagingKit/Utilities/OWSAudioPlayer.h @@ -37,7 +37,7 @@ typedef NS_ENUM(NSUInteger, OWSAudioBehavior) { @interface OWSAudioPlayer : NSObject -@property (nonatomic, readonly, weak) id delegate; +@property (nonatomic, weak) id delegate; // This property can be used to associate instances of the player with view or model objects. @property (nonatomic, weak) id owner; @property (nonatomic) BOOL isLooping; diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift b/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift index 7ae639d0e..3feba33a3 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift @@ -55,21 +55,26 @@ class OpenGroupAPISpec: QuickSpec { ) mockStorage.write { db in - try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)).insert(db) - try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)).insert(db) - try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.edPublicKey)).insert(db) - try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)).insert(db) + try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).insert(db) + try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).insert(db) + try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.edPublicKey)!).insert(db) + try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).insert(db) try OpenGroup( server: "testServer", roomToken: "testRoom", publicKey: TestConstants.publicKey, + isActive: true, name: "Test", - groupDescription: nil, - imageID: nil, - infoUpdates: 0 + roomDescription: nil, + imageId: nil, + userCount: 0, + infoUpdates: 0, + sequenceNumber: 0, + inboxLatestMessageId: 0, + outboxLatestMessageId: 0 ).insert(db) - try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) } mockGenericHash.when { $0.hash(message: anyArray(), outputLength: any()) }.thenReturn([]) @@ -169,7 +174,16 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the correct request") { - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -615,7 +629,16 @@ class OpenGroupAPISpec: QuickSpec { } it("errors when no data is returned") { - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -635,7 +658,16 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -655,7 +687,16 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -675,7 +716,16 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -727,7 +777,16 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.poll( + db, + server: "testserver", + hasPerformedInitialPoll: false, + timeSinceLastPoll: 0, + using: dependencies + ) + } .get { result in pollResponse = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -756,7 +815,14 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities)? - OpenGroupAPI.capabilities(on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.capabilities( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -820,7 +886,14 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: [OpenGroupAPI.Room])? - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -905,7 +978,15 @@ class OpenGroupAPISpec: QuickSpec { var response: (capabilities: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities?), room: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Room?))? - OpenGroupAPI.capabilitiesAndRoom(for: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.capabilitiesAndRoom( + db, + for: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -953,7 +1034,16 @@ class OpenGroupAPISpec: QuickSpec { var response: (capabilities: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities?), room: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Room?))? - OpenGroupAPI.capabilitiesAndRoom(for: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .capabilitiesAndRoom( + db, + for: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1016,7 +1106,16 @@ class OpenGroupAPISpec: QuickSpec { var response: (capabilities: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities?), room: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Room?))? - OpenGroupAPI.capabilitiesAndRoom(for: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .capabilitiesAndRoom( + db, + for: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1096,7 +1195,15 @@ class OpenGroupAPISpec: QuickSpec { var response: (capabilities: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Capabilities?), room: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Room?))? - OpenGroupAPI.capabilitiesAndRoom(for: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI.capabilitiesAndRoom( + db, + for: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1145,16 +1252,20 @@ class OpenGroupAPISpec: QuickSpec { it("correctly sends the message") { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testServer", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1176,60 +1287,31 @@ class OpenGroupAPISpec: QuickSpec { expect(requestData?.urlString).to(equal("testServer/room/testRoom/message")) } - it("saves the received message timestamp to the database in milliseconds") { - var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) - .get { result in response = result } - .catch { requestError in error = requestError } - .retainUntilComplete() - - expect(response) - .toEventuallyNot( - beNil(), - timeout: .milliseconds(100) - ) - expect(error?.localizedDescription).to(beNil()) - expect(mockStorage) - .to(call(matchingParameters: true) { - $0.addReceivedMessageTimestamp(321000, using: anyAny()) - }) - } - context("when unblinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + } } it("signs the message correctly") { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testServer", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1249,21 +1331,27 @@ class OpenGroupAPISpec: QuickSpec { expect(requestBody.signature).to(equal("TestStandardSignature".data(using: .utf8))) } - it("fails to sign if there is no public key") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil) + it("fails to sign if there is no open group") { + mockStorage.write { db in + _ = try OpenGroup.deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1278,20 +1366,27 @@ class OpenGroupAPISpec: QuickSpec { } it("fails to sign if there is no user key pair") { - mockStorage.when { $0.getUserKeyPair() }.thenReturn(nil) + mockStorage.write { db in + _ = try Identity.filter(id: .x25519PublicKey).deleteAll(db) + _ = try Identity.filter(id: .x25519PrivateKey).deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1311,16 +1406,20 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1337,29 +1436,30 @@ class OpenGroupAPISpec: QuickSpec { context("when blinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + try Capability(openGroupServer: "testserver", variant: .blind, isMissing: false).insert(db) + } } it("signs the message correctly") { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1379,21 +1479,27 @@ class OpenGroupAPISpec: QuickSpec { expect(requestBody.signature).to(equal("TestSogsSignature".data(using: .utf8))) } - it("fails to sign if there is no public key") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil) + it("fails to sign if there is no open group") { + mockStorage.write { db in + _ = try OpenGroup.deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testServer", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1408,20 +1514,27 @@ class OpenGroupAPISpec: QuickSpec { } it("fails to sign if there is no ed key pair key") { - mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil) + mockStorage.write { db in + _ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db) + _ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1449,16 +1562,20 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - to: "testRoom", - on: "testServer", - whisperTo: nil, - whisperMods: false, - fileIds: nil, - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + plaintext: "test".data(using: .utf8)!, + to: "testRoom", + on: "testserver", + whisperTo: nil, + whisperMods: false, + fileIds: nil, + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1496,7 +1613,17 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)? - OpenGroupAPI.message(123, in: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .message( + db, + id: 123, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1526,21 +1653,32 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(Box.KeyPair(publicKey: [], secretKey: [])) + mockStorage.write { db in + _ = try Identity + .filter(id: .ed25519PublicKey) + .updateAll(db, Identity.Columns.data.set(to: Data())) + _ = try Identity + .filter(id: .ed25519SecretKey) + .updateAll(db, Identity.Columns.data.set(to: Data())) + } } it("correctly sends the update") { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1561,28 +1699,28 @@ class OpenGroupAPISpec: QuickSpec { context("when unblinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + } } it("signs the message correctly") { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testServer", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1602,20 +1740,26 @@ class OpenGroupAPISpec: QuickSpec { expect(requestBody.signature).to(equal("TestStandardSignature".data(using: .utf8))) } - it("fails to sign if there is no public key") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil) + it("fails to sign if there is no open group") { + mockStorage.write { db in + _ = try OpenGroup.deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1630,19 +1774,26 @@ class OpenGroupAPISpec: QuickSpec { } it("fails to sign if there is no user key pair") { - mockStorage.when { $0.getUserKeyPair() }.thenReturn(nil) + mockStorage.write { db in + _ = try Identity.filter(id: .x25519PublicKey).deleteAll(db) + _ = try Identity.filter(id: .x25519PrivateKey).deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1662,15 +1813,19 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testServer", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1687,28 +1842,29 @@ class OpenGroupAPISpec: QuickSpec { context("when blinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + try Capability(openGroupServer: "testserver", variant: .blind, isMissing: false).insert(db) + } } it("signs the message correctly") { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testServer", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1728,20 +1884,26 @@ class OpenGroupAPISpec: QuickSpec { expect(requestBody.signature).to(equal("TestSogsSignature".data(using: .utf8))) } - it("fails to sign if there is no public key") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil) + it("fails to sign if there is no open group") { + mockStorage.write { db in + _ = try OpenGroup.deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1756,19 +1918,26 @@ class OpenGroupAPISpec: QuickSpec { } it("fails to sign if there is no ed key pair key") { - mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil) + mockStorage.write { db in + _ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db) + _ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db) + } var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1796,15 +1965,19 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI - .messageUpdate( - 123, - plaintext: "test".data(using: .utf8)!, - fileIds: nil, - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messageUpdate( + db, + id: 123, + plaintext: "test".data(using: .utf8)!, + fileIds: nil, + in: "testRoom", + on: "testServer", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1829,7 +2002,17 @@ class OpenGroupAPISpec: QuickSpec { var response: (info: OnionRequestResponseInfoType, data: Data?)? - OpenGroupAPI.messageDelete(123, in: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .messageDelete( + db, + id: 123, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1864,13 +2047,17 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the request and handles the response correctly") { - OpenGroupAPI - .messagesDeleteAll( - "testUserId", - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .messagesDeleteAll( + db, + sessionId: "testUserId", + in: "testRoom", + on: "testServer", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1901,7 +2088,17 @@ class OpenGroupAPISpec: QuickSpec { var response: OnionRequestResponseInfoType? - OpenGroupAPI.pinMessage(id: 123, in: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .pinMessage( + db, + id: 123, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1930,7 +2127,17 @@ class OpenGroupAPISpec: QuickSpec { var response: OnionRequestResponseInfoType? - OpenGroupAPI.unpinMessage(id: 123, in: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .unpinMessage( + db, + id: 123, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1959,7 +2166,16 @@ class OpenGroupAPISpec: QuickSpec { var response: OnionRequestResponseInfoType? - OpenGroupAPI.unpinAll(in: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .unpinAll( + db, + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -1990,7 +2206,17 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.uploadFile([], to: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .uploadFile( + db, + bytes: [], + to: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2017,7 +2243,17 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.uploadFile([], to: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .uploadFile( + db, + bytes: [], + to: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2043,7 +2279,18 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.uploadFile([], fileName: "TestFileName", to: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .uploadFile( + db, + bytes: [], + fileName: "TestFileName", + to: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2070,7 +2317,17 @@ class OpenGroupAPISpec: QuickSpec { } dependencies = dependencies.with(onionApi: TestApi.self) - OpenGroupAPI.downloadFile(1, from: "testRoom", on: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .downloadFile( + db, + fileId: 1, + from: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2118,13 +2375,17 @@ class OpenGroupAPISpec: QuickSpec { it("correctly sends the message request") { var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.SendDirectMessageResponse)? - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - toInboxFor: "testUserId", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .send( + db, + ciphertext: "test".data(using: .utf8)!, + toInboxFor: "testUserId", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2145,32 +2406,6 @@ class OpenGroupAPISpec: QuickSpec { expect(requestData?.server).to(equal("testServer")) expect(requestData?.urlString).to(equal("testServer/inbox/testUserId")) } - - it("saves the received message timestamp to the database in milliseconds") { - var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.SendDirectMessageResponse)? - - OpenGroupAPI - .send( - "test".data(using: .utf8)!, - toInboxFor: "testUserId", - on: "testServer", - using: dependencies - ) - .get { result in response = result } - .catch { requestError in error = requestError } - .retainUntilComplete() - - expect(response) - .toEventuallyNot( - beNil(), - timeout: .milliseconds(100) - ) - expect(error?.localizedDescription).to(beNil()) - expect(mockStorage) - .to(call(matchingParameters: true) { - $0.addReceivedMessageTimestamp(321000, using: anyAny()) - }) - } } // MARK: - Users @@ -2190,14 +2425,18 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the request and handles the response correctly") { - OpenGroupAPI - .userBan( - "testUserId", - for: nil, - from: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userBan( + db, + sessionId: "testUserId", + for: nil, + from: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2217,14 +2456,18 @@ class OpenGroupAPISpec: QuickSpec { } it("does a global ban if no room tokens are provided") { - OpenGroupAPI - .userBan( - "testUserId", - for: nil, - from: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userBan( + db, + sessionId: "testUserId", + for: nil, + from: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2245,14 +2488,18 @@ class OpenGroupAPISpec: QuickSpec { } it("does room specific bans if room tokens are provided") { - OpenGroupAPI - .userBan( - "testUserId", - for: nil, - from: ["testRoom"], - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userBan( + db, + sessionId: "testUserId", + for: nil, + from: ["testRoom"], + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2288,13 +2535,17 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the request and handles the response correctly") { - OpenGroupAPI - .userUnban( - "testUserId", - from: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userUnban( + db, + sessionId: "testUserId", + from: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2314,13 +2565,17 @@ class OpenGroupAPISpec: QuickSpec { } it("does a global ban if no room tokens are provided") { - OpenGroupAPI - .userUnban( - "testUserId", - from: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userUnban( + db, + sessionId: "testUserId", + from: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2341,13 +2596,17 @@ class OpenGroupAPISpec: QuickSpec { } it("does room specific bans if room tokens are provided") { - OpenGroupAPI - .userUnban( - "testUserId", - from: ["testRoom"], - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userUnban( + db, + sessionId: "testUserId", + from: ["testRoom"], + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2383,16 +2642,20 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the request and handles the response correctly") { - OpenGroupAPI - .userModeratorUpdate( - "testUserId", - moderator: true, - admin: nil, - visible: true, - for: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userModeratorUpdate( + db, + sessionId: "testUserId", + moderator: true, + admin: nil, + visible: true, + for: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2412,16 +2675,20 @@ class OpenGroupAPISpec: QuickSpec { } it("does a global update if no room tokens are provided") { - OpenGroupAPI - .userModeratorUpdate( - "testUserId", - moderator: true, - admin: nil, - visible: true, - for: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userModeratorUpdate( + db, + sessionId: "testUserId", + moderator: true, + admin: nil, + visible: true, + for: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2442,16 +2709,20 @@ class OpenGroupAPISpec: QuickSpec { } it("does room specific updates if room tokens are provided") { - OpenGroupAPI - .userModeratorUpdate( - "testUserId", - moderator: true, - admin: nil, - visible: true, - for: ["testRoom"], - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userModeratorUpdate( + db, + sessionId: "testUserId", + moderator: true, + admin: nil, + visible: true, + for: ["testRoom"], + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2472,16 +2743,20 @@ class OpenGroupAPISpec: QuickSpec { } it("fails if neither moderator or admin are set") { - OpenGroupAPI - .userModeratorUpdate( - "testUserId", - moderator: nil, - admin: nil, - visible: true, - for: nil, - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userModeratorUpdate( + db, + sessionId: "testUserId", + moderator: nil, + admin: nil, + visible: true, + for: nil, + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2532,13 +2807,17 @@ class OpenGroupAPISpec: QuickSpec { } it("generates the request and handles the response correctly") { - OpenGroupAPI - .userBanAndDeleteAllMessages( - "testUserId", - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userBanAndDeleteAllMessages( + db, + sessionId: "testUserId", + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2558,13 +2837,17 @@ class OpenGroupAPISpec: QuickSpec { } it("bans the user from the specified room rather than globally") { - OpenGroupAPI - .userBanAndDeleteAllMessages( - "testUserId", - in: "testRoom", - on: "testServer", - using: dependencies - ) + mockStorage + .read { db in + OpenGroupAPI + .userBanAndDeleteAllMessages( + db, + sessionId: "testUserId", + in: "testRoom", + on: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2606,9 +2889,20 @@ class OpenGroupAPISpec: QuickSpec { } it("fails when there is no userEdKeyPair") { - mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil) + mockStorage.write { db in + _ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db) + _ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db) + } - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2623,9 +2917,19 @@ class OpenGroupAPISpec: QuickSpec { } it("fails when there is no serverPublicKey") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil) + mockStorage.write { db in + _ = try OpenGroup.deleteAll(db) + } - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2640,9 +2944,19 @@ class OpenGroupAPISpec: QuickSpec { } it("fails when the serverPublicKey is not a hex string") { - mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn("TestString!!!") + mockStorage.write { db in + _ = try OpenGroup.updateAll(db, OpenGroup.Columns.publicKey.set(to: "TestString!!!")) + } - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2658,18 +2972,22 @@ class OpenGroupAPISpec: QuickSpec { context("when unblinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + } } it("signs correctly") { - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2698,7 +3016,15 @@ class OpenGroupAPISpec: QuickSpec { it("fails when the signature is not generated") { mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn(nil) - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2715,18 +3041,23 @@ class OpenGroupAPISpec: QuickSpec { context("when blinded") { beforeEach { - mockStorage - .when { $0.getOpenGroupServer(name: any()) } - .thenReturn( - OpenGroupAPI.Server( - name: "testServer", - capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: []) - ) - ) + mockStorage.write { db in + _ = try Capability.deleteAll(db) + try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false).insert(db) + try Capability(openGroupServer: "testserver", variant: .blind, isMissing: false).insert(db) + } } it("signs correctly") { - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2756,7 +3087,15 @@ class OpenGroupAPISpec: QuickSpec { .when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) } .thenReturn(nil) - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() @@ -2775,7 +3114,15 @@ class OpenGroupAPISpec: QuickSpec { .when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) } .thenReturn(nil) - OpenGroupAPI.rooms(for: "testServer", using: dependencies) + mockStorage + .read { db in + OpenGroupAPI + .rooms( + db, + server: "testserver", + using: dependencies + ) + } .get { result in response = result } .catch { requestError in error = requestError } .retainUntilComplete() diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift index 240648781..55c01e1d6 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift @@ -113,7 +113,6 @@ class OpenGroupManagerSpec: QuickSpec { dependencies = OpenGroupManager.OGMDependencies( cache: Atomic(mockOGMCache), onionApi: TestCapabilitiesAndRoomApi.self, - identityManager: mockIdentityManager, generalCache: Atomic(mockGeneralCache), storage: mockStorage, sodium: mockSodium, diff --git a/SessionMessagingKitTests/Sending & Receiving/MessageReceiverDecryptionSpec.swift b/SessionMessagingKitTests/Sending & Receiving/MessageReceiverDecryptionSpec.swift index c59f20e23..abd8393aa 100644 --- a/SessionMessagingKitTests/Sending & Receiving/MessageReceiverDecryptionSpec.swift +++ b/SessionMessagingKitTests/Sending & Receiving/MessageReceiverDecryptionSpec.swift @@ -2,6 +2,8 @@ import Foundation import Sodium +import GRDB +import SessionUtilitiesKit import Quick import Nimble @@ -12,7 +14,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { // MARK: - Spec override func spec() { - var mockStorage: MockStorage! + var mockStorage: GRDBStorage! var mockSodium: MockSodium! var mockBox: MockBox! var mockGenericHash: MockGenericHash! @@ -23,7 +25,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { describe("a MessageReceiver") { beforeEach { - mockStorage = MockStorage() + mockStorage = GRDBStorage(customWriter: DatabaseQueue()) mockSodium = MockSodium() mockBox = MockBox() mockGenericHash = MockGenericHash() @@ -45,14 +47,10 @@ class MessageReceiverDecryptionSpec: QuickSpec { nonceGenerator24: mockNonce24Generator ) - mockStorage - .when { $0.getUserED25519KeyPair() } - .thenReturn( - Box.KeyPair( - publicKey: Data(hex: TestConstants.edPublicKey).bytes, - secretKey: Data(hex: TestConstants.edSecretKey).bytes - ) - ) + mockStorage.write { db in + try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db) + try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db) + } mockBox .when { $0.open( @@ -109,9 +107,9 @@ class MessageReceiverDecryptionSpec: QuickSpec { "sFMhE5G4PbRtQFey1hsxLl221Qivc3ayaX2Mm/X89Dl8e45BC+Lb/KU9EdesxIK4pVgYXs9XrMtX3v8" + "dt0eBaXneOBfr7qB8pHwwMZjtkOu1ED07T9nszgbWabBphUfWXe2U9K3PTRisSCI=" )!, - using: try! ECKeyPair( - publicKeyData: Data.data(fromHex: TestConstants.publicKey)!, - privateKeyData: Data.data(fromHex: TestConstants.privateKey)! + using: Box.KeyPair( + publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes, + secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes ), dependencies: Dependencies() ) @@ -135,14 +133,14 @@ class MessageReceiverDecryptionSpec: QuickSpec { expect { try MessageReceiver.decryptWithSessionProtocol( ciphertext: "TestMessage".data(using: .utf8)!, - using: try! ECKeyPair( - publicKeyData: Data.data(fromHex: TestConstants.publicKey)!, - privateKeyData: Data.data(fromHex: TestConstants.privateKey)! + using: Box.KeyPair( + publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes, + secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes ), dependencies: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if the open message is too short") { @@ -159,14 +157,14 @@ class MessageReceiverDecryptionSpec: QuickSpec { expect { try MessageReceiver.decryptWithSessionProtocol( ciphertext: "TestMessage".data(using: .utf8)!, - using: try! ECKeyPair( - publicKeyData: Data.data(fromHex: TestConstants.publicKey)!, - privateKeyData: Data.data(fromHex: TestConstants.privateKey)! + using: Box.KeyPair( + publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes, + secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes ), dependencies: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if it cannot verify the message") { @@ -177,14 +175,14 @@ class MessageReceiverDecryptionSpec: QuickSpec { expect { try MessageReceiver.decryptWithSessionProtocol( ciphertext: "TestMessage".data(using: .utf8)!, - using: try! ECKeyPair( - publicKeyData: Data.data(fromHex: TestConstants.publicKey)!, - privateKeyData: Data.data(fromHex: TestConstants.privateKey)! + using: Box.KeyPair( + publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes, + secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes ), dependencies: dependencies ) } - .to(throwError(MessageReceiver.Error.invalidSignature)) + .to(throwError(MessageReceiverError.invalidSignature)) } it("throws an error if it cannot get the senders x25519 public key") { @@ -193,14 +191,14 @@ class MessageReceiverDecryptionSpec: QuickSpec { expect { try MessageReceiver.decryptWithSessionProtocol( ciphertext: "TestMessage".data(using: .utf8)!, - using: try! ECKeyPair( - publicKeyData: Data.data(fromHex: TestConstants.publicKey)!, - privateKeyData: Data.data(fromHex: TestConstants.privateKey)! + using: Box.KeyPair( + publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes, + secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes ), dependencies: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } } @@ -263,7 +261,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if it cannot get the blinded keyPair") { @@ -288,7 +286,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if it cannot get the decryption key") { @@ -321,7 +319,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if the data version is not 0") { @@ -342,7 +340,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if it cannot decrypt the data") { @@ -367,7 +365,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if the inner bytes are too short") { @@ -392,7 +390,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } it("throws an error if it cannot generate the blinding factor") { @@ -417,7 +415,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.invalidSignature)) + .to(throwError(MessageReceiverError.invalidSignature)) } it("throws an error if it cannot generate the combined key") { @@ -442,7 +440,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.invalidSignature)) + .to(throwError(MessageReceiverError.invalidSignature)) } it("throws an error if the combined key does not match kA") { @@ -467,7 +465,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.invalidSignature)) + .to(throwError(MessageReceiverError.invalidSignature)) } it("throws an error if it cannot get the senders x25519 public key") { @@ -492,7 +490,7 @@ class MessageReceiverDecryptionSpec: QuickSpec { using: dependencies ) } - .to(throwError(MessageReceiver.Error.decryptionFailed)) + .to(throwError(MessageReceiverError.decryptionFailed)) } } } diff --git a/SessionMessagingKitTests/_TestUtilities/MockedExtensions.swift b/SessionMessagingKitTests/_TestUtilities/MockedExtensions.swift index cf0b1152c..021b59581 100644 --- a/SessionMessagingKitTests/_TestUtilities/MockedExtensions.swift +++ b/SessionMessagingKitTests/_TestUtilities/MockedExtensions.swift @@ -8,8 +8,8 @@ extension OpenGroup: Mocked { server: any(), roomToken: any(), publicKey: TestConstants.publicKey, - name: any(), isActive: any(), + name: any(), roomDescription: any(), imageId: any(), imageData: any(), @@ -22,7 +22,7 @@ extension OpenGroup: Mocked { } extension VisibleMessage: Mocked { - static var mockValue: VisibleMessage = VisibleMessage() + static var mockValue: VisibleMessage = VisibleMessage(text: "") } extension BlindedIdMapping: Mocked { diff --git a/SessionNotificationServiceExtension/NSENotificationPresenter.swift b/SessionNotificationServiceExtension/NSENotificationPresenter.swift index 491c55275..2f487f70e 100644 --- a/SessionNotificationServiceExtension/NSENotificationPresenter.swift +++ b/SessionNotificationServiceExtension/NSENotificationPresenter.swift @@ -13,7 +13,7 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { guard Date().timeIntervalSince1970 > (thread.mutedUntilTimestamp ?? 0) else { return } let userPublicKey: String = getUserHexEncodedPublicKey(db) - let isMessageRequest: Bool = thread.isMessageRequest(db) + let isMessageRequest: Bool = thread.isMessageRequest(db, includeNonVisible: true) // If the thread is a message request and the user hasn't hidden message requests then we need // to check if this is the only message request thread (group threads can't be message requests @@ -21,13 +21,13 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { // notification regardless of how many message requests there are) if thread.variant == .contact { if isMessageRequest && !db[.hasHiddenMessageRequests] { - let numMessageRequestThreads: Int? = (try? SessionThread - .messageRequestsCountQuery(userPublicKey: userPublicKey) - .fetchOne(db)) + let numMessageRequestThreads: Int = (try? SessionThread + .messageRequestsQuery(userPublicKey: userPublicKey, includeNonVisible: true) + .fetchCount(db)) .defaulting(to: 0) // Allow this to show a notification if there are no message requests (ie. this is the first one) - guard (numMessageRequestThreads ?? 0) == 0 else { return } + guard numMessageRequestThreads == 0 else { return } } else if isMessageRequest && db[.hasHiddenMessageRequests] { // If there are other interactions on this thread already then don't show the notification