Started working on fixing the broken unit tests

Updated the GRDB storage to support custom writer injection
pull/612/head
Morgan Pretty 3 years ago
parent ff08579088
commit 428cc95ec2

@ -560,7 +560,6 @@
FCB11D8C1A129A76002F93FB /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB11D8B1A129A76002F93FB /* CoreMedia.framework */; };
FD078E4827E02561000769AF /* CommonMockedExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4727E02561000769AF /* CommonMockedExtensions.swift */; };
FD078E4927E02576000769AF /* CommonMockedExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4727E02561000769AF /* CommonMockedExtensions.swift */; };
FD078E4B27E02C5D000769AF /* Failable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4A27E02C5D000769AF /* Failable.swift */; };
FD078E4D27E17156000769AF /* MockOGMCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4C27E17156000769AF /* MockOGMCache.swift */; };
FD078E4F27E175F1000769AF /* DependencyExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E4E27E175F1000769AF /* DependencyExtensions.swift */; };
FD078E5227E1760A000769AF /* OGMDependencyExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5127E1760A000769AF /* OGMDependencyExtensions.swift */; };
@ -568,7 +567,6 @@
FD078E5827E1B831000769AF /* TestIncomingMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5727E1B831000769AF /* TestIncomingMessage.swift */; };
FD078E5A27E29F09000769AF /* MockNonce16Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5927E29F09000769AF /* MockNonce16Generator.swift */; };
FD078E5C27E29F78000769AF /* MockNonce24Generator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5B27E29F78000769AF /* MockNonce24Generator.swift */; };
FD078E6027E2BB36000769AF /* MockIdentityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */; };
FD09796927F6BEA700936362 /* SwarmSnode.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09796827F6BEA700936362 /* SwarmSnode.swift */; };
FD09796B27F6C67500936362 /* Failable.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09796A27F6C67500936362 /* Failable.swift */; };
FD09796E27FA6D0000936362 /* Contact.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD09796D27FA6D0000936362 /* Contact.swift */; };
@ -699,7 +697,6 @@
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BA27CF20AF005E1583 /* SessionIdSpec.swift */; };
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BD27CF2243005E1583 /* TestConstants.swift */; };
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9BD27CF2243005E1583 /* TestConstants.swift */; };
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C227CF33F7005E1583 /* ServerSpec.swift */; };
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */; };
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */; };
FD83B9C927D0487A005E1583 /* SendDirectMessageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */; };
@ -772,7 +769,6 @@
FDC4387827B5C35400C60D73 /* SendMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */; };
FDC4389227B9FFC700C60D73 /* SessionMessagingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A6F025539DE700C340D1 /* SessionMessagingKit.framework */; platformFilter = ios; };
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */; };
FDC4389D27BA01F000C60D73 /* MockStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389C27BA01F000C60D73 /* MockStorage.swift */; };
FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A327BB107F00C60D73 /* UserBanRequest.swift */; };
FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */; };
FDC438AA27BB12BB00C60D73 /* UserModeratorRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */; };
@ -1635,14 +1631,12 @@
FC3BD9871A30A790005B96BB /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
FCB11D8B1A129A76002F93FB /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
FD078E4727E02561000769AF /* CommonMockedExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonMockedExtensions.swift; sourceTree = "<group>"; };
FD078E4A27E02C5D000769AF /* Failable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Failable.swift; sourceTree = "<group>"; };
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>"; };
FD078E5927E29F09000769AF /* MockNonce16Generator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNonce16Generator.swift; sourceTree = "<group>"; };
FD078E5B27E29F78000769AF /* MockNonce24Generator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNonce24Generator.swift; sourceTree = "<group>"; };
FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIdentityManager.swift; sourceTree = "<group>"; };
FD09796827F6BEA700936362 /* SwarmSnode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwarmSnode.swift; sourceTree = "<group>"; };
FD09796A27F6C67500936362 /* Failable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Failable.swift; sourceTree = "<group>"; };
FD09796D27FA6D0000936362 /* Contact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contact.swift; sourceTree = "<group>"; };
@ -1746,7 +1740,6 @@
FD83B9AF27CF200A005E1583 /* SessionUtilitiesKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionUtilitiesKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
FD83B9BA27CF20AF005E1583 /* SessionIdSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionIdSpec.swift; sourceTree = "<group>"; };
FD83B9BD27CF2243005E1583 /* TestConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConstants.swift; sourceTree = "<group>"; };
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSpec.swift; sourceTree = "<group>"; };
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupSpec.swift; sourceTree = "<group>"; };
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapabilitiesSpec.swift; sourceTree = "<group>"; };
FD83B9C827D0487A005E1583 /* SendDirectMessageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendDirectMessageResponse.swift; sourceTree = "<group>"; };
@ -1817,7 +1810,6 @@
FDC4387727B5C35400C60D73 /* SendMessageRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageRequest.swift; sourceTree = "<group>"; };
FDC4388E27B9FFC700C60D73 /* SessionMessagingKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionMessagingKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupAPISpec.swift; sourceTree = "<group>"; };
FDC4389C27BA01F000C60D73 /* MockStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockStorage.swift; sourceTree = "<group>"; };
FDC438A327BB107F00C60D73 /* UserBanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserBanRequest.swift; sourceTree = "<group>"; };
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserUnbanRequest.swift; sourceTree = "<group>"; };
FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserModeratorRequest.swift; sourceTree = "<group>"; };
@ -3068,7 +3060,6 @@
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */,
FDC438C027BB4E6800C60D73 /* Dependencies.swift */,
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
FD078E4A27E02C5D000769AF /* Failable.swift */,
C3BBE0C62554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift */,
C3A71D0A2558989C0043A11F /* MessageWrapper.swift */,
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */,
@ -3728,7 +3719,6 @@
FD83B9C127CF33EE005E1583 /* Models */ = {
isa = PBXGroup;
children = (
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */,
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */,
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */,
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */,
@ -3867,9 +3857,7 @@
isa = PBXGroup;
children = (
FDC438BC27BB2AB400C60D73 /* Mockable.swift */,
FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */,
FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */,
FDC4389C27BA01F000C60D73 /* MockStorage.swift */,
FD859EF327C2F49200510D0C /* MockSodium.swift */,
FD3C906E27E43E8700CD579F /* MockBox.swift */,
FD859EF927C2F5C500510D0C /* MockGenericHash.swift */,
@ -5231,7 +5219,6 @@
FDC438CB27BB7DB100C60D73 /* UpdateMessageRequest.swift in Sources */,
C352A2FF25574B6300338F3E /* MessageSendJob.swift in Sources */,
FDC438C327BB512200C60D73 /* SodiumProtocols.swift in Sources */,
FD078E4B27E02C5D000769AF /* Failable.swift in Sources */,
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */,
FD716E722850647600C96BF4 /* Data+Utilities.swift in Sources */,
@ -5484,14 +5471,12 @@
FDC290B327DFF9F5005DAE71 /* TestOnionRequestAPI.swift in Sources */,
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */,
FD3C906A27E417CE00CD579F /* SodiumUtilitiesSpec.swift in Sources */,
FD078E6027E2BB36000769AF /* MockIdentityManager.swift in Sources */,
FD3C907127E445E500CD579F /* MessageReceiverDecryptionSpec.swift in Sources */,
FDC2909627D71252005DAE71 /* SOGSErrorSpec.swift in Sources */,
FDC2908727D7047F005DAE71 /* RoomSpec.swift in Sources */,
FD078E4F27E175F1000769AF /* DependencyExtensions.swift in Sources */,
FDC2909C27D713D2005DAE71 /* SodiumProtocolsSpec.swift in Sources */,
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */,
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */,
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */,
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */,
FD3C906427E4122F00CD579F /* RequestSpec.swift in Sources */,
@ -5522,7 +5507,6 @@
FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */,
FDC290A227D85890005DAE71 /* TestInteraction.swift in Sources */,
FD078E5427E197CA000769AF /* OpenGroupManagerSpec.swift in Sources */,
FDC4389D27BA01F000C60D73 /* MockStorage.swift in Sources */,
FD3C906727E416AF00CD579F /* BlindedIdMappingSpec.swift in Sources */,
FD83B9D227D59495005E1583 /* MockUserDefaults.swift in Sources */,
FD078E5C27E29F78000769AF /* MockNonce24Generator.swift in Sources */,

@ -176,6 +176,27 @@ public extension OpenGroup {
}
}
extension OpenGroup: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String { "\(name) (Server: \(server), Room: \(roomToken))" }
public var debugDescription: String {
[
"OpenGroup(server: \"\(server)\"",
"roomToken: \"\(roomToken)\"",
"id: \"\(id)\"",
"publicKey: \"\(publicKey)\"",
"isActive: \"\(isActive)\"",
"name: \"\(name)\"",
"roomDescription: \(roomDescription.map { "\"\($0)\"" } ?? "null")",
"imageId: \(imageId ?? "null")",
"userCount: \(userCount)",
"infoUpdates: \(infoUpdates)",
"sequenceNumber: \(sequenceNumber)",
"inboxLatestMessageId: \(inboxLatestMessageId)",
"outboxLatestMessageId: \(outboxLatestMessageId))"
].joined(separator: ", ")
}
}
// MARK: - Objective-C Support
// TODO: Remove this when possible

