From 8344ed5d811b49f3aee5d3e17d6bab661090db10 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Tue, 29 Mar 2022 10:15:22 +1100 Subject: [PATCH] Fixed the unit tests broken by the merge Added the ability to mock the GeneralCache data Added a couple additional tests to validate some updated OpenGroupManager code --- Session.xcodeproj/project.pbxproj | 4 + Session/Settings/NukeDataModal.swift | 4 +- .../Open Groups/OpenGroupManager.swift | 2 + .../Utilities/Dependencies.swift | 8 ++ SessionMessagingKit/Utilities/General.swift | 15 +- .../Open Groups/OpenGroupManagerSpec.swift | 128 +++++++++++++++++- .../_TestUtilities/DependencyExtensions.swift | 2 + .../_TestUtilities/MockGeneralCache.swift | 12 ++ .../_TestUtilities/MockStorage.swift | 26 +++- .../OGMDependencyExtensions.swift | 2 + 10 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 SessionMessagingKitTests/_TestUtilities/MockGeneralCache.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 039414203..b7509e9fd 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -890,6 +890,7 @@ FDC438CF27BCA45400C60D73 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CE27BCA45400C60D73 /* Server.swift */; }; FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; }; FDFD645B27F26D4600808CA1 /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */; }; + FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -2049,6 +2050,7 @@ FDC438CC27BC641200C60D73 /* Set+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Set+Utilities.swift"; sourceTree = ""; }; FDC438CE27BCA45400C60D73 /* Server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = ""; }; + FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGeneralCache.swift; sourceTree = ""; }; FEDBAE1B98C49BBE8C87F575 /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-ExtendedDependencies-SessionUtilitiesKit.debug.xcconfig"; sourceTree = ""; }; FF9BA33D021B115B1F5B4E46 /* Pods-SessionMessagingKit.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SessionMessagingKit.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SessionMessagingKit/Pods-SessionMessagingKit.app store release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -4122,6 +4124,7 @@ children = ( FDC438BC27BB2AB400C60D73 /* Mockable.swift */, FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */, + FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */, FDC4389C27BA01F000C60D73 /* MockStorage.swift */, FD859EF327C2F49200510D0C /* MockSodium.swift */, FD3C906E27E43E8700CD579F /* MockBox.swift */, @@ -5775,6 +5778,7 @@ FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */, FD859EF627C2F52C00510D0C /* MockSign.swift in Sources */, FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */, + FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */, FD078E5A27E29F09000769AF /* MockNonce16Generator.swift in Sources */, FD3C906D27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift in Sources */, FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */, diff --git a/Session/Settings/NukeDataModal.swift b/Session/Settings/NukeDataModal.swift index dba878a22..d08b30d9a 100644 --- a/Session/Settings/NukeDataModal.swift +++ b/Session/Settings/NukeDataModal.swift @@ -128,7 +128,7 @@ final class NukeDataModal : Modal { appDelegate.forceSyncConfigurationNowIfNeeded().ensure(on: DispatchQueue.main) { self?.dismiss(animated: true, completion: nil) // Dismiss the loader UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later - General.Cache.cachedEncodedPublicKey.mutate { $0 = nil } // Remove the cached key so it gets re-cached on next access + General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access NotificationCenter.default.post(name: .dataNukeRequested, object: nil) }.retainUntilComplete() } @@ -140,7 +140,7 @@ final class NukeDataModal : Modal { self?.dismiss(animated: true, completion: nil) // Dismiss the loader let potentiallyMaliciousSnodes = confirmations.compactMap { $0.value == false ? $0.key : nil } if potentiallyMaliciousSnodes.isEmpty { - General.Cache.cachedEncodedPublicKey.mutate { $0 = nil } // Remove the cached key so it gets re-cached on next access + General.cache.mutate { $0.encodedPublicKey = nil } // Remove the cached key so it gets re-cached on next access UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later NotificationCenter.default.post(name: .dataNukeRequested, object: nil) } else { diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index cd1bee054..ad777f3d4 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -742,6 +742,7 @@ extension OpenGroupManager { cache: Atomic? = nil, onionApi: OnionRequestAPIType.Type? = nil, identityManager: IdentityManagerProtocol? = nil, + generalCache: Atomic? = nil, storage: SessionMessagingKitStorageProtocol? = nil, sodium: SodiumType? = nil, box: BoxType? = nil, @@ -759,6 +760,7 @@ extension OpenGroupManager { super.init( onionApi: onionApi, identityManager: identityManager, + generalCache: generalCache, storage: storage, sodium: sodium, box: box, diff --git a/SessionMessagingKit/Utilities/Dependencies.swift b/SessionMessagingKit/Utilities/Dependencies.swift index 7eb2b56fe..b4709d0f0 100644 --- a/SessionMessagingKit/Utilities/Dependencies.swift +++ b/SessionMessagingKit/Utilities/Dependencies.swift @@ -18,6 +18,12 @@ public class Dependencies { set { _identityManager = newValue } } + internal var _generalCache: Atomic? + public var generalCache: Atomic { + get { Dependencies.getValueSettingIfNull(&_generalCache) { General.cache } } + set { _generalCache = newValue } + } + internal var _storage: SessionMessagingKitStorageProtocol? public var storage: SessionMessagingKitStorageProtocol { get { Dependencies.getValueSettingIfNull(&_storage) { SNMessagingKitConfiguration.shared.storage } } @@ -89,6 +95,7 @@ public class Dependencies { public init( onionApi: OnionRequestAPIType.Type? = nil, identityManager: IdentityManagerProtocol? = nil, + generalCache: Atomic? = nil, storage: SessionMessagingKitStorageProtocol? = nil, sodium: SodiumType? = nil, box: BoxType? = nil, @@ -103,6 +110,7 @@ public class Dependencies { ) { _onionApi = onionApi _identityManager = identityManager + _generalCache = generalCache _storage = storage _sodium = sodium _box = box diff --git a/SessionMessagingKit/Utilities/General.swift b/SessionMessagingKit/Utilities/General.swift index 61d164333..017357c46 100644 --- a/SessionMessagingKit/Utilities/General.swift +++ b/SessionMessagingKit/Utilities/General.swift @@ -1,9 +1,16 @@ import Foundation +import SessionUtilitiesKit + +public protocol GeneralCacheType { + var encodedPublicKey: String? { get set } +} public enum General { - public enum Cache { - public static var cachedEncodedPublicKey: Atomic = Atomic(nil) + public class Cache: GeneralCacheType { + public var encodedPublicKey: String? = nil } + + public static var cache: Atomic = Atomic(Cache()) } @objc(SNGeneralUtilities) @@ -14,10 +21,10 @@ public class GeneralUtilities: NSObject { } public func getUserHexEncodedPublicKey(using dependencies: Dependencies = Dependencies()) -> String { - if let cachedKey: String = General.Cache.cachedEncodedPublicKey.wrappedValue { return cachedKey } + if let cachedKey: String = dependencies.generalCache.wrappedValue.encodedPublicKey { return cachedKey } if let keyPair = dependencies.identityManager.identityKeyPair() { // Can be nil under some circumstances - General.Cache.cachedEncodedPublicKey.mutate { $0 = keyPair.hexEncodedPublicKey } + dependencies.generalCache.mutate { $0.encodedPublicKey = keyPair.hexEncodedPublicKey } return keyPair.hexEncodedPublicKey } diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift index d01388efa..1205194b2 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift @@ -71,6 +71,7 @@ class OpenGroupManagerSpec: QuickSpec { override func spec() { var mockOGMCache: MockOGMCache! var mockIdentityManager: MockIdentityManager! + var mockGeneralCache: MockGeneralCache! var mockStorage: MockStorage! var mockSodium: MockSodium! var mockAeadXChaCha20Poly1305Ietf: MockAeadXChaCha20Poly1305Ietf! @@ -100,6 +101,7 @@ class OpenGroupManagerSpec: QuickSpec { beforeEach { mockOGMCache = MockOGMCache() mockIdentityManager = MockIdentityManager() + mockGeneralCache = MockGeneralCache() mockStorage = MockStorage() mockSodium = MockSodium() mockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf() @@ -112,6 +114,7 @@ class OpenGroupManagerSpec: QuickSpec { cache: Atomic(mockOGMCache), onionApi: TestCapabilitiesAndRoomApi.self, identityManager: mockIdentityManager, + generalCache: Atomic(mockGeneralCache), storage: mockStorage, sodium: mockSodium, genericHash: mockGenericHash, @@ -220,6 +223,7 @@ class OpenGroupManagerSpec: QuickSpec { privateKeyData: Data.data(fromHex: TestConstants.privateKey)! ) ) + mockGeneralCache.when { $0.encodedPublicKey }.thenReturn("05\(TestConstants.publicKey)") mockStorage .when { $0.write(with: { _ in }) } .then { [testTransaction] args in (args.first as? ((Any) -> Void))?(testTransaction! as Any) } @@ -2046,6 +2050,23 @@ class OpenGroupManagerSpec: QuickSpec { ) } .thenReturn(()) + mockStorage + .when { + $0.addOpenGroupServerIdLookup( + any(), + tsMessageId: any(), + in: any(), + on: any(), + using: testTransaction + ) + } + .thenReturn(()) + mockStorage + .when { $0.getOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn(nil) + mockStorage + .when { $0.removeOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn(nil) mockStorage.when { $0.getUserPublicKey() }.thenReturn("05\(TestConstants.publicKey)") mockStorage.when { $0.getReceivedMessageTimestamps(using: testTransaction as Any) }.thenReturn([]) mockStorage.when { $0.addReceivedMessageTimestamp(any(), using: testTransaction as Any) }.thenReturn(()) @@ -2196,6 +2217,31 @@ class OpenGroupManagerSpec: QuickSpec { ) } + it("adds the open group server id lookup") { + OpenGroupManager.handleMessages( + [testMessage], + for: "testRoom", + on: "testServer", + isBackgroundPoll: false, + using: testTransaction, + dependencies: dependencies + ) + + expect(mockStorage) + .toEventually( + call(matchingParameters: true) { + $0.addOpenGroupServerIdLookup( + 127, + tsMessageId: "TestMessageId", + in: "testRoom", + on: "testserver", + using: testTransaction + ) + }, + timeout: .milliseconds(50) + ) + } + it("processes valid messages when combined with invalid ones") { OpenGroupManager.handleMessages( [ @@ -2229,7 +2275,16 @@ class OpenGroupManagerSpec: QuickSpec { context("with no data") { it("deletes the message if we have the message") { - testTransaction.mockData[.objectForKey] = testGroupThread + mockStorage + .when { $0.getOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn( + OpenGroupServerIdLookup( + server: "testServer", + room: "testRoom", + serverId: 127, + tsMessageId: "TestMessageId" + ) + ) OpenGroupManager.handleMessages( [ @@ -2260,13 +2315,63 @@ class OpenGroupManagerSpec: QuickSpec { ) } - it("does nothing if we do not have the thread") { - testTransaction.mockData[.objectForKey] = nil + it("deletes the open group server lookup id if we have the message") { + mockStorage + .when { $0.getOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn( + OpenGroupServerIdLookup( + server: "testServer", + room: "testRoom", + serverId: 127, + tsMessageId: "TestMessageId" + ) + ) OpenGroupManager.handleMessages( [ OpenGroupAPI.Message( - id: 1, + id: 127, + sender: "05\(TestConstants.publicKey)", + posted: 123, + edited: nil, + seqNo: 123, + whisper: false, + whisperMods: false, + whisperTo: nil, + base64EncodedData: nil, + base64EncodedSignature: nil + ) + ], + for: "testRoom", + on: "testServer", + isBackgroundPoll: false, + using: testTransaction, + dependencies: dependencies + ) + + expect(mockStorage) + .toEventually( + call(matchingParameters: true) { + $0.removeOpenGroupServerIdLookup( + 127, + in: "testRoom", + on: "testServer", + using: testTransaction + ) + }, + timeout: .milliseconds(50) + ) + } + + it("does nothing if we do not have the lookup") { + mockStorage + .when { $0.getOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn(nil) + + OpenGroupManager.handleMessages( + [ + OpenGroupAPI.Message( + id: 127, sender: "05\(TestConstants.publicKey)", posted: 123, edited: nil, @@ -2293,8 +2398,17 @@ class OpenGroupManagerSpec: QuickSpec { } it("does nothing if we do not have the message") { - testGroupThread.mockData[.interactions] = [testInteraction] - testTransaction.mockData[.objectForKey] = testGroupThread + mockStorage + .when { $0.getOpenGroupServerIdLookup(any(), in: any(), on: any(), using: testTransaction) } + .thenReturn( + OpenGroupServerIdLookup( + server: "testServer", + room: "testRoom", + serverId: 127, + tsMessageId: "TestMessageId" + ) + ) + testTransaction.mockData[.objectForKey] = nil OpenGroupManager.handleMessages( [ @@ -3010,6 +3124,7 @@ class OpenGroupManagerSpec: QuickSpec { privateKeyData: Data.data(fromHex: TestConstants.privateKey)! ) ) + mockGeneralCache.when { $0.encodedPublicKey }.thenReturn("05\(otherKey)") mockStorage .when { $0.getUserED25519KeyPair() } .thenReturn( @@ -3158,6 +3273,7 @@ class OpenGroupManagerSpec: QuickSpec { privateKeyData: Data.data(fromHex: TestConstants.privateKey)! ) ) + mockGeneralCache.when { $0.encodedPublicKey }.thenReturn("05\(otherKey)") mockStorage .when { $0.getUserED25519KeyPair() } .thenReturn( diff --git a/SessionMessagingKitTests/_TestUtilities/DependencyExtensions.swift b/SessionMessagingKitTests/_TestUtilities/DependencyExtensions.swift index 4002f5af6..cf5021489 100644 --- a/SessionMessagingKitTests/_TestUtilities/DependencyExtensions.swift +++ b/SessionMessagingKitTests/_TestUtilities/DependencyExtensions.swift @@ -9,6 +9,7 @@ extension Dependencies { public func with( onionApi: OnionRequestAPIType.Type? = nil, identityManager: IdentityManagerProtocol? = nil, + generalCache: Atomic? = nil, storage: SessionMessagingKitStorageProtocol? = nil, sodium: SodiumType? = nil, box: BoxType? = nil, @@ -24,6 +25,7 @@ extension Dependencies { return Dependencies( onionApi: (onionApi ?? self._onionApi), identityManager: (identityManager ?? self._identityManager), + generalCache: (generalCache ?? self._generalCache), storage: (storage ?? self._storage), sodium: (sodium ?? self._sodium), box: (box ?? self._box), diff --git a/SessionMessagingKitTests/_TestUtilities/MockGeneralCache.swift b/SessionMessagingKitTests/_TestUtilities/MockGeneralCache.swift new file mode 100644 index 000000000..9a0d5d0a6 --- /dev/null +++ b/SessionMessagingKitTests/_TestUtilities/MockGeneralCache.swift @@ -0,0 +1,12 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation + +@testable import SessionMessagingKit + +class MockGeneralCache: Mock, GeneralCacheType { + var encodedPublicKey: String? { + get { return accept() as? String } + set { accept(args: [newValue]) } + } +} diff --git a/SessionMessagingKitTests/_TestUtilities/MockStorage.swift b/SessionMessagingKitTests/_TestUtilities/MockStorage.swift index 7440c80da..093345bfe 100644 --- a/SessionMessagingKitTests/_TestUtilities/MockStorage.swift +++ b/SessionMessagingKitTests/_TestUtilities/MockStorage.swift @@ -27,6 +27,9 @@ class MockStorage: Mock, SessionMessagingKit func getUserKeyPair() -> ECKeyPair? { return accept() as? ECKeyPair } func getUserED25519KeyPair() -> Box.KeyPair? { return accept() as? Box.KeyPair } func getUser() -> Contact? { return accept() as? Contact } + func getUser(using transaction: YapDatabaseReadTransaction?) -> Contact? { + return accept(args: [transaction]) as? Contact + } // MARK: - Contacts @@ -74,11 +77,17 @@ class MockStorage: Mock, SessionMessagingKit accept(args: [groupPublicKey, transaction]) } func getUserClosedGroupPublicKeys() -> Set { return accept() as! Set } - func getZombieMembers(for groupPublicKey: String) -> Set { return accept() as! Set } + func getUserClosedGroupPublicKeys(using transaction: YapDatabaseReadTransaction) -> Set { + return accept(args: [transaction]) as! Set + } + func getZombieMembers(for groupPublicKey: String) -> Set { return accept(args: [groupPublicKey]) as! Set } func setZombieMembers(for groupPublicKey: String, to zombies: Set, using transaction: Any) { accept(args: [groupPublicKey, zombies, transaction]) } - func isClosedGroup(_ publicKey: String) -> Bool { return accept() as! Bool } + func isClosedGroup(_ publicKey: String) -> Bool { return accept(args: [publicKey]) as! Bool } + func isClosedGroup(_ publicKey: String, using transaction: YapDatabaseReadTransaction) -> Bool { + return accept(args: [publicKey, transaction]) as! Bool + } // MARK: - Jobs @@ -141,6 +150,19 @@ class MockStorage: Mock, SessionMessagingKit accept(args: [room, server, transaction]) } + func getOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadTransaction) -> OpenGroupServerIdLookup? { + return accept(args: [serverId, room, server, transaction]) as? OpenGroupServerIdLookup + } + func addOpenGroupServerIdLookup(_ serverId: UInt64?, tsMessageId: String?, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction) { + accept(args: [serverId, tsMessageId, room, server, transaction]) + } + func addOpenGroupServerIdLookup(_ lookup: OpenGroupServerIdLookup, using transaction: YapDatabaseReadWriteTransaction) { + accept(args: [lookup, transaction]) + } + func removeOpenGroupServerIdLookup(_ serverId: UInt64, in room: String, on server: String, using transaction: YapDatabaseReadWriteTransaction) { + accept(args: [serverId, room, server, transaction]) + } + func getOpenGroupInboxLatestMessageId(for server: String) -> Int64? { return accept(args: [server]) as? Int64 } func setOpenGroupInboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) { accept(args: [server, newValue, transaction]) diff --git a/SessionMessagingKitTests/_TestUtilities/OGMDependencyExtensions.swift b/SessionMessagingKitTests/_TestUtilities/OGMDependencyExtensions.swift index fce02cfeb..dd0a8413b 100644 --- a/SessionMessagingKitTests/_TestUtilities/OGMDependencyExtensions.swift +++ b/SessionMessagingKitTests/_TestUtilities/OGMDependencyExtensions.swift @@ -10,6 +10,7 @@ extension OpenGroupManager.OGMDependencies { cache: Atomic? = nil, onionApi: OnionRequestAPIType.Type? = nil, identityManager: IdentityManagerProtocol? = nil, + generalCache: Atomic? = nil, storage: SessionMessagingKitStorageProtocol? = nil, sodium: SodiumType? = nil, box: BoxType? = nil, @@ -26,6 +27,7 @@ extension OpenGroupManager.OGMDependencies { cache: (cache ?? self._mutableCache), onionApi: (onionApi ?? self._onionApi), identityManager: (identityManager ?? self._identityManager), + generalCache: (generalCache ?? self._generalCache), storage: (storage ?? self._storage), sodium: (sodium ?? self._sodium), box: (box ?? self._box),