Further work on OpenGroupManager tests

Added tests for the OpenGroupManager handleMessages logic
Updated some logic to do an optional unwrap instead of a forced one to prevent the code from crashing when running unit tests
Fixed some tests which were flaky (could fail if other tests ran at the same time on other threads)
Fixed a minor potential bug where a message with no sender data wouldn't get deleted (shouldn't be dependant on the sender value being populated)
This commit is contained in:
Morgan Pretty 2022-03-16 17:43:40 +11:00
parent b1684f6b23
commit 37f4d2ecca
8 changed files with 416 additions and 33 deletions

View File

@ -781,6 +781,7 @@
FD078E4F27E175F1000769AF /* DependencyExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4E27E175F1000769AF /* DependencyExtensions.swift */; };
FD078E5227E1760A000769AF /* OGMDependencyExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5127E1760A000769AF /* OGMDependencyExtensions.swift */; };
FD078E5427E197CA000769AF /* OpenGroupManagerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909D27D85751005DAE71 /* OpenGroupManagerSpec.swift */; };
FD078E5827E1B831000769AF /* TestIncomingMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5727E1B831000769AF /* TestIncomingMessage.swift */; };
FD0BA51B27CD88EC00CC6805 /* BlindedIdMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0BA51A27CD88EC00CC6805 /* BlindedIdMapping.swift */; };
FD0BA51D27CDC34600CC6805 /* SOGSV4Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0BA51C27CDC34600CC6805 /* SOGSV4Migration.swift */; };
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */; };
@ -1926,6 +1927,7 @@
FD078E4C27E17156000769AF /* MockOGMCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOGMCache.swift; sourceTree = "<group>"; };
FD078E4E27E175F1000769AF /* DependencyExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependencyExtensions.swift; sourceTree = "<group>"; };
FD078E5127E1760A000769AF /* OGMDependencyExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OGMDependencyExtensions.swift; sourceTree = "<group>"; };
FD078E5727E1B831000769AF /* TestIncomingMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestIncomingMessage.swift; sourceTree = "<group>"; };
FD0BA51A27CD88EC00CC6805 /* BlindedIdMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindedIdMapping.swift; sourceTree = "<group>"; };
FD0BA51C27CDC34600CC6805 /* SOGSV4Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSV4Migration.swift; sourceTree = "<group>"; };
FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; };
@ -4060,6 +4062,7 @@
FDC2909F27D85826005DAE71 /* TestThread.swift */,
FDC290B627E00FDB005DAE71 /* TestGroupThread.swift */,
FDC290A127D85890005DAE71 /* TestInteraction.swift */,
FD078E5727E1B831000769AF /* TestIncomingMessage.swift */,
FDC290AB27DB0B1C005DAE71 /* MockedExtensions.swift */,
FD078E4E27E175F1000769AF /* DependencyExtensions.swift */,
FD078E5127E1760A000769AF /* OGMDependencyExtensions.swift */,
@ -5657,6 +5660,7 @@
buildActionMask = 2147483647;
files = (
FDC290AC27DB0B1C005DAE71 /* MockedExtensions.swift in Sources */,
FD078E5827E1B831000769AF /* TestIncomingMessage.swift in Sources */,
FD859EFA27C2F5C500510D0C /* MockGenericHash.swift in Sources */,
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */,
FDC290AF27DFEE97005DAE71 /* TestTransaction.swift in Sources */,

View File

@ -321,6 +321,7 @@ public final class OpenGroupManager: NSObject {
// Sorting the messages by server ID before importing them fixes an issue where messages
// that quote older messages can't find those older messages
let openGroupID = "\(server).\(roomToken)"
let openGroupIdData: Data = LKGroupUtilities.getEncodedOpenGroupIDAsData(openGroupID)
let sortedMessages: [OpenGroupAPI.Message] = messages
.sorted { lhs, rhs in lhs.id < rhs.id }
let seqNo: Int64? = sortedMessages.map { $0.seqNo }.max()
@ -333,11 +334,12 @@ public final class OpenGroupManager: NSObject {
// Process the messages
sortedMessages.forEach { message in
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString), let sender: String = message.sender else {
guard let base64EncodedString: String = message.base64EncodedData, let data = Data(base64Encoded: base64EncodedString) else {
// A message with no data has been deleted so add it to the list to remove
messageServerIDsToRemove.append(UInt64(message.id))
return
}
guard let sender: String = message.sender else { return } // Need a sender in order to process the message
// Note: The `posted` value is in seconds but all messages in the database use milliseconds for timestamps
let envelope = SNProtoEnvelope.builder(type: .sessionMessage, timestamp: UInt64(floor(message.posted * 1000)))
@ -356,7 +358,7 @@ public final class OpenGroupManager: NSObject {
// Handle any deletions that are needed
guard !messageServerIDsToRemove.isEmpty else { return }
guard let thread = TSGroupThread.fetch(groupId: openGroupID, transaction: transaction) else { return }
guard let thread = TSGroupThread.fetch(groupId: openGroupIdData, transaction: transaction) else { return }
var messagesToRemove: [TSMessage] = []

View File

@ -411,12 +411,17 @@ extension MessageReceiver {
}
// Notify the user if needed
guard let tsIncomingMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) as? TSIncomingMessage,
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction) else { return tsMessageID }
guard
let tsIncomingMessage = TSMessage.fetch(uniqueId: tsMessageID, transaction: transaction) as? TSIncomingMessage,
let thread = TSThread.fetch(uniqueId: threadID, transaction: transaction)
else {
return tsMessageID
}
// Use the same identifier for notifications when in backgroud polling to prevent spam
let notificationIdentifier = isBackgroundPoll ? thread.uniqueId : UUID().uuidString
tsIncomingMessage.setNotificationIdentifier(notificationIdentifier, transaction: transaction)
SSKEnvironment.shared.notificationsManager!.notifyUser(for: tsIncomingMessage, in: thread, transaction: transaction)
SSKEnvironment.shared.notificationsManager?.notifyUser(for: tsIncomingMessage, in: thread, transaction: transaction)
return tsMessageID
}

View File

@ -79,10 +79,12 @@ class OpenGroupManagerSpec: QuickSpec {
var dependencies: OpenGroupManager.OGMDependencies!
var testInteraction: TestInteraction!
var testIncomingMessage: TestIncomingMessage!
var testGroupThread: TestGroupThread!
var testTransaction: TestTransaction!
var testOpenGroup: OpenGroup!
var testPollInfo: OpenGroupAPI.RoomPollInfo!
var testMessage: OpenGroupAPI.Message!
var cache: OpenGroupManager.Cache!
var openGroupManager: OpenGroupManager!
@ -116,6 +118,9 @@ class OpenGroupManagerSpec: QuickSpec {
testInteraction.mockData[.uniqueId] = "TestInteractionId"
testInteraction.mockData[.timestamp] = UInt64(123)
testIncomingMessage = TestIncomingMessage(uniqueId: "TestMessageId")
testIncomingMessage.openGroupServerMessageID = 127
testGroupThread = TestGroupThread()
testGroupThread.mockData[.uniqueId] = "TestGroupId"
testGroupThread.mockData[.groupModel] = TSGroupModel(
@ -127,7 +132,7 @@ class OpenGroupManagerSpec: QuickSpec {
adminIds: [],
moderatorIds: []
)
testGroupThread.mockData[.interactions] = [testInteraction]
testGroupThread.mockData[.interactions] = [testInteraction, testIncomingMessage]
testTransaction = TestTransaction()
testTransaction.mockData[.objectForKey] = testGroupThread
@ -157,6 +162,30 @@ class OpenGroupManagerSpec: QuickSpec {
defaultUpload: nil,
details: TestCapabilitiesAndRoomApi.roomData
)
testMessage = OpenGroupAPI.Message(
id: 127,
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
whisperTo: nil,
base64EncodedData: [
"Cg0KC1Rlc3RNZXNzYWdlg",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AAAAAAAAAAAAAAAAAAAAA",
"AA"
].joined(),
base64EncodedSignature: nil
)
mockStorage
.when { $0.write(with: { _ in }) }
@ -407,6 +436,8 @@ class OpenGroupManagerSpec: QuickSpec {
// MARK: - Adding & Removing
// MARK: - --add
context("when adding") {
beforeEach {
mockStorage.when { $0.removeOpenGroupSequenceNumber(for: any(), on: any(), using: anyAny()) }.thenReturn(())
@ -428,6 +459,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("resets the sequence number of the open group") {
var didComplete: Bool = false // Prevent multi-threading test bugs
openGroupManager
.add(
roomToken: "testRoom",
@ -437,21 +470,25 @@ class OpenGroupManagerSpec: QuickSpec {
using: testTransaction,
dependencies: dependencies
)
.map { _ -> Void in didComplete = true }
.retainUntilComplete()
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(
call(.exactly(times: 1)) {
$0.removeOpenGroupSequenceNumber(
for: "testRoom",
on: "testServer",
using: testTransaction as Any
using: testTransaction! as Any
)
}
)
}
it("sets the public key of the open group server") {
var didComplete: Bool = false // Prevent multi-threading test bugs
openGroupManager
.add(
roomToken: "testRoom",
@ -461,21 +498,25 @@ class OpenGroupManagerSpec: QuickSpec {
using: testTransaction,
dependencies: dependencies
)
.map { _ -> Void in didComplete = true }
.retainUntilComplete()
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(
call(.exactly(times: 1)) {
$0.setOpenGroupPublicKey(
for: "testRoom",
to: "testKey",
using: testTransaction as Any
using: testTransaction! as Any
)
}
)
}
it("adds a poller") {
var didComplete: Bool = false // Prevent multi-threading test bugs
openGroupManager
.add(
roomToken: "testRoom",
@ -485,8 +526,10 @@ class OpenGroupManagerSpec: QuickSpec {
using: testTransaction,
dependencies: dependencies
)
.map { _ -> Void in didComplete = true }
.retainUntilComplete()
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.toEventually(
call(matchingParameters: true) {
@ -502,6 +545,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("does not reset the sequence number or update the public key") {
var didComplete: Bool = false // Prevent multi-threading test bugs
openGroupManager
.add(
roomToken: "testRoom",
@ -511,15 +556,17 @@ class OpenGroupManagerSpec: QuickSpec {
using: testTransaction,
dependencies: dependencies
)
.map { _ -> Void in didComplete = true }
.retainUntilComplete()
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.toEventuallyNot(
call {
$0.removeOpenGroupSequenceNumber(
for: "testRoom",
on: "testServer",
using: testTransaction as Any
using: testTransaction! as Any
)
},
timeout: .milliseconds(100)
@ -530,7 +577,7 @@ class OpenGroupManagerSpec: QuickSpec {
$0.setOpenGroupPublicKey(
for: "testRoom",
to: "testKey",
using: testTransaction as Any
using: testTransaction! as Any
)
},
timeout: .milliseconds(100)
@ -574,8 +621,12 @@ class OpenGroupManagerSpec: QuickSpec {
}
}
context("when removing") {
// MARK: - --delete
context("when deleting") {
beforeEach {
testGroupThread.mockData[.interactions] = [testInteraction]
mockStorage
.when { $0.updateMessageIDCollectionByPruningMessagesWithIDs(anySet(), using: anyAny()) }
.thenReturn(())
@ -838,6 +889,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("attempts to retrieve the existing thread") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -845,13 +898,15 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
// The 'testGroupThread' should be the one retrieved
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(testGroupThread.numSaveCalls).to(equal(1))
}
it("attempts to retrieve the existing open group") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -859,12 +914,15 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage).to(call { $0.getOpenGroup(for: any()) })
}
it("saves the thread") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -872,12 +930,15 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(testGroupThread.numSaveCalls).to(equal(1))
}
it("saves the open group") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -885,12 +946,15 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage).to(call { $0.setOpenGroup(any(), for: any(), using: anyAny()) })
}
it("saves the updated user count") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -898,8 +962,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setUserCount(to: 10, forOpenGroupWithID: "testServer.testRoom", using: testTransaction! as Any)
@ -929,6 +994,8 @@ class OpenGroupManagerSpec: QuickSpec {
context("and updating the moderator list") {
it("successfully updates") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.moderators }.thenReturn([:])
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
@ -954,8 +1021,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.toEventually(
call(matchingParameters: true) {
@ -966,6 +1034,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("defaults to an empty array if no moderators are provided") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.moderators }.thenReturn([:])
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
@ -991,8 +1061,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.toEventually(
call(matchingParameters: true) {
@ -1005,6 +1076,8 @@ class OpenGroupManagerSpec: QuickSpec {
context("and updating the admin list") {
it("successfully updates") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.admins }.thenReturn([:])
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
@ -1030,8 +1103,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.toEventually(
call(matchingParameters: true) {
@ -1042,6 +1116,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("defaults to an empty array if no moderators are provided") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.admins }.thenReturn([:])
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
@ -1067,8 +1143,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.toEventually(
call(matchingParameters: true) {
@ -1098,6 +1175,8 @@ class OpenGroupManagerSpec: QuickSpec {
context("when not given a public key") {
it("saves the open group with the existing public key") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: nil,
@ -1105,8 +1184,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setOpenGroup(
@ -1145,6 +1225,8 @@ class OpenGroupManagerSpec: QuickSpec {
context("when storing the open group") {
it("defaults the infoUpdates to zero") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockStorage.when { $0.getOpenGroup(for: any()) }.thenReturn(nil)
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
@ -1170,8 +1252,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setOpenGroup(
@ -1193,6 +1276,8 @@ class OpenGroupManagerSpec: QuickSpec {
context("when checking to start polling") {
it("starts a new poller when not already polling") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.pollers }.thenReturn([:])
OpenGroupManager.handlePollInfo(
@ -1202,8 +1287,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache)
.to(call(matchingParameters: true) {
$0.pollers = ["testServer": OpenGroupAPI.Poller(for: "testServer")]
@ -1211,6 +1297,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("does not start a new poller when already polling") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.pollers }.thenReturn(["testServer": OpenGroupAPI.Poller(for: "testServer")])
OpenGroupManager.handlePollInfo(
@ -1220,8 +1308,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockOGMCache).to(call(.exactly(times: 1)) { $0.pollers })
}
}
@ -1237,6 +1326,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("uses the provided room image id if available") {
var didComplete: Bool = false // Prevent multi-threading test bugs
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
activeUsers: 10,
@ -1287,8 +1378,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setOpenGroup(
@ -1318,6 +1410,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("uses the existing room image id if none is provided") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockStorage
.when { $0.getOpenGroup(for: any()) }
.thenReturn(
@ -1355,8 +1449,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setOpenGroup(
@ -1386,6 +1481,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("uses the new room image id if there is an existing one") {
var didComplete: Bool = false // Prevent multi-threading test bugs
testGroupThread.mockData[.groupModel] = TSGroupModel(
title: "TestTitle",
memberIds: [],
@ -1458,8 +1555,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(mockStorage)
.to(call(matchingParameters: true) {
$0.setOpenGroup(
@ -1489,6 +1587,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("does nothing if there is no room image") {
var didComplete: Bool = false // Prevent multi-threading test bugs
OpenGroupManager.handlePollInfo(
testPollInfo,
publicKey: TestConstants.publicKey,
@ -1496,8 +1596,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(testGroupThread.groupModel.groupImage)
.toEventually(
beNil(),
@ -1511,6 +1612,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("does nothing if it fails to retrieve the room image") {
var didComplete: Bool = false // Prevent multi-threading test bugs
mockOGMCache.when { $0.groupImagePromises }
.thenReturn(["testServer.testRoom": Promise(error: HTTP.Error.generic)])
@ -1564,8 +1667,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(testGroupThread.groupModel.groupImage)
.toEventually(
beNil(),
@ -1579,6 +1683,8 @@ class OpenGroupManagerSpec: QuickSpec {
}
it("saves the retrieved room image") {
var didComplete: Bool = false // Prevent multi-threading test bugs
testPollInfo = OpenGroupAPI.RoomPollInfo(
token: "testRoom",
activeUsers: 10,
@ -1629,8 +1735,9 @@ class OpenGroupManagerSpec: QuickSpec {
on: "testServer",
using: testTransaction,
dependencies: dependencies
)
) { didComplete = true }
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(100))
expect(testGroupThread.groupModel.groupImage)
.toEventuallyNot(
beNil(),
@ -1649,6 +1756,11 @@ class OpenGroupManagerSpec: QuickSpec {
context("when handling messages") {
beforeEach {
testTransaction.mockData[.objectForKey] = [
"TestGroupId": testGroupThread,
"TestMessageId": testIncomingMessage
]
mockStorage
.when {
$0.setOpenGroupSequenceNumber(
@ -1659,6 +1771,33 @@ class OpenGroupManagerSpec: QuickSpec {
)
}
.thenReturn(())
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(())
mockStorage.when { $0.persist(anyArray(), using: testTransaction as Any) }.thenReturn([])
mockStorage
.when {
$0.getOrCreateThread(
for: any(),
groupPublicKey: any(),
openGroupID: any(),
using: testTransaction as Any
)
}
.thenReturn("TestGroupId")
mockStorage
.when {
$0.persist(
any(),
quotedMessage: nil,
linkPreview: nil,
groupPublicKey: any(),
openGroupID: any(),
using: testTransaction as Any
)
}
.thenReturn("TestMessageId")
mockStorage.when { $0.getContact(with: any()) }.thenReturn(nil)
}
it("updates the sequence number when there are messages") {
@ -1690,7 +1829,7 @@ class OpenGroupManagerSpec: QuickSpec {
for: "testRoom",
on: "testServer",
to: 124,
using: testTransaction as Any
using: testTransaction! as Any
)
})
}
@ -1710,6 +1849,208 @@ class OpenGroupManagerSpec: QuickSpec {
$0.setOpenGroupSequenceNumber(for: any(), on: any(), to: any(), using: testTransaction as Any)
})
}
it("ignores a message with no sender") {
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
id: 1,
sender: nil,
posted: 123,
edited: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
whisperTo: nil,
base64EncodedData: Data([1, 2, 3]).base64EncodedString(),
base64EncodedSignature: nil
)
],
for: "testRoom",
on: "testServer",
isBackgroundPoll: false,
using: testTransaction,
dependencies: dependencies
)
expect(testIncomingMessage.didCallSave).toEventuallyNot(beTrue(), timeout: .milliseconds(50))
expect(testIncomingMessage.didCallRemove).toEventuallyNot(beTrue(), timeout: .milliseconds(50))
}
it("ignores a message with invalid data") {
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
id: 1,
sender: "05\(TestConstants.publicKey)",
posted: 123,
edited: nil,
seqNo: 124,
whisper: false,
whisperMods: false,
whisperTo: nil,
base64EncodedData: Data([1, 2, 3]).base64EncodedString(),
base64EncodedSignature: nil
)
],
for: "testRoom",
on: "testServer",
isBackgroundPoll: false,
using: testTransaction,
dependencies: dependencies
)
expect(testIncomingMessage.didCallSave).toEventuallyNot(beTrue(), timeout: .milliseconds(50))
expect(testIncomingMessage.didCallRemove).toEventuallyNot(beTrue(), timeout: .milliseconds(50))
}
it("processes a message with valid data") {
OpenGroupManager.handleMessages(
[testMessage],
for: "testRoom",
on: "testServer",
isBackgroundPoll: false,
using: testTransaction,
dependencies: dependencies
)
expect(testIncomingMessage.didCallSave)
.toEventually(
beTrue(),
timeout: .milliseconds(100)
)
}
it("processes valid messages when combined with invalid ones") {
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
id: 2,
sender: "05\(TestConstants.publicKey)",
posted: 122,
edited: nil,
seqNo: 123,
whisper: false,
whisperMods: false,
whisperTo: nil,
base64EncodedData: Data([1, 2, 3]).base64EncodedString(),
base64EncodedSignature: nil
),
testMessage,
],
for: "testRoom",
on: "testServer",
isBackgroundPoll: false,
using: testTransaction,
dependencies: dependencies
)
expect(testIncomingMessage.didCallSave)
.toEventually(
beTrue(),
timeout: .milliseconds(100)
)
}
context("with no data") {
it("deletes the message if we have the message") {
testTransaction.mockData[.objectForKey] = testGroupThread
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
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(testIncomingMessage.didCallRemove)
.toEventually(
beTrue(),
timeout: .milliseconds(100)
)
}
it("does nothing if we do not have the thread") {
testTransaction.mockData[.objectForKey] = nil
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
id: 1,
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(testIncomingMessage.didCallRemove)
.toEventuallyNot(
beTrue(),
timeout: .milliseconds(100)
)
}
it("does nothing if we do not have the message") {
testGroupThread.mockData[.interactions] = [testInteraction]
testTransaction.mockData[.objectForKey] = testGroupThread
OpenGroupManager.handleMessages(
[
OpenGroupAPI.Message(
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(testIncomingMessage.didCallRemove)
.toEventuallyNot(
beTrue(),
timeout: .milliseconds(100)
)
}
}
}
}
}
}

View File

@ -21,3 +21,7 @@ extension OpenGroupAPI.Server: Mocked {
capabilities: OpenGroupAPI.Capabilities(capabilities: anyArray(), missing: anyArray())
)
}
extension VisibleMessage: Mocked {
static var mockValue: VisibleMessage = VisibleMessage()
}

View File

@ -0,0 +1,23 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import SessionMessagingKit
// FIXME: Turn this into a protocol to make mocking possible
class TestIncomingMessage: TSIncomingMessage, Mockable {
// MARK: - Mockable
enum DataKey: Hashable {
}
typealias Key = DataKey
var mockData: [DataKey: Any] = [:]
var didCallSave: Bool = false
var didCallRemove: Bool = false
// MARK: - TSInteraction
override func save(with transaction: YapDatabaseReadWriteTransaction) { didCallSave = true }
override func remove(with transaction: YapDatabaseReadWriteTransaction) { didCallRemove = true }
}

View File

@ -15,7 +15,7 @@ class TestInteraction: TSInteraction, Mockable {
typealias Key = DataKey
var mockData: [DataKey: Any] = [:]
var didCallSave: Bool = true
var didCallSave: Bool = false
// MARK: - TSInteraction

View File

@ -18,6 +18,10 @@ final class TestTransaction: YapDatabaseReadWriteTransaction, Mockable {
// MARK: - YapDatabaseReadWriteTransaction
override func object(forKey key: String, inCollection collection: String?) -> Any? {
if let dictionary: [String: Any] = mockData[.objectForKey] as? [String: Any] {
return dictionary[key]
}
return mockData[.objectForKey]
}