@ -1,24 +0,0 @@
// 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)
}
}

@ -4,6 +4,7 @@ import Foundation
import Quick
import Nimble
import SessionUtilitiesKit
@testable import SessionMessagingKit

@ -3,6 +3,7 @@
import Foundation
import PromiseKit
import SessionSnodeKit
import SessionUtilitiesKit
import Quick
import Nimble

@ -16,55 +16,40 @@ class OpenGroupSpec: QuickSpec {
it("generates the id") {
let openGroup: OpenGroup = OpenGroup(
server: "server",
room: "room",
roomToken: "room",
publicKey: "1234",
isActive: true,
name: "name",
groupDescription: nil,
imageID: nil,
infoUpdates: 0
roomDescription: nil,
imageId: nil,
imageData: nil,
userCount: 0,
infoUpdates: 0,
sequenceNumber: 0,
inboxLatestMessageId: 0,
outboxLatestMessageId: 0
)
expect(openGroup.id).to(equal("server.room"))
}
}
context("when NSCoding") {
// Note: Unit testing NSCoder is horrible so we won't do it properly - wait until we refactor it to Codable
it("successfully encodes and decodes") {
let openGroupToEncode: OpenGroup = OpenGroup(
server: "server",
room: "room",
publicKey: "1234",
name: "name",
groupDescription: "desc",
imageID: "image",
infoUpdates: 1
)
let encodedData: Data = try! NSKeyedArchiver.archivedData(withRootObject: openGroupToEncode, requiringSecureCoding: false)
let openGroup: OpenGroup? = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as? OpenGroup
expect(openGroup).toNot(beNil())
expect(openGroup?.id).to(equal("server.room"))
expect(openGroup?.server).to(equal("server"))
expect(openGroup?.room).to(equal("room"))
expect(openGroup?.publicKey).to(equal("1234"))
expect(openGroup?.name).to(equal("name"))
expect(openGroup?.groupDescription).to(equal("desc"))
expect(openGroup?.imageID).to(equal("image"))
expect(openGroup?.infoUpdates).to(equal(1))
}
}
context("when describing") {
it("includes relevant information") {
let openGroup: OpenGroup = OpenGroup(
server: "server",
room: "room",
roomToken: "room",
publicKey: "1234",
isActive: true,
name: "name",
groupDescription: nil,
imageID: nil,
infoUpdates: 0
roomDescription: nil,
imageId: nil,
imageData: nil,
userCount: 0,
infoUpdates: 0,
sequenceNumber: 0,
inboxLatestMessageId: 0,
outboxLatestMessageId: 0
)
expect(openGroup.description)
@ -76,16 +61,22 @@ class OpenGroupSpec: QuickSpec {
it("includes relevant information") {
let openGroup: OpenGroup = OpenGroup(
server: "server",
room: "room",
roomToken: "room",
publicKey: "1234",
isActive: true,
name: "name",
groupDescription: nil,
imageID: nil,
infoUpdates: 0
roomDescription: nil,
imageId: nil,
imageData: nil,
userCount: 0,
infoUpdates: 0,
sequenceNumber: 0,
inboxLatestMessageId: 0,
outboxLatestMessageId: 0
)
expect(openGroup.debugDescription)
.to(equal("OpenGroup(server: \"server\", room: \"room\", id: \"server.room\", publicKey: \"1234\", name: \"name\", groupDescription: null, imageID: null, infoUpdates: 0)"))
.to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0)"))
}
}
}

