mirror of https://github.com/oxen-io/session-ios
Finalised the OpenGroupAPI and more tests
Fixed an issue where messages where signed incorrectly when blinding wasn't enabled on a SOGS Fixed an issue where a single invalid message would result in all messages in that request being dropped Updated the final legacy endpoint (ban and delete all messages) Moved the OpenGroupManager poller values into the 'Cache' (so they are thread safe) Started adding unit tests for the OpenGroupManager Removed some redundant parameters from the 'Request' typepull/592/head
parent
c415fc9e06
commit
f9c2655df4
@ -0,0 +1,24 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Failable<T: Codable>: Codable {
|
||||
let value: T?
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
guard let container = try? decoder.singleValueContainer() else {
|
||||
self.value = nil
|
||||
return
|
||||
}
|
||||
|
||||
self.value = try? container.decode(T.self)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
guard let value: T = value else { return }
|
||||
|
||||
var container: SingleValueEncodingContainer = encoder.singleValueContainer()
|
||||
|
||||
try container.encode(value)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,559 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
import SessionSnodeKit
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class OpenGroupManagerSpec: QuickSpec {
|
||||
class TestCapabilitiesAndRoomApi: TestOnionRequestAPI {
|
||||
static let capabilitiesData: OpenGroupAPI.Capabilities = OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: nil)
|
||||
static let roomData: OpenGroupAPI.Room = OpenGroupAPI.Room(
|
||||
token: "test",
|
||||
name: "test",
|
||||
description: nil,
|
||||
infoUpdates: 0,
|
||||
messageSequence: 0,
|
||||
created: 0,
|
||||
activeUsers: 0,
|
||||
activeUsersCutoff: 0,
|
||||
imageId: nil,
|
||||
pinnedMessages: nil,
|
||||
admin: false,
|
||||
globalAdmin: false,
|
||||
admins: [],
|
||||
hiddenAdmins: nil,
|
||||
moderator: false,
|
||||
globalModerator: false,
|
||||
moderators: [],
|
||||
hiddenModerators: nil,
|
||||
read: false,
|
||||
defaultRead: nil,
|
||||
defaultAccessible: nil,
|
||||
write: false,
|
||||
defaultWrite: nil,
|
||||
upload: false,
|
||||
defaultUpload: nil
|
||||
)
|
||||
|
||||
override class var mockResponse: Data? {
|
||||
let responses: [Data] = [
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: capabilitiesData,
|
||||
failedToParseBody: false
|
||||
)
|
||||
),
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: roomData,
|
||||
failedToParseBody: false
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
return "[\(responses.map { String(data: $0, encoding: .utf8)! }.joined(separator: ","))]".data(using: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
var mockStorage: MockStorage!
|
||||
var mockSodium: MockSodium!
|
||||
var mockAeadXChaCha20Poly1305Ietf: MockAeadXChaCha20Poly1305Ietf!
|
||||
var mockGenericHash: MockGenericHash!
|
||||
var mockSign: MockSign!
|
||||
var mockUserDefaults: MockUserDefaults!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
var testInteraction: TestInteraction!
|
||||
var testGroupThread: TestGroupThread!
|
||||
var testTransaction: TestTransaction!
|
||||
|
||||
describe("an OpenGroupAPI") {
|
||||
// MARK: - Configuration
|
||||
|
||||
beforeEach {
|
||||
mockStorage = MockStorage()
|
||||
mockSodium = MockSodium()
|
||||
mockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
|
||||
mockGenericHash = MockGenericHash()
|
||||
mockSign = MockSign()
|
||||
mockUserDefaults = MockUserDefaults()
|
||||
dependencies = Dependencies(
|
||||
onionApi: TestCapabilitiesAndRoomApi.self,
|
||||
storage: mockStorage,
|
||||
sodium: mockSodium,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
sign: mockSign,
|
||||
genericHash: mockGenericHash,
|
||||
ed25519: MockEd25519(),
|
||||
nonceGenerator16: OpenGroupAPISpec.TestNonce16Generator(),
|
||||
nonceGenerator24: OpenGroupAPISpec.TestNonce24Generator(),
|
||||
standardUserDefaults: mockUserDefaults,
|
||||
date: Date(timeIntervalSince1970: 1234567890)
|
||||
)
|
||||
testInteraction = TestInteraction()
|
||||
testInteraction.mockData[.uniqueId] = "TestInteractionId"
|
||||
testInteraction.mockData[.timestamp] = UInt64(123)
|
||||
|
||||
testGroupThread = TestGroupThread()
|
||||
testGroupThread.mockData[.groupModel] = TSGroupModel(
|
||||
title: "TestTitle",
|
||||
memberIds: [],
|
||||
image: nil,
|
||||
groupId: LKGroupUtilities.getEncodedOpenGroupIDAsData("testServer.testRoom"),
|
||||
groupType: .openGroup,
|
||||
adminIds: [],
|
||||
moderatorIds: []
|
||||
)
|
||||
testGroupThread.mockData[.interactions] = [testInteraction]
|
||||
|
||||
testTransaction = TestTransaction()
|
||||
testTransaction.mockData[.objectForKey] = testGroupThread
|
||||
|
||||
mockStorage
|
||||
.when { $0.write(with: { _ in }) }
|
||||
.then { args in (args.first as? ((Any) -> Void))?(testTransaction as Any) }
|
||||
.thenReturn(Promise.value(()))
|
||||
mockStorage
|
||||
.when { $0.write(with: { _ in }, completion: { }) }
|
||||
.then { args in
|
||||
(args.first as? ((Any) -> Void))?(testTransaction as Any)
|
||||
(args.last as? (() -> Void))?()
|
||||
}
|
||||
.thenReturn(Promise.value(()))
|
||||
mockStorage
|
||||
.when { $0.getUserKeyPair() }
|
||||
.thenReturn(
|
||||
try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
)
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getUserED25519KeyPair() }
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
)
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getAllOpenGroups() }
|
||||
.thenReturn([
|
||||
"0": OpenGroup(
|
||||
server: "testServer",
|
||||
room: "testRoom",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
)
|
||||
])
|
||||
mockStorage
|
||||
.when { $0.getOpenGroup(for: any()) }
|
||||
.thenReturn(
|
||||
OpenGroup(
|
||||
server: "testServer",
|
||||
room: "testRoom",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
)
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupPublicKey(for: any()) }
|
||||
.thenReturn(TestConstants.publicKey)
|
||||
|
||||
mockGenericHash.when { $0.hash(message: any(), outputLength: any()) }.thenReturn([])
|
||||
mockSodium
|
||||
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
)
|
||||
)
|
||||
mockSodium
|
||||
.when {
|
||||
$0.sogsSignature(
|
||||
message: any(),
|
||||
secretKey: any(),
|
||||
blindedSecretKey: any(),
|
||||
blindedPublicKey: any()
|
||||
)
|
||||
}
|
||||
.thenReturn("TestSogsSignature".bytes)
|
||||
mockSign.when { $0.signature(message: any(), secretKey: any()) }.thenReturn("TestSignature".bytes)
|
||||
}
|
||||
|
||||
afterEach {
|
||||
OpenGroupManager.shared.stopPolling() // Need to stop any pollers which get created during tests
|
||||
|
||||
mockStorage = nil
|
||||
mockSodium = nil
|
||||
mockAeadXChaCha20Poly1305Ietf = nil
|
||||
mockGenericHash = nil
|
||||
mockSign = nil
|
||||
mockUserDefaults = nil
|
||||
dependencies = nil
|
||||
|
||||
testInteraction = nil
|
||||
testGroupThread = nil
|
||||
testTransaction = nil
|
||||
}
|
||||
|
||||
// MARK: - Polling
|
||||
|
||||
context("when starting polling") {
|
||||
beforeEach {
|
||||
mockStorage
|
||||
.when { $0.getAllOpenGroups() }
|
||||
.thenReturn([
|
||||
"0": OpenGroup(
|
||||
server: "testServer",
|
||||
room: "testRoom",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
),
|
||||
"1": OpenGroup(
|
||||
server: "testServer1",
|
||||
room: "testRoom1",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test1",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
)
|
||||
])
|
||||
mockStorage.when { $0.removeOpenGroupSequenceNumber(for: any(), on: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupPublicKey(for: any(), to: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupServer(any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroup(any(), for: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setUserCount(to: any(), forOpenGroupWithID: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(nil)
|
||||
|
||||
mockUserDefaults
|
||||
.when { $0.object(forKey: SNUserDefaults.Date.lastOpen.rawValue) }
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
||||
it("creates pollers for all of the open groups") {
|
||||
OpenGroupManager.shared.startPolling(using: dependencies)
|
||||
|
||||
expect(OpenGroupManager.shared.cache.pollers.keys.map { String($0) }.sorted)
|
||||
.to(equal(["testserver", "testserver1"]))
|
||||
}
|
||||
|
||||
it("updates the isPolling flag") {
|
||||
OpenGroupManager.shared.startPolling(using: dependencies)
|
||||
|
||||
expect(OpenGroupManager.shared.cache.isPolling).to(beTrue())
|
||||
}
|
||||
|
||||
it("does nothing if already polling") {
|
||||
OpenGroupManager.shared.mutableCache.mutate { $0.isPolling = true }
|
||||
OpenGroupManager.shared.startPolling(using: dependencies)
|
||||
|
||||
expect(OpenGroupManager.shared.cache.pollers.keys.map { String($0) })
|
||||
.to(equal([]))
|
||||
}
|
||||
}
|
||||
|
||||
context("when stopping polling") {
|
||||
beforeEach {
|
||||
mockStorage
|
||||
.when { $0.getAllOpenGroups() }
|
||||
.thenReturn([
|
||||
"0": OpenGroup(
|
||||
server: "testServer",
|
||||
room: "testRoom",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
),
|
||||
"1": OpenGroup(
|
||||
server: "testServer1",
|
||||
room: "testRoom1",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test1",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
)
|
||||
])
|
||||
mockStorage.when { $0.removeOpenGroupSequenceNumber(for: any(), on: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupPublicKey(for: any(), to: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupServer(any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroup(any(), for: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setUserCount(to: any(), forOpenGroupWithID: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(nil)
|
||||
|
||||
mockUserDefaults
|
||||
.when { $0.object(forKey: SNUserDefaults.Date.lastOpen.rawValue) }
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
|
||||
OpenGroupManager.shared.startPolling(using: dependencies)
|
||||
}
|
||||
|
||||
it("removes all pollers") {
|
||||
OpenGroupManager.shared.stopPolling()
|
||||
|
||||
expect(OpenGroupManager.shared.cache.pollers.keys.map { String($0) })
|
||||
.to(equal([]))
|
||||
}
|
||||
|
||||
it("updates the isPolling flag") {
|
||||
OpenGroupManager.shared.stopPolling()
|
||||
|
||||
expect(OpenGroupManager.shared.cache.isPolling).to(beFalse())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Adding & Removing
|
||||
|
||||
context("when adding") {
|
||||
beforeEach {
|
||||
mockStorage.when { $0.removeOpenGroupSequenceNumber(for: any(), on: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupPublicKey(for: any(), to: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroupServer(any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setOpenGroup(any(), for: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.setUserCount(to: any(), forOpenGroupWithID: any(), using: any()) }.thenReturn(())
|
||||
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(nil)
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(nil)
|
||||
|
||||
mockUserDefaults
|
||||
.when { $0.object(forKey: SNUserDefaults.Date.lastOpen.rawValue) }
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
||||
it("resets the sequence number of the open group") {
|
||||
OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(mockStorage)
|
||||
.to(
|
||||
call(.exactly(times: 1)) {
|
||||
$0.removeOpenGroupSequenceNumber(
|
||||
for: "testRoom",
|
||||
on: "testServer",
|
||||
using: testTransaction as Any
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
it("sets the public key of the open group server") {
|
||||
OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(mockStorage)
|
||||
.to(
|
||||
call(.exactly(times: 1)) {
|
||||
$0.setOpenGroupPublicKey(
|
||||
for: "testRoom",
|
||||
to: "testKey",
|
||||
using: testTransaction as Any
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
it("adds a poller") {
|
||||
OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(OpenGroupManager.shared.cache.pollers["testServer"])
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
}
|
||||
|
||||
context("an existing room") {
|
||||
beforeEach {
|
||||
OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
.retainUntilComplete()
|
||||
|
||||
// There is a bunch of async code in this function so we need to wait until if finishes
|
||||
// processing before doing the actual tests
|
||||
expect(mockStorage)
|
||||
.toEventually(
|
||||
call { $0.setOpenGroup(any(), for: "testServer", using: testTransaction as Any) },
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
|
||||
mockStorage.resetCallCounts()
|
||||
}
|
||||
|
||||
it("does not reset the sequence number or update the public key") {
|
||||
OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(mockStorage)
|
||||
.toEventuallyNot(
|
||||
call {
|
||||
$0.removeOpenGroupSequenceNumber(
|
||||
for: "testRoom",
|
||||
on: "testServer",
|
||||
using: testTransaction as Any
|
||||
)
|
||||
},
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(mockStorage)
|
||||
.toEventuallyNot(
|
||||
call {
|
||||
$0.setOpenGroupPublicKey(
|
||||
for: "testRoom",
|
||||
to: "testKey",
|
||||
using: testTransaction as Any
|
||||
)
|
||||
},
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context("with an invalid response") {
|
||||
beforeEach {
|
||||
class TestApi: TestOnionRequestAPI {
|
||||
override class var mockResponse: Data? { return Data() }
|
||||
}
|
||||
dependencies = dependencies.with(onionApi: TestApi.self)
|
||||
|
||||
mockUserDefaults
|
||||
.when { $0.object(forKey: SNUserDefaults.Date.lastOpen.rawValue) }
|
||||
.thenReturn(Date(timeIntervalSince1970: 1234567890))
|
||||
}
|
||||
|
||||
it("fails with the error") {
|
||||
var error: Error?
|
||||
|
||||
let promise = OpenGroupManager.shared
|
||||
.add(
|
||||
roomToken: "testRoom",
|
||||
server: "testServer",
|
||||
publicKey: "testKey",
|
||||
isConfigMessage: false,
|
||||
using: testTransaction,
|
||||
dependencies: dependencies
|
||||
)
|
||||
promise.catch { error = $0 }
|
||||
promise.retainUntilComplete()
|
||||
|
||||
expect(error?.localizedDescription)
|
||||
.toEventually(
|
||||
equal(HTTP.Error.parsingFailed.localizedDescription),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("when removing") {
|
||||
|
||||
// public func delete(_ openGroup: OpenGroup, associatedWith thread: TSThread, using transaction: YapDatabaseReadWriteTransaction, dependencies: Dependencies = Dependencies()) {
|
||||
// // Stop the poller if needed
|
||||
// let openGroups = dependencies.storage.getAllOpenGroups().values.filter { $0.server == openGroup.server }
|
||||
// if openGroups.count == 1 && openGroups.last == openGroup {
|
||||
// let poller = pollers[openGroup.server]
|
||||
// poller?.stop()
|
||||
// pollers[openGroup.server] = nil
|
||||
// }
|
||||
//
|
||||
// // Remove all data
|
||||
// var messageIDs: Set<String> = []
|
||||
// var messageTimestamps: Set<UInt64> = []
|
||||
// thread.enumerateInteractions(with: transaction) { interaction, _ in
|
||||
// messageIDs.insert(interaction.uniqueId!)
|
||||
// messageTimestamps.insert(interaction.timestamp)
|
||||
// }
|
||||
// dependencies.storage.updateMessageIDCollectionByPruningMessagesWithIDs(messageIDs, using: transaction)
|
||||
// dependencies.storage.removeReceivedMessageTimestamps(messageTimestamps, using: transaction)
|
||||
// dependencies.storage.removeOpenGroupSequenceNumber(for: openGroup.room, on: openGroup.server, using: transaction)
|
||||
//
|
||||
// thread.removeAllThreadInteractions(with: transaction)
|
||||
// thread.remove(with: transaction)
|
||||
// dependencies.storage.removeOpenGroup(for: thread.uniqueId!, using: transaction)
|
||||
//
|
||||
// // Only remove the open group public key and server info if the user isn't in any other rooms
|
||||
// if openGroups.count <= 1 {
|
||||
// dependencies.storage.removeOpenGroupServer(name: openGroup.server, using: transaction)
|
||||
// dependencies.storage.removeOpenGroupPublicKey(for: openGroup.server, using: transaction)
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class MockUserDefaults: Mock<UserDefaultsType>, UserDefaultsType {
|
||||
var storage: [String: Any] = [:]
|
||||
|
||||
func object(forKey defaultName: String) -> Any? { return accept(args: [defaultName]) }
|
||||
func string(forKey defaultName: String) -> String? { return accept(args: [defaultName]) as? String }
|
||||
func array(forKey defaultName: String) -> [Any]? { return accept(args: [defaultName]) as? [Any] }
|
||||
func dictionary(forKey defaultName: String) -> [String: Any]? { return accept(args: [defaultName]) as? [String: Any] }
|
||||
func data(forKey defaultName: String) -> Data? { return accept(args: [defaultName]) as? Data }
|
||||
func stringArray(forKey defaultName: String) -> [String]? { return accept(args: [defaultName]) as? [String] }
|
||||
func integer(forKey defaultName: String) -> Int { return ((accept(args: [defaultName]) as? Int) ?? 0) }
|
||||
func float(forKey defaultName: String) -> Float { return ((accept(args: [defaultName]) as? Float) ?? 0) }
|
||||
func double(forKey defaultName: String) -> Double { return ((accept(args: [defaultName]) as? Double) ?? 0) }
|
||||
func bool(forKey defaultName: String) -> Bool { return ((accept(args: [defaultName]) as? Bool) ?? false) }
|
||||
func url(forKey defaultName: String) -> URL? { return accept(args: [defaultName]) as? URL }
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String) { accept(args: [value, defaultName]) }
|
||||
func set(_ value: Int, forKey defaultName: String) { accept(args: [value, defaultName]) }
|
||||
func set(_ value: Float, forKey defaultName: String) { accept(args: [value, defaultName]) }
|
||||
func set(_ value: Double, forKey defaultName: String) { accept(args: [value, defaultName]) }
|
||||
func set(_ value: Bool, forKey defaultName: String) { accept(args: [value, defaultName]) }
|
||||
func set(_ url: URL?, forKey defaultName: String) { accept(args: [url, defaultName]) }
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
|
||||
extension OpenGroup: Mocked {
|
||||
static var mockValue: OpenGroup = OpenGroup(
|
||||
server: any(),
|
||||
room: any(),
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: any(),
|
||||
groupDescription: any(),
|
||||
imageID: any(),
|
||||
infoUpdates: any()
|
||||
)
|
||||
}
|
||||
|
||||
extension OpenGroupAPI.Server: Mocked {
|
||||
static var mockValue: OpenGroupAPI.Server = OpenGroupAPI.Server(
|
||||
name: any(),
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: any(), missing: any())
|
||||
)
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
|
||||
// FIXME: Turn this into a protocol to make mocking possible
|
||||
class TestGroupThread: TSGroupThread, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case groupModel
|
||||
case interactions
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
var didCallSave: Bool = false
|
||||
|
||||
// MARK: - TSGroupThread
|
||||
|
||||
override var groupModel: TSGroupModel {
|
||||
get { (mockData[.groupModel] as! TSGroupModel) }
|
||||
set {}
|
||||
}
|
||||
|
||||
override func enumerateInteractions(_ block: @escaping (TSInteraction) -> Void) {
|
||||
((mockData[.interactions] as? [TSInteraction]) ?? []).forEach(block)
|
||||
}
|
||||
|
||||
override func save(with transaction: YapDatabaseReadWriteTransaction) { didCallSave = true }
|
||||
}
|
@ -1,3 +1,32 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
|
||||
// FIXME: Turn this into a protocol to make mocking possible
|
||||
class TestInteraction: TSInteraction, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case uniqueId
|
||||
case timestamp
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
var didCallSave: Bool = true
|
||||
|
||||
// MARK: - TSInteraction
|
||||
|
||||
override var uniqueId: String? {
|
||||
get { (mockData[.uniqueId] as? String) }
|
||||
set { mockData[.uniqueId] = newValue }
|
||||
}
|
||||
|
||||
override var timestamp: UInt64 {
|
||||
(mockData[.timestamp] as! UInt64)
|
||||
}
|
||||
|
||||
override func save(with transaction: YapDatabaseReadWriteTransaction) { didCallSave = true }
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import SessionSnodeKit
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
// FIXME: Change 'OnionRequestAPIType' to have instance methods instead of static methods once everything is updated to use 'Dependencies'
|
||||
class TestOnionRequestAPI: OnionRequestAPIType {
|
||||
struct RequestData: Codable {
|
||||
let urlString: String?
|
||||
let httpMethod: String
|
||||
let headers: [String: String]
|
||||
let snodeMethod: String?
|
||||
let body: Data?
|
||||
|
||||
let server: String
|
||||
let version: OnionRequestAPI.Version
|
||||
let publicKey: String?
|
||||
}
|
||||
class ResponseInfo: OnionRequestResponseInfoType {
|
||||
let requestData: RequestData
|
||||
let code: Int
|
||||
let headers: [String: String]
|
||||
|
||||
init(requestData: RequestData, code: Int, headers: [String: String]) {
|
||||
self.requestData = requestData
|
||||
self.code = code
|
||||
self.headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
class var mockResponse: Data? { return nil }
|
||||
|
||||
static func sendOnionRequest(_ request: URLRequest, to server: String, using version: OnionRequestAPI.Version, with x25519PublicKey: String) -> Promise<(OnionRequestResponseInfoType, Data?)> {
|
||||
let responseInfo: ResponseInfo = ResponseInfo(
|
||||
requestData: RequestData(
|
||||
urlString: request.url?.absoluteString,
|
||||
httpMethod: (request.httpMethod ?? "GET"),
|
||||
headers: (request.allHTTPHeaderFields ?? [:]),
|
||||
snodeMethod: nil,
|
||||
body: request.httpBody,
|
||||
|
||||
server: server,
|
||||
version: version,
|
||||
publicKey: x25519PublicKey
|
||||
),
|
||||
code: 200,
|
||||
headers: [:]
|
||||
)
|
||||
|
||||
return Promise.value((responseInfo, mockResponse))
|
||||
}
|
||||
|
||||
static func sendOnionRequest(to snode: Snode, invoking method: Snode.Method, with parameters: JSON, using version: OnionRequestAPI.Version, associatedWith publicKey: String?) -> Promise<Data> {
|
||||
// TODO: Test the 'responseInfo' somehow?
|
||||
return Promise.value(mockResponse!)
|
||||
}
|
||||
}
|
@ -1,3 +1,26 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
|
||||
// FIXME: Turn this into a protocol to make mocking possible
|
||||
class TestThread: TSThread, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case interactions
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
var didCallSave: Bool = false
|
||||
|
||||
// MARK: - TSThread
|
||||
|
||||
override func enumerateInteractions(_ block: @escaping (TSInteraction) -> Void) {
|
||||
((mockData[.interactions] as? [TSInteraction]) ?? []).forEach(block)
|
||||
}
|
||||
|
||||
override func save(with transaction: YapDatabaseReadWriteTransaction) { didCallSave = true }
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import YapDatabase
|
||||
|
||||
// FIXME: Turn this into a protocol to make mocking possible
|
||||
final class TestTransaction: YapDatabaseReadWriteTransaction, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case objectForKey
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - YapDatabaseReadWriteTransaction
|
||||
|
||||
override func object(forKey key: String, inCollection collection: String?) -> Any? {
|
||||
return mockData[.objectForKey]
|
||||
}
|
||||
|
||||
override func addCompletionQueue(_ completionQueue: DispatchQueue?, completionBlock: @escaping () -> Void) {
|
||||
completionBlock()
|
||||
}
|
||||
}
|
||||
|
||||
extension TestTransaction: Mocked {
|
||||
static var mockValue: TestTransaction = TestTransaction()
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class TestUserDefaults: UserDefaultsType {
|
||||
var storage: [String: Any] = [:]
|
||||
|
||||
func object(forKey defaultName: String) -> Any? { return storage[defaultName] }
|
||||
func string(forKey defaultName: String) -> String? { return storage[defaultName] as? String }
|
||||
func array(forKey defaultName: String) -> [Any]? { return storage[defaultName] as? [Any] }
|
||||
func dictionary(forKey defaultName: String) -> [String: Any]? { return storage[defaultName] as? [String: Any] }
|
||||
func data(forKey defaultName: String) -> Data? { return storage[defaultName] as? Data }
|
||||
func stringArray(forKey defaultName: String) -> [String]? { return storage[defaultName] as? [String] }
|
||||
func integer(forKey defaultName: String) -> Int { return ((storage[defaultName] as? Int) ?? 0) }
|
||||
func float(forKey defaultName: String) -> Float { return ((storage[defaultName] as? Float) ?? 0) }
|
||||
func double(forKey defaultName: String) -> Double { return ((storage[defaultName] as? Double) ?? 0) }
|
||||
func bool(forKey defaultName: String) -> Bool { return ((storage[defaultName] as? Bool) ?? false) }
|
||||
func url(forKey defaultName: String) -> URL? { return storage[defaultName] as? URL }
|
||||
|
||||
func set(_ value: Any?, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Int, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Float, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Double, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ value: Bool, forKey defaultName: String) { storage[defaultName] = value }
|
||||
func set(_ url: URL?, forKey defaultName: String) { storage[defaultName] = url }
|
||||
}
|
Loading…
Reference in New Issue