@ -4,6 +4,7 @@ import Foundation
import Quick
import Nimble
import SessionUtilitiesKit
@testable import SessionMessagingKit

@ -1,74 +0,0 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Quick
import Nimble
@testable import SessionMessagingKit
class ServerSpec: QuickSpec {
// MARK: - Spec
override func spec() {
describe("an Open Group Server") {
context("when initializing") {
it("converts the server name to lowercase") {
let server: OpenGroupAPI.Server = OpenGroupAPI.Server(
name: "TeSt",
capabilities: OpenGroupAPI.Capabilities(capabilities: [], missing: nil)
)
expect(server.name).to(equal("test"))
}
}
context("when NSCoding") {
// Note: Unit testing NSCoder is horrible so we won't do it properly - wait until we refactor it to Codable
it("successfully encodes and decodes") {
let serverToEncode: OpenGroupAPI.Server = OpenGroupAPI.Server(
name: "test",
capabilities: OpenGroupAPI.Capabilities(
capabilities: [.sogs, .unsupported("other")],
missing: [.blind, .unsupported("other2")])
)
let encodedData: Data = try! NSKeyedArchiver.archivedData(withRootObject: serverToEncode, requiringSecureCoding: false)
let server: OpenGroupAPI.Server? = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as? OpenGroupAPI.Server
expect(server).toNot(beNil())
expect(server?.name).to(equal("test"))
expect(server?.capabilities.capabilities).to(equal([.sogs, .unsupported("other")]))
expect(server?.capabilities.missing).to(equal([.blind, .unsupported("other2")]))
}
}
context("when describing") {
it("includes relevant information") {
let server: OpenGroupAPI.Server = OpenGroupAPI.Server(
name: "TeSt",
capabilities: OpenGroupAPI.Capabilities(
capabilities: [.sogs, .unsupported("other")],
missing: [.blind, .unsupported("other2")]
)
)
expect(server.description)
.to(equal("test (Capabilities: [sogs, other], Missing: [blind, other2])"))
}
it("handles nil missing capabilities") {
let server: OpenGroupAPI.Server = OpenGroupAPI.Server(
name: "TeSt",
capabilities: OpenGroupAPI.Capabilities(
capabilities: [.sogs, .unsupported("other")],
missing: nil
)
)
expect(server.description)
.to(equal("test (Capabilities: [sogs, other], Missing: [])"))
}
}
}
}
}

@ -1,8 +1,10 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import PromiseKit
import GRDB
import Sodium
import SessionSnodeKit
import SessionUtilitiesKit
import Quick
import Nimble
@ -13,7 +15,7 @@ class OpenGroupAPISpec: QuickSpec {
// MARK: - Spec
override func spec() {
var mockStorage: MockStorage!
var mockStorage: GRDBStorage!
var mockSodium: MockSodium!
var mockAeadXChaCha20Poly1305Ietf: MockAeadXChaCha20Poly1305Ietf!
var mockSign: MockSign!
@ -31,7 +33,7 @@ class OpenGroupAPISpec: QuickSpec {
// MARK: - Configuration
beforeEach {
mockStorage = MockStorage()
mockStorage = GRDBStorage(customWriter: DatabaseQueue())
mockSodium = MockSodium()
mockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
mockSign = MockSign()
@ -52,61 +54,23 @@ class OpenGroupAPISpec: QuickSpec {
date: Date(timeIntervalSince1970: 1234567890)
)
mockStorage
.when { $0.write(with: { _ in }) }
.then { args in (args.first as? ((Any) -> Void))?(anyAny()) }
.thenReturn(Promise.value(()))
mockStorage
.when { $0.write(with: { _ in }, completion: { }) }
.then { args in
(args.first as? ((Any) -> Void))?(anyAny())
(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.getOpenGroupServer(name: any()) }
.thenReturn(
OpenGroupAPI.Server(
name: "testServer",
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
)
)
mockStorage
.when { $0.getOpenGroupPublicKey(for: any()) }
.thenReturn(TestConstants.publicKey)
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(nil)
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(nil)
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(nil)
mockStorage.when { $0.addReceivedMessageTimestamp(any(), using: anyAny()) }.thenReturn(())
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 OpenGroup(
server: "testServer",
roomToken: "testRoom",
publicKey: TestConstants.publicKey,
name: "Test",
groupDescription: nil,
imageID: nil,
infoUpdates: 0
).insert(db)
try Capability(openGroupServer: "testserver", variant: .sogs, isMissing: false)
}
mockGenericHash.when { $0.hash(message: anyArray(), outputLength: any()) }.thenReturn([])
mockSodium
@ -233,13 +197,16 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent messages if there was no last message") {
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()
@ -254,15 +221,21 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent messages if there was a last message and it has not performed the initial poll and the last message was too long ago") {
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
mockStorage.write { db in
try OpenGroup
.updateAll(db, OpenGroup.Columns.sequenceNumber.set(to: 123))
}
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: false,
timeSinceLastPoll: (OpenGroupAPI.Poller.maxInactivityPeriod + 1),
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: false,
timeSinceLastPoll: (OpenGroupAPI.Poller.maxInactivityPeriod + 1),
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -277,15 +250,21 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent messages if there was a last message and it has performed an initial poll but it was not too long ago") {
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
mockStorage.write { db in
try OpenGroup
.updateAll(db, OpenGroup.Columns.sequenceNumber.set(to: 123))
}
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()
@ -300,15 +279,21 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent messages if there was a last message and there has already been a poll this session") {
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
mockStorage.write { db in
try OpenGroup
.updateAll(db, OpenGroup.Columns.sequenceNumber.set(to: 123))
}
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -324,18 +309,23 @@ 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("does not call the inbox and outbox endpoints") {
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()
@ -417,18 +407,24 @@ class OpenGroupAPISpec: QuickSpec {
dependencies = dependencies.with(onionApi: TestApi.self)
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("includes the inbox and outbox endpoints") {
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()
@ -446,13 +442,16 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent inbox messages if there was no last message") {
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -467,15 +466,21 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves inbox messages since the last message if there was one") {
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(124)
mockStorage.write { db in
try OpenGroup
.updateAll(db, OpenGroup.Columns.inboxLatestMessageId.set(to: 124))
}
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -490,13 +495,16 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves recent outbox messages if there was no last message") {
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -511,15 +519,21 @@ class OpenGroupAPISpec: QuickSpec {
}
it("retrieves outbox messages since the last message if there was one") {
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(125)
mockStorage.write { db in
try OpenGroup
.updateAll(db, OpenGroup.Columns.outboxLatestMessageId.set(to: 125))
}
OpenGroupAPI
.poll(
"testServer",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
mockStorage
.read { db in
OpenGroupAPI.poll(
db,
server: "testserver",
hasPerformedInitialPoll: true,
timeSinceLastPoll: 0,
using: dependencies
)
}
.get { result in pollResponse = result }
.catch { requestError in error = requestError }
.retainUntilComplete()
@ -571,7 +585,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()

@ -1,8 +1,10 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import PromiseKit
import GRDB
import Sodium
import SessionSnodeKit
import SessionUtilitiesKit
import Quick
import Nimble
@ -70,9 +72,8 @@ class OpenGroupManagerSpec: QuickSpec {
override func spec() {
var mockOGMCache: MockOGMCache!
var mockIdentityManager: MockIdentityManager!
var mockGeneralCache: MockGeneralCache!
var mockStorage: MockStorage!
var mockStorage: GRDBStorage!
var mockSodium: MockSodium!
var mockAeadXChaCha20Poly1305Ietf: MockAeadXChaCha20Poly1305Ietf!
var mockGenericHash: MockGenericHash!
@ -100,9 +101,8 @@ class OpenGroupManagerSpec: QuickSpec {
beforeEach {
mockOGMCache = MockOGMCache()
mockIdentityManager = MockIdentityManager()
mockGeneralCache = MockGeneralCache()
mockStorage = MockStorage()
mockStorage = GRDBStorage(customWriter: DatabaseQueue())
mockSodium = MockSodium()
mockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
mockGenericHash = MockGenericHash()
@ -155,7 +155,7 @@ class OpenGroupManagerSpec: QuickSpec {
testOpenGroup = OpenGroup(
server: "testServer",
room: "testRoom",
roomToken: "testRoom",
publicKey: TestConstants.publicKey,
name: "Test",
groupDescription: nil,
@ -215,62 +215,16 @@ class OpenGroupManagerSpec: QuickSpec {
).base64EncodedString()
)
mockIdentityManager
.when { $0.identityKeyPair() }
.thenReturn(
try! ECKeyPair(
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
)
)
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 testOpenGroup.insert(db)
try Capability(openGroupServer: testOpenGroup.server, variant: .sogs, isMissing: false).insert(db)
}
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) }
.thenReturn(Promise.value(()))
mockStorage
.when { $0.write(with: { _ in }, completion: { }) }
.then { [testTransaction] 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": testOpenGroup
])
mockStorage
.when { $0.getOpenGroup(for: any()) }
.thenReturn(testOpenGroup)
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: anyArray(), outputLength: any()) }.thenReturn([])
mockSodium
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
@ -370,28 +324,17 @@ class OpenGroupManagerSpec: QuickSpec {
context("when starting polling") {
beforeEach {
mockStorage
.when { $0.getAllOpenGroups() }
.thenReturn([
"0": testOpenGroup,
"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: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroupPublicKey(for: any(), to: any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroupServer(any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroup(any(), for: any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setUserCount(to: any(), forOpenGroupWithID: any(), using: anyAny()) }.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)
mockStorage.write { db in
try OpenGroup(
server: "testServer1",
room: "testRoom1",
publicKey: TestConstants.publicKey,
name: "Test1",
groupDescription: nil,
imageID: nil,
infoUpdates: 0
).insert(db)
}
mockOGMCache.when { $0.hasPerformedInitialPoll }.thenReturn([:])
mockOGMCache.when { $0.timeSinceLastPoll }.thenReturn([:])
@ -433,28 +376,17 @@ class OpenGroupManagerSpec: QuickSpec {
context("when stopping polling") {
beforeEach {
mockStorage
.when { $0.getAllOpenGroups() }
.thenReturn([
"0": testOpenGroup,
"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: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroupPublicKey(for: any(), to: any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroupServer(any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setOpenGroup(any(), for: any(), using: anyAny()) }.thenReturn(())
mockStorage.when { $0.setUserCount(to: any(), forOpenGroupWithID: any(), using: anyAny()) }.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)
mockStorage.write { db in
try OpenGroup(
server: "testServer1",
room: "testRoom1",
publicKey: TestConstants.publicKey,
name: "Test1",
groupDescription: nil,
imageId: nil,
infoUpdates: 0
).insert(db)
}
mockOGMCache.when { $0.isPolling }.thenReturn(true)
mockOGMCache.when { $0.pollers }.thenReturn(["testserver": OpenGroupAPI.Poller(for: "testserver")])

@ -1,7 +1,9 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import Sodium
import SessionUtilitiesKit
import Quick
import Nimble
@ -12,7 +14,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
// MARK: - Spec
override func spec() {
var mockStorage: MockStorage!
var mockStorage: GRDBStorage!
var mockBox: MockBox!
var mockSign: MockSign!
var mockNonce24Generator: MockNonce24Generator!
@ -20,7 +22,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
describe("a MessageSender") {
beforeEach {
mockStorage = MockStorage()
mockStorage = GRDBStorage(customWriter: DatabaseQueue())
mockBox = MockBox()
mockSign = MockSign()
mockNonce24Generator = MockNonce24Generator()
@ -32,13 +34,10 @@ class MessageSenderEncryptionSpec: 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)
}
mockNonce24Generator
.when { $0.nonce() }
.thenReturn(Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes)
@ -73,7 +72,10 @@ class MessageSenderEncryptionSpec: QuickSpec {
}
it("throws an error if there is no ed25519 keyPair") {
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil)
mockStorage.write { db in
_ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db)
_ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db)
}
expect {
try MessageSender.encryptWithSessionProtocol(
@ -82,7 +84,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.noUserED25519KeyPair))
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
it("throws an error if the signature generation fails") {
@ -95,7 +97,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.signingFailed))
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if the encryption fails") {
@ -108,7 +110,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.encryptionFailed))
.to(throwError(MessageSenderError.encryptionFailed))
}
}
@ -163,11 +165,14 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.signingFailed))
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if there is no ed25519 keyPair") {
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil)
mockStorage.write { db in
_ = try Identity.filter(id: .ed25519PublicKey).deleteAll(db)
_ = try Identity.filter(id: .ed25519SecretKey).deleteAll(db)
}
expect {
try MessageSender.encryptWithSessionBlindingProtocol(
@ -177,7 +182,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.noUserED25519KeyPair))
.to(throwError(MessageSenderError.noUserED25519KeyPair))
}
it("throws an error if it fails to generate a blinded keyPair") {
@ -203,7 +208,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.signingFailed))
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if it fails to generate an encryption key") {
@ -245,7 +250,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.signingFailed))
.to(throwError(MessageSenderError.signingFailed))
}
it("throws an error if it fails to encrypt") {
@ -264,7 +269,7 @@ class MessageSenderEncryptionSpec: QuickSpec {
using: dependencies
)
}
.to(throwError(MessageSender.Error.encryptionFailed))
.to(throwError(MessageSenderError.encryptionFailed))
}
}
}

@ -2,15 +2,15 @@
import Foundation
import SessionSnodeKit
import SessionUtilitiesKit
@testable import SessionMessagingKit
extension Dependencies {
public func with(
onionApi: OnionRequestAPIType.Type? = nil,
identityManager: IdentityManagerProtocol? = nil,
generalCache: Atomic<GeneralCacheType>? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
storage: GRDBStorage? = nil,
sodium: SodiumType? = nil,
box: BoxType? = nil,
genericHash: GenericHashType? = nil,
@ -24,7 +24,6 @@ extension Dependencies {
) -> Dependencies {
return Dependencies(
onionApi: (onionApi ?? self._onionApi),
identityManager: (identityManager ?? self._identityManager),
generalCache: (generalCache ?? self._generalCache),
storage: (storage ?? self._storage),
sodium: (sodium ?? self._sodium),

@ -1,6 +1,7 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import SessionUtilitiesKit
@testable import SessionMessagingKit

@ -1,9 +0,0 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
@testable import SessionMessagingKit
class MockIdentityManager: Mock<IdentityManagerProtocol>, IdentityManagerProtocol {
func identityKeyPair() -> ECKeyPair? { return accept() as? ECKeyPair }
}

@ -1,228 +0,0 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import PromiseKit
import Sodium
@testable import SessionMessagingKit
class MockStorage: Mock<SessionMessagingKitStorageProtocol>, SessionMessagingKitStorageProtocol {
// MARK: - Shared
@discardableResult func write(with block: @escaping (Any) -> Void) -> Promise<Void> {
return accept(args: [block]) as! Promise<Void>
}
@discardableResult func write(with block: @escaping (Any) -> Void, completion: @escaping () -> Void) -> Promise<Void> {
return accept(args: [block, completion]) as! Promise<Void>
}
func writeSync(with block: @escaping (Any) -> Void) {
accept(args: [block])
}
// MARK: - General
func getUserPublicKey() -> String? { return accept() as? String }
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
func getContact(with sessionID: String) -> Contact? { return accept(args: [sessionID]) as? Contact }
func getContact(with sessionID: String, using transaction: Any) -> Contact? {
return accept(args: [sessionID, transaction]) as? Contact
}
func setContact(_ contact: Contact, using transaction: Any) { accept(args: [contact, transaction]) }
func getAllContacts() -> Set<Contact> { return accept() as! Set<Contact> }
func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set<Contact> { return accept() as! Set<Contact> }
// MARK: - Blinded Id cache
func getBlindedIdMapping(with blindedId: String) -> BlindedIdMapping? {
return accept(args: [blindedId]) as? BlindedIdMapping
}
func getBlindedIdMapping(with blindedId: String, using transaction: YapDatabaseReadTransaction) -> BlindedIdMapping? {
return accept(args: [blindedId, transaction]) as? BlindedIdMapping
}
func cacheBlindedIdMapping(_ mapping: BlindedIdMapping) { accept(args: [mapping]) }
func cacheBlindedIdMapping(_ mapping: BlindedIdMapping, using transaction: YapDatabaseReadWriteTransaction) {
accept(args: [mapping, transaction])
}
func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer<ObjCBool>) -> ()) {
accept(args: [block])
}
func enumerateBlindedIdMapping(using transaction: YapDatabaseReadTransaction, with block: @escaping (BlindedIdMapping, UnsafeMutablePointer<ObjCBool>) -> ()) {
accept(args: [transaction, block])
}
// MARK: - Closed Groups
func getClosedGroupEncryptionKeyPairs(for groupPublicKey: String) -> [ECKeyPair] {
return accept(args: [groupPublicKey]) as! [ECKeyPair]
}
func getLatestClosedGroupEncryptionKeyPair(for groupPublicKey: String) -> ECKeyPair? {
return accept(args: [groupPublicKey]) as? ECKeyPair
}
func addClosedGroupEncryptionKeyPair(_ keyPair: ECKeyPair, for groupPublicKey: String, using transaction: Any) {
accept(args: [keyPair, groupPublicKey, transaction])
}
func removeAllClosedGroupEncryptionKeyPairs(for groupPublicKey: String, using transaction: Any) {
accept(args: [groupPublicKey, transaction])
}
func getUserClosedGroupPublicKeys() -> Set<String> { return accept() as! Set<String> }
func getUserClosedGroupPublicKeys(using transaction: YapDatabaseReadTransaction) -> Set<String> {
return accept(args: [transaction]) as! Set<String>
}
func getZombieMembers(for groupPublicKey: String) -> Set<String> { return accept(args: [groupPublicKey]) as! Set<String> }
func setZombieMembers(for groupPublicKey: String, to zombies: Set<String>, using transaction: Any) {
accept(args: [groupPublicKey, zombies, transaction])
}
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
func persist(_ job: Job, using transaction: Any) { accept(args: [job, transaction]) }
func markJobAsSucceeded(_ job: Job, using transaction: Any) { accept(args: [job, transaction]) }
func markJobAsFailed(_ job: Job, using transaction: Any) { accept(args: [job, transaction]) }
func getAllPendingJobs(of type: Job.Type) -> [Job] {
return accept(args: [type]) as! [Job]
}
func getAttachmentUploadJob(for attachmentID: String) -> AttachmentUploadJob? {
return accept(args: [attachmentID]) as? AttachmentUploadJob
}
func getMessageSendJob(for messageSendJobID: String) -> MessageSendJob? {
return accept(args: [messageSendJobID]) as? MessageSendJob
}
func getMessageSendJob(for messageSendJobID: String, using transaction: Any) -> MessageSendJob? {
return accept(args: [messageSendJobID, transaction]) as? MessageSendJob
}
func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) { accept(args: [messageSendJobID]) }
func isJobCanceled(_ job: Job) -> Bool {
return accept(args: [job]) as! Bool
}
// MARK: - Open Groups
func getAllOpenGroups() -> [String: OpenGroup] { return accept() as! [String: OpenGroup] }
func getThreadID(for v2OpenGroupID: String) -> String? { return accept(args: [v2OpenGroupID]) as? String }
func getOpenGroupImage(for room: String, on server: String) -> Data? { return accept(args: [room, server]) as? Data }
func setOpenGroupImage(to data: Data, for room: String, on server: String, using transaction: Any) {
accept(args: [data, room, server, transaction])
}
func getOpenGroup(for threadID: String) -> OpenGroup? { return accept(args: [threadID]) as? OpenGroup }
func setOpenGroup(_ openGroup: OpenGroup, for threadID: String, using transaction: Any) {
accept(args: [openGroup, threadID, transaction])
}
func removeOpenGroup(for threadID: String, using transaction: Any) { accept(args: [threadID, transaction]) }
func getOpenGroupServer(name: String) -> OpenGroupAPI.Server? { return accept(args: [name]) as? OpenGroupAPI.Server }
func setOpenGroupServer(_ server: OpenGroupAPI.Server, using transaction: Any) { accept(args: [server, transaction]) }
func removeOpenGroupServer(name: String, using transaction: Any) {
accept(args: [name, transaction])
}
func getUserCount(forOpenGroupWithID openGroupID: String) -> UInt64? { return accept(args: [openGroupID]) as? UInt64 }
func setUserCount(to newValue: UInt64, forOpenGroupWithID openGroupID: String, using transaction: Any) {
accept(args: [newValue, openGroupID, transaction])
}
func getOpenGroupSequenceNumber(for room: String, on server: String) -> Int64? {
return accept(args: [room, server]) as? Int64
}
func setOpenGroupSequenceNumber(for room: String, on server: String, to newValue: Int64, using transaction: Any) {
accept(args: [room, server, newValue, transaction])
}
func removeOpenGroupSequenceNumber(for room: String, on server: String, using transaction: Any) {
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])
}
func removeOpenGroupInboxLatestMessageId(for server: String, using transaction: Any) { accept(args: [server, transaction]) }
func getOpenGroupOutboxLatestMessageId(for server: String) -> Int64? { return accept(args: [server]) as? Int64 }
func setOpenGroupOutboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) {
accept(args: [server, newValue, transaction])
}
func removeOpenGroupOutboxLatestMessageId(for server: String, using transaction: Any) {
accept(args: [server, transaction])
}
// MARK: - Open Group Public Keys
func getOpenGroupPublicKey(for server: String) -> String? { return accept(args: [server]) as? String }
func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {
accept(args: [server, newValue, transaction])
}
func removeOpenGroupPublicKey(for server: String, using transaction: Any) { accept(args: [server, transaction]) }
// MARK: - Message Handling
func getAllMessageRequestThreads() -> [String: TSContactThread] { return accept() as! [String: TSContactThread] }
func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread] {
return accept(args: [transaction]) as! [String: TSContactThread]
}
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] {
return accept(args: [transaction]) as! [UInt64]
}
func removeReceivedMessageTimestamps(_ timestamps: Set<UInt64>, using transaction: Any) {
accept(args: [timestamps, transaction])
}
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {
accept(args: [timestamp, transaction])
}
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? {
return accept(args: [publicKey, groupPublicKey, openGroupID, transaction]) as? String
}
func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? {
return accept(args: [message, quotedMessage, linkPreview, groupPublicKey, openGroupID, transaction]) as? String
}
func persist(_ attachments: [VisibleMessage.Attachment], using transaction: Any) -> [String] {
return accept(args: [attachments, transaction]) as! [String]
}
func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsIncomingMessageID: String, using transaction: Any) {
accept(args: [state, pointer, tsIncomingMessageID, transaction])
}
func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any) {
accept(args: [stream, tsIncomingMessageID, transaction])
}
// MARK: - Calls
func getReceivedCalls(for publicKey: String, using transaction: Any) -> Set<String> {
return accept(args: [publicKey, transaction]) as! Set<String>
}
func setReceivedCalls(to receivedCalls: Set<String>, for publicKey: String, using transaction: Any) {
accept(args: [receivedCalls, publicKey, transaction])
}
}

@ -6,19 +6,18 @@ import SessionMessagingKit
extension OpenGroup: Mocked {
static var mockValue: OpenGroup = OpenGroup(
server: any(),
room: any(),
roomToken: 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: anyArray(), missing: anyArray())
isActive: any(),
roomDescription: any(),
imageId: any(),
imageData: any(),
userCount: any(),
infoUpdates: any(),
sequenceNumber: any(),
inboxLatestMessageId: any(),
outboxLatestMessageId: any()
)
}

@ -2,6 +2,7 @@
import Foundation
import SessionSnodeKit
import SessionUtilitiesKit
@testable import SessionMessagingKit
@ -9,9 +10,8 @@ extension OpenGroupManager.OGMDependencies {
public func with(
cache: Atomic<OGMCacheType>? = nil,
onionApi: OnionRequestAPIType.Type? = nil,
identityManager: IdentityManagerProtocol? = nil,
generalCache: Atomic<GeneralCacheType>? = nil,
storage: SessionMessagingKitStorageProtocol? = nil,
storage: GRDBStorage? = nil,
sodium: SodiumType? = nil,
box: BoxType? = nil,
genericHash: GenericHashType? = nil,
@ -26,7 +26,6 @@ extension OpenGroupManager.OGMDependencies {
return 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),

@ -3,6 +3,7 @@
import Foundation
import PromiseKit
import SessionSnodeKit
import SessionUtilitiesKit
@testable import SessionMessagingKit
@ -16,7 +17,7 @@ class TestOnionRequestAPI: OnionRequestAPIType {
let body: Data?
let server: String
let version: OnionRequestAPI.Version
let version: OnionRequestAPIVersion
let publicKey: String?
}
class ResponseInfo: OnionRequestResponseInfoType {
@ -33,7 +34,7 @@ class TestOnionRequestAPI: OnionRequestAPIType {
class var mockResponse: Data? { return nil }
static func sendOnionRequest(_ request: URLRequest, to server: String, using version: OnionRequestAPI.Version, with x25519PublicKey: String) -> Promise<(OnionRequestResponseInfoType, Data?)> {
static func sendOnionRequest(_ request: URLRequest, to server: String, using version: OnionRequestAPIVersion, with x25519PublicKey: String) -> Promise<(OnionRequestResponseInfoType, Data?)> {
let responseInfo: ResponseInfo = ResponseInfo(
requestData: RequestData(
urlString: request.url?.absoluteString,
@ -53,7 +54,7 @@ class TestOnionRequestAPI: OnionRequestAPIType {
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> {
static func sendOnionRequest(to snode: Snode, invoking method: SnodeAPIEndpoint, with parameters: JSON, using version: OnionRequestAPIVersion, associatedWith publicKey: String?) -> Promise<Data> {
return Promise.value(mockResponse!)
}
}

@ -1,3 +1,5 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import CryptoSwift
import GRDB

@ -26,17 +26,17 @@ public final class GRDBStorage {
public private(set) var isValid: Bool = false
public private(set) var hasCompletedMigrations: Bool = false
private var dbPool: DatabasePool?
private var dbWriter: DatabaseWriter?
private var migrator: DatabaseMigrator?
private var migrationProgressUpdater: Atomic<((String, CGFloat) -> ())>?
// MARK: - Initialization
public init() {
// if CurrentAppContext().isMainApp {
// GRDBStorage.deleteDatabaseFiles() // TODO: Remove this.
// try! GRDBStorage.deleteDbKeys() // TODO: Remove this.
// }
public init(customWriter: DatabaseWriter? = nil) {
// Create the database directory if needed and ensure it's protection level is set before attempting to
// create the database KeySpec or the database itself
@ -76,9 +76,16 @@ public final class GRDBStorage {
try db.execute(sql: "PRAGMA cipher_plaintext_header_size = 32")
}
// If a custom writer was provided then use that (for unit testing)
guard customWriter == nil else {
dbWriter = customWriter
isValid = true
return
}
// Create the DatabasePool to allow us to connect to the database and mark the storage as valid
do {
dbPool = try DatabasePool(
dbWriter = try DatabasePool(
path: "\(GRDBStorage.sharedDatabaseDirectoryPath)/\(GRDBStorage.dbFileName)",
configuration: config
)
@ -94,7 +101,7 @@ public final class GRDBStorage {
onProgressUpdate: ((CGFloat, TimeInterval) -> ())?,
onComplete: @escaping (Bool, Bool) -> ()
) {
guard isValid, let dbPool: DatabasePool = dbPool else { return }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return }
typealias MigrationInfo = (identifier: TargetMigrations.Identifier, migrations: TargetMigrations.MigrationSet)
let sortedMigrationInfo: [MigrationInfo] = migrations
@ -124,7 +131,7 @@ public final class GRDBStorage {
// Determine which migrations need to be performed and gather the relevant settings needed to
// inform the app of progress/states
let completedMigrations: [String] = (try? dbPool.read { db in try migrator?.completedMigrations(db) })
let completedMigrations: [String] = (try? dbWriter.read { db in try migrator?.completedMigrations(db) })
.defaulting(to: [])
let unperformedMigrations: [(key: String, migration: Migration.Type)] = sortedMigrationInfo
.reduce(into: []) { result, next in
@ -167,7 +174,7 @@ public final class GRDBStorage {
self.migrationProgressUpdater?.wrappedValue(firstMigrationKey, 0)
}
self.migrator?.asyncMigrate(dbPool) { [weak self] _, error in
self.migrator?.asyncMigrate(dbWriter) { [weak self] _, error in
self?.hasCompletedMigrations = true
self?.migrationProgressUpdater = nil
SUKLegacy.clearLegacyDatabaseInstance()
@ -280,9 +287,9 @@ public final class GRDBStorage {
// MARK: - Functions
@discardableResult public func write<T>(updates: (Database) throws -> T?) -> T? {
guard isValid, let dbPool: DatabasePool = dbPool else { return nil }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return nil }
return try? dbPool.write(updates)
return try? dbWriter.write(updates)
}
public func writeAsync<T>(updates: @escaping (Database) throws -> T) {
@ -290,9 +297,9 @@ public final class GRDBStorage {
}
public func writeAsync<T>(updates: @escaping (Database) throws -> T, completion: @escaping (Database, Swift.Result<T, Error>) throws -> Void) {
guard isValid, let dbPool: DatabasePool = dbPool else { return }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return }
dbPool.asyncWrite(
dbWriter.asyncWrite(
updates,
completion: { db, result in
try? completion(db, result)
@ -301,9 +308,9 @@ public final class GRDBStorage {
}
@discardableResult public func read<T>(_ value: (Database) throws -> T?) -> T? {
guard isValid, let dbPool: DatabasePool = dbPool else { return nil }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return nil }
return try? dbPool.read(value)
return try? dbWriter.read(value)
}
/// Rever to the `ValueObservation.start` method for full documentation
@ -321,10 +328,10 @@ public final class GRDBStorage {
onError: @escaping (Error) -> Void,
onChange: @escaping (Reducer.Value) -> Void
) -> DatabaseCancellable {
guard isValid, let dbPool: DatabasePool = dbPool else { return AnyDatabaseCancellable(cancel: {}) }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return AnyDatabaseCancellable(cancel: {}) }
return observation.start(
in: dbPool,
in: dbWriter,
scheduling: scheduler,
onError: onError,
onChange: onChange
@ -332,17 +339,17 @@ public final class GRDBStorage {
}
public func addObserver(_ observer: TransactionObserver?) {
guard isValid, let dbPool: DatabasePool = dbPool else { return }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return }
guard let observer: TransactionObserver = observer else { return }
dbPool.add(transactionObserver: observer)
dbWriter.add(transactionObserver: observer)
}
public func removeObserver(_ observer: TransactionObserver?) {
guard isValid, let dbPool: DatabasePool = dbPool else { return }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else { return }
guard let observer: TransactionObserver = observer else { return }
dbPool.remove(transactionObserver: observer)
dbWriter.remove(transactionObserver: observer)
}
}
@ -351,10 +358,12 @@ public final class GRDBStorage {
public extension GRDBStorage {
// FIXME: Would be good to replace these with Swift Combine
@discardableResult func read<T>(_ value: (Database) throws -> Promise<T>) -> Promise<T> {
guard isValid, let dbPool: DatabasePool = dbPool else { return Promise(error: StorageError.databaseInvalid) }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else {
return Promise(error: StorageError.databaseInvalid)
}
do {
return try dbPool.read(value)
return try dbWriter.read(value)
}
catch {
return Promise(error: error)
@ -362,10 +371,12 @@ public extension GRDBStorage {
}
@discardableResult func write<T>(updates: (Database) throws -> Promise<T>) -> Promise<T> {
guard isValid, let dbPool: DatabasePool = dbPool else { return Promise(error: StorageError.databaseInvalid) }
guard isValid, let dbWriter: DatabaseWriter = dbWriter else {
return Promise(error: StorageError.databaseInvalid)
}
do {
return try dbPool.write(updates)
return try dbWriter.write(updates)
}
catch {
return Promise(error: error)

@ -27,6 +27,16 @@ public struct Identity: Codable, Identifiable, FetchableRecord, PersistableRecor
let variant: Variant
let data: Data
// MARK: - Initialization
public init(
variant: Variant,
data: Data
) {
self.variant = variant
self.data = data
}
}
// MARK: - Convenience

Loading…
Cancel
Save