Refactored the mocking code to use a better convention which also allows for call validation
Added a Nimble predicate for checking a function on a mock was called Added the various remove methods to the Storage protocol Updated the Ed25519Type to be an instance-based protocol (needed for mocking)
This commit is contained in:
parent
a39afd6037
commit
31ecd78737
6
Podfile
6
Podfile
|
@ -57,7 +57,8 @@ abstract_target 'GlobalDependencies' do
|
|||
inherit! :complete
|
||||
|
||||
pod 'Quick'
|
||||
pod 'Nimble'
|
||||
# FIXME: change this back to use the latest 'Nimble' once a version newer than 9.2.1 has been released
|
||||
pod 'Nimble', :git => 'https://github.com/Quick/Nimble', :commit => 'cabe966'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,7 +69,8 @@ abstract_target 'GlobalDependencies' do
|
|||
inherit! :complete
|
||||
|
||||
pod 'Quick'
|
||||
pod 'Nimble'
|
||||
# FIXME: change this back to use the latest 'Nimble' once a version newer than 9.2.1 has been released
|
||||
pod 'Nimble', :git => 'https://github.com/Quick/Nimble', :commit => 'cabe966'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
15
Podfile.lock
15
Podfile.lock
|
@ -24,7 +24,7 @@ PODS:
|
|||
- Mantle (2.1.0):
|
||||
- Mantle/extobjc (= 2.1.0)
|
||||
- Mantle/extobjc (2.1.0)
|
||||
- Nimble (9.2.1)
|
||||
- Nimble (9.2.0)
|
||||
- NVActivityIndicatorView (5.1.1):
|
||||
- NVActivityIndicatorView/Base (= 5.1.1)
|
||||
- NVActivityIndicatorView/Base (5.1.1)
|
||||
|
@ -126,7 +126,7 @@ DEPENDENCIES:
|
|||
- CryptoSwift
|
||||
- Curve25519Kit (from `https://github.com/oxen-io/session-ios-curve-25519-kit.git`, branch `session-version`)
|
||||
- Mantle (from `https://github.com/signalapp/Mantle`, branch `signal-master`)
|
||||
- Nimble
|
||||
- Nimble (from `https://github.com/Quick/Nimble`, commit `cabe966`)
|
||||
- NVActivityIndicatorView
|
||||
- PromiseKit
|
||||
- PureLayout (~> 3.1.8)
|
||||
|
@ -145,7 +145,6 @@ SPEC REPOS:
|
|||
- AFNetworking
|
||||
- CocoaLumberjack
|
||||
- CryptoSwift
|
||||
- Nimble
|
||||
- NVActivityIndicatorView
|
||||
- OpenSSL-Universal
|
||||
- PromiseKit
|
||||
|
@ -164,6 +163,9 @@ EXTERNAL SOURCES:
|
|||
Mantle:
|
||||
:branch: signal-master
|
||||
:git: https://github.com/signalapp/Mantle
|
||||
Nimble:
|
||||
:commit: cabe966
|
||||
:git: https://github.com/Quick/Nimble
|
||||
SignalCoreKit:
|
||||
:branch: session-version
|
||||
:git: https://github.com/oxen-io/session-ios-core-kit
|
||||
|
@ -183,6 +185,9 @@ CHECKOUT OPTIONS:
|
|||
Mantle:
|
||||
:commit: e7e46253bb01ce39525d90aa69ed9e85e758bfc4
|
||||
:git: https://github.com/signalapp/Mantle
|
||||
Nimble:
|
||||
:commit: cabe966
|
||||
:git: https://github.com/Quick/Nimble
|
||||
SignalCoreKit:
|
||||
:commit: 4590c2737a2b5dc0ef4ace9f9019b581caccc1de
|
||||
:git: https://github.com/oxen-io/session-ios-core-kit
|
||||
|
@ -202,7 +207,7 @@ SPEC CHECKSUMS:
|
|||
CryptoSwift: a532e74ed010f8c95f611d00b8bbae42e9fe7c17
|
||||
Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6
|
||||
Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b
|
||||
Nimble: e7e615c0335ee4bf5b0d786685451e62746117d5
|
||||
Nimble: 0526ae760c851747ff4a682f7646af07a0cc2013
|
||||
NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667
|
||||
OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2
|
||||
PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5
|
||||
|
@ -218,6 +223,6 @@ SPEC CHECKSUMS:
|
|||
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: b95d8bb031996cffdb5d9b9b49bce3b24d6026d7
|
||||
PODFILE CHECKSUM: cb9862059da2976422ff9c4fa94d406b68581456
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
|
@ -801,11 +801,11 @@
|
|||
FD83B9D227D59495005E1583 /* TestUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9D127D59495005E1583 /* TestUserDefaults.swift */; };
|
||||
FD83B9D427D5A7D5005E1583 /* ConversationViewItem+Refactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD83B9D327D5A7D5005E1583 /* ConversationViewItem+Refactor.swift */; };
|
||||
FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF127BF6BA200510D0C /* Data+Utilities.swift */; };
|
||||
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* TestSodium.swift */; };
|
||||
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* TestSign.swift */; };
|
||||
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */; };
|
||||
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF927C2F5C500510D0C /* TestGenericHash.swift */; };
|
||||
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFB27C2F60700510D0C /* TestEd25519.swift */; };
|
||||
FD859EF427C2F49200510D0C /* MockSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* MockSodium.swift */; };
|
||||
FD859EF627C2F52C00510D0C /* MockSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* MockSign.swift */; };
|
||||
FD859EF827C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF727C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift */; };
|
||||
FD859EFA27C2F5C500510D0C /* MockGenericHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF927C2F5C500510D0C /* MockGenericHash.swift */; };
|
||||
FD859EFC27C2F60700510D0C /* MockEd25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFB27C2F60700510D0C /* MockEd25519.swift */; };
|
||||
FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; };
|
||||
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
|
||||
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
|
||||
|
@ -820,6 +820,15 @@
|
|||
FDC2909827D7129B005DAE71 /* PersonalizationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909727D7129B005DAE71 /* PersonalizationSpec.swift */; };
|
||||
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909927D71376005DAE71 /* NonceGeneratorSpec.swift */; };
|
||||
FDC2909C27D713D2005DAE71 /* SodiumProtocolsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909B27D713D2005DAE71 /* SodiumProtocolsSpec.swift */; };
|
||||
FDC2909E27D85751005DAE71 /* OpenGroupManagerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909D27D85751005DAE71 /* OpenGroupManagerSpec.swift */; };
|
||||
FDC290A027D85826005DAE71 /* TestThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC2909F27D85826005DAE71 /* TestThread.swift */; };
|
||||
FDC290A227D85890005DAE71 /* TestInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A127D85890005DAE71 /* TestInteraction.swift */; };
|
||||
FDC290A627D860CE005DAE71 /* Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A527D860CE005DAE71 /* Mock.swift */; };
|
||||
FDC290A827D9B46D005DAE71 /* NimbleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A727D9B46D005DAE71 /* NimbleExtensions.swift */; };
|
||||
FDC290A927D9B46D005DAE71 /* NimbleExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A727D9B46D005DAE71 /* NimbleExtensions.swift */; };
|
||||
FDC290AA27D9B6FD005DAE71 /* Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A527D860CE005DAE71 /* Mock.swift */; };
|
||||
FDC290AC27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290AB27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift */; };
|
||||
FDC290AD27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290AB27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift */; };
|
||||
FDC4380927B31D4E00C60D73 /* SOGSError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* SOGSError.swift */; };
|
||||
FDC4381527B329CE00C60D73 /* NonceGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381427B329CE00C60D73 /* NonceGenerator.swift */; };
|
||||
FDC4381727B32EC700C60D73 /* Personalization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4381627B32EC700C60D73 /* Personalization.swift */; };
|
||||
|
@ -844,12 +853,10 @@
|
|||
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 /* TestStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4389C27BA01F000C60D73 /* TestStorage.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 */; };
|
||||
FDC438AC27BB145200C60D73 /* UserDeleteMessagesRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438AB27BB145200C60D73 /* UserDeleteMessagesRequest.swift */; };
|
||||
FDC438AE27BB148700C60D73 /* UserDeleteMessagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438AD27BB148700C60D73 /* UserDeleteMessagesResponse.swift */; };
|
||||
FDC438B127BB159600C60D73 /* RequestInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438B027BB159600C60D73 /* RequestInfo.swift */; };
|
||||
FDC438B327BB15B400C60D73 /* ResponseInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438B227BB15B400C60D73 /* ResponseInfo.swift */; };
|
||||
FDC438B527BB15D400C60D73 /* Destination.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438B427BB15D400C60D73 /* Destination.swift */; };
|
||||
|
@ -863,7 +870,6 @@
|
|||
FDC438CB27BB7DB100C60D73 /* UpdateMessageRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CA27BB7DB100C60D73 /* UpdateMessageRequest.swift */; };
|
||||
FDC438CD27BC641200C60D73 /* Set+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CC27BC641200C60D73 /* Set+Utilities.swift */; };
|
||||
FDC438CF27BCA45400C60D73 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC438CE27BCA45400C60D73 /* Server.swift */; };
|
||||
FDC4389E27BA2B8A00C60D73 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -1937,11 +1943,11 @@
|
|||
FD859EEF27BF207700510D0C /* SessionProtos.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SessionProtos.proto; sourceTree = "<group>"; };
|
||||
FD859EF027BF207C00510D0C /* WebSocketResources.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = WebSocketResources.proto; sourceTree = "<group>"; };
|
||||
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FD859EF327C2F49200510D0C /* TestSodium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSodium.swift; sourceTree = "<group>"; };
|
||||
FD859EF527C2F52C00510D0C /* TestSign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSign.swift; sourceTree = "<group>"; };
|
||||
FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAeadXChaCha20Poly1305Ietf.swift; sourceTree = "<group>"; };
|
||||
FD859EF927C2F5C500510D0C /* TestGenericHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGenericHash.swift; sourceTree = "<group>"; };
|
||||
FD859EFB27C2F60700510D0C /* TestEd25519.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestEd25519.swift; sourceTree = "<group>"; };
|
||||
FD859EF327C2F49200510D0C /* MockSodium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSodium.swift; sourceTree = "<group>"; };
|
||||
FD859EF527C2F52C00510D0C /* MockSign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSign.swift; sourceTree = "<group>"; };
|
||||
FD859EF727C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAeadXChaCha20Poly1305Ietf.swift; sourceTree = "<group>"; };
|
||||
FD859EF927C2F5C500510D0C /* MockGenericHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGenericHash.swift; sourceTree = "<group>"; };
|
||||
FD859EFB27C2F60700510D0C /* MockEd25519.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockEd25519.swift; sourceTree = "<group>"; };
|
||||
FD859EFF27C4691300510D0C /* MockDataGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = "<group>"; };
|
||||
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
|
||||
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
|
||||
|
@ -1957,6 +1963,12 @@
|
|||
FDC2909727D7129B005DAE71 /* PersonalizationSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalizationSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2909927D71376005DAE71 /* NonceGeneratorSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGeneratorSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2909B27D713D2005DAE71 /* SodiumProtocolsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SodiumProtocolsSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2909D27D85751005DAE71 /* OpenGroupManagerSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenGroupManagerSpec.swift; sourceTree = "<group>"; };
|
||||
FDC2909F27D85826005DAE71 /* TestThread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestThread.swift; sourceTree = "<group>"; };
|
||||
FDC290A127D85890005DAE71 /* TestInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestInteraction.swift; sourceTree = "<group>"; };
|
||||
FDC290A527D860CE005DAE71 /* Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mock.swift; sourceTree = "<group>"; };
|
||||
FDC290A727D9B46D005DAE71 /* NimbleExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NimbleExtensions.swift; sourceTree = "<group>"; };
|
||||
FDC290AB27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BoxKeyPair+Mocked.swift"; sourceTree = "<group>"; };
|
||||
FDC4380827B31D4E00C60D73 /* SOGSError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSError.swift; sourceTree = "<group>"; };
|
||||
FDC4381427B329CE00C60D73 /* NonceGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonceGenerator.swift; sourceTree = "<group>"; };
|
||||
FDC4381627B32EC700C60D73 /* Personalization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Personalization.swift; sourceTree = "<group>"; };
|
||||
|
@ -1980,12 +1992,10 @@
|
|||
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 /* TestStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestStorage.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>"; };
|
||||
FDC438AB27BB145200C60D73 /* UserDeleteMessagesRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDeleteMessagesRequest.swift; sourceTree = "<group>"; };
|
||||
FDC438AD27BB148700C60D73 /* UserDeleteMessagesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDeleteMessagesResponse.swift; sourceTree = "<group>"; };
|
||||
FDC438B027BB159600C60D73 /* RequestInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestInfo.swift; sourceTree = "<group>"; };
|
||||
FDC438B227BB15B400C60D73 /* ResponseInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseInfo.swift; sourceTree = "<group>"; };
|
||||
FDC438B427BB15D400C60D73 /* Destination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Destination.swift; sourceTree = "<group>"; };
|
||||
|
@ -3876,7 +3886,9 @@
|
|||
FD83B9BC27CF2215005E1583 /* SharedTest */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC290A527D860CE005DAE71 /* Mock.swift */,
|
||||
FD83B9BD27CF2243005E1583 /* TestConstants.swift */,
|
||||
FDC290A727D9B46D005DAE71 /* NimbleExtensions.swift */,
|
||||
);
|
||||
path = SharedTest;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3956,8 +3968,6 @@
|
|||
FDC438A327BB107F00C60D73 /* UserBanRequest.swift */,
|
||||
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */,
|
||||
FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */,
|
||||
FDC438AB27BB145200C60D73 /* UserDeleteMessagesRequest.swift */,
|
||||
FDC438AD27BB148700C60D73 /* UserDeleteMessagesResponse.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4013,6 +4023,7 @@
|
|||
FD83B9C127CF33EE005E1583 /* Models */,
|
||||
FDC2909227D710A9005DAE71 /* Types */,
|
||||
FDC4389927BA002500C60D73 /* OpenGroupAPISpec.swift */,
|
||||
FDC2909D27D85751005DAE71 /* OpenGroupManagerSpec.swift */,
|
||||
);
|
||||
path = "Open Groups";
|
||||
sourceTree = "<group>";
|
||||
|
@ -4021,13 +4032,16 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC438BC27BB2AB400C60D73 /* Mockable.swift */,
|
||||
FDC4389C27BA01F000C60D73 /* TestStorage.swift */,
|
||||
FD859EF327C2F49200510D0C /* TestSodium.swift */,
|
||||
FD859EF527C2F52C00510D0C /* TestSign.swift */,
|
||||
FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */,
|
||||
FD859EF927C2F5C500510D0C /* TestGenericHash.swift */,
|
||||
FD859EFB27C2F60700510D0C /* TestEd25519.swift */,
|
||||
FDC4389C27BA01F000C60D73 /* MockStorage.swift */,
|
||||
FD859EF327C2F49200510D0C /* MockSodium.swift */,
|
||||
FD859EF527C2F52C00510D0C /* MockSign.swift */,
|
||||
FD859EF727C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift */,
|
||||
FD859EF927C2F5C500510D0C /* MockGenericHash.swift */,
|
||||
FD859EFB27C2F60700510D0C /* MockEd25519.swift */,
|
||||
FD83B9D127D59495005E1583 /* TestUserDefaults.swift */,
|
||||
FDC2909F27D85826005DAE71 /* TestThread.swift */,
|
||||
FDC290A127D85890005DAE71 /* TestInteraction.swift */,
|
||||
FDC290AB27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift */,
|
||||
);
|
||||
path = _TestUtilities;
|
||||
sourceTree = "<group>";
|
||||
|
@ -5285,7 +5299,6 @@
|
|||
B8B32021258B1A650020074B /* Contact.swift in Sources */,
|
||||
C32C5C89256DD0D2003C73A2 /* Storage+Jobs.swift in Sources */,
|
||||
C300A5FC2554B0A000555489 /* MessageReceiver.swift in Sources */,
|
||||
FDC438AC27BB145200C60D73 /* UserDeleteMessagesRequest.swift in Sources */,
|
||||
7B1581E2271E743B00848B49 /* OWSSounds.swift in Sources */,
|
||||
FDC438A627BB113A00C60D73 /* UserUnbanRequest.swift in Sources */,
|
||||
FDC4383E27B4708600C60D73 /* Atomic.swift in Sources */,
|
||||
|
@ -5393,7 +5406,6 @@
|
|||
FDC438C327BB512200C60D73 /* SodiumProtocols.swift in Sources */,
|
||||
B8856D11256F112A001CE70E /* OWSAudioSession.swift in Sources */,
|
||||
C3DB66C3260ACCE6001EFC55 /* OpenGroupPoller.swift in Sources */,
|
||||
FDC438AE27BB148700C60D73 /* UserDeleteMessagesResponse.swift in Sources */,
|
||||
C3C2A75F2553A3C500C340D1 /* VisibleMessage+LinkPreview.swift in Sources */,
|
||||
C32C5FBB256E0206003C73A2 /* OWSBackgroundTask.m in Sources */,
|
||||
B8856CA8256F0F42001CE70E /* OWSBackupFragment.m in Sources */,
|
||||
|
@ -5611,8 +5623,11 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FDC290AD27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift in Sources */,
|
||||
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */,
|
||||
FD83B9BB27CF20AF005E1583 /* SessionIdSpec.swift in Sources */,
|
||||
FDC290A927D9B46D005DAE71 /* NimbleExtensions.swift in Sources */,
|
||||
FDC290AA27D9B6FD005DAE71 /* Mock.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -5620,7 +5635,8 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */,
|
||||
FDC290AC27DB0B1C005DAE71 /* BoxKeyPair+Mocked.swift in Sources */,
|
||||
FD859EFA27C2F5C500510D0C /* MockGenericHash.swift in Sources */,
|
||||
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */,
|
||||
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */,
|
||||
FDC2909627D71252005DAE71 /* SOGSErrorSpec.swift in Sources */,
|
||||
|
@ -5629,20 +5645,25 @@
|
|||
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */,
|
||||
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */,
|
||||
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */,
|
||||
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */,
|
||||
FDC290A027D85826005DAE71 /* TestThread.swift in Sources */,
|
||||
FD859EF827C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift in Sources */,
|
||||
FDC2909827D7129B005DAE71 /* PersonalizationSpec.swift in Sources */,
|
||||
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */,
|
||||
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */,
|
||||
FD859EF427C2F49200510D0C /* MockSodium.swift in Sources */,
|
||||
FD859EFC27C2F60700510D0C /* MockEd25519.swift in Sources */,
|
||||
FDC290A627D860CE005DAE71 /* Mock.swift in Sources */,
|
||||
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */,
|
||||
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */,
|
||||
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */,
|
||||
FDC2908B27D707F3005DAE71 /* SendMessageRequestSpec.swift in Sources */,
|
||||
FDC290A827D9B46D005DAE71 /* NimbleExtensions.swift in Sources */,
|
||||
FDC2908F27D70938005DAE71 /* SendDirectMessageRequestSpec.swift in Sources */,
|
||||
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
|
||||
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */,
|
||||
FD859EF627C2F52C00510D0C /* MockSign.swift in Sources */,
|
||||
FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */,
|
||||
FDC2909E27D85751005DAE71 /* OpenGroupManagerSpec.swift in Sources */,
|
||||
FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */,
|
||||
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
|
||||
FDC290A227D85890005DAE71 /* TestInteraction.swift in Sources */,
|
||||
FDC4389D27BA01F000C60D73 /* MockStorage.swift in Sources */,
|
||||
FD83B9D227D59495005E1583 /* TestUserDefaults.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -58,6 +58,10 @@ extension Storage {
|
|||
public func setOpenGroupServer(_ server: OpenGroupAPI.Server, using transaction: Any) {
|
||||
(transaction as! YapDatabaseReadWriteTransaction).setObject(server, forKey: "SOGS.\(server.name)", inCollection: Storage.openGroupCollection)
|
||||
}
|
||||
|
||||
public func removeOpenGroupServer(name: String, using transaction: Any) {
|
||||
(transaction as! YapDatabaseReadWriteTransaction).removeObject(forKey: "SOGS.\(name)", inCollection: Storage.openGroupCollection)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension OpenGroupAPI {
|
||||
struct UserDeleteMessagesRequest: Codable {
|
||||
let rooms: [String]?
|
||||
let global: Bool?
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension OpenGroupAPI {
|
||||
public struct UserDeleteMessagesResponse: Codable, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case messagesDeleted = "messages_deleted"
|
||||
}
|
||||
|
||||
let id: String
|
||||
let messagesDeleted: Int64
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ public protocol AeadXChaCha20Poly1305IetfType {
|
|||
}
|
||||
|
||||
public protocol Ed25519Type {
|
||||
static func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool
|
||||
func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool
|
||||
}
|
||||
|
||||
public protocol SignType {
|
||||
|
@ -80,4 +80,9 @@ extension Sodium: SodiumType {
|
|||
extension Aead.XChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {}
|
||||
extension Sign: SignType {}
|
||||
extension GenericHash: GenericHashType {}
|
||||
extension Ed25519: Ed25519Type {}
|
||||
|
||||
struct Ed25519Wrapper: Ed25519Type {
|
||||
func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool {
|
||||
return try Ed25519.verifySignature(signature, publicKey: publicKey, data: data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,17 +60,20 @@ public protocol SessionMessagingKitStorageProtocol {
|
|||
|
||||
func getOpenGroup(for threadID: String) -> OpenGroup?
|
||||
func setOpenGroup(_ openGroup: OpenGroup, for threadID: String, using transaction: Any)
|
||||
func removeOpenGroup(for threadID: String, using transaction: Any)
|
||||
|
||||
func getUserCount(forOpenGroupWithID openGroupID: String) -> UInt64?
|
||||
func setUserCount(to newValue: UInt64, forOpenGroupWithID openGroupID: String, using transaction: Any)
|
||||
|
||||
func getOpenGroupServer(name: String) -> OpenGroupAPI.Server?
|
||||
func setOpenGroupServer(_ server: OpenGroupAPI.Server, using transaction: Any)
|
||||
func removeOpenGroupServer(name: String, using transaction: Any)
|
||||
|
||||
// MARK: - -- Open Group Public Keys
|
||||
|
||||
func getOpenGroupPublicKey(for server: String) -> String?
|
||||
func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any)
|
||||
func removeOpenGroupPublicKey(for server: String, using transaction: Any)
|
||||
|
||||
// MARK: - -- Open Group Sequence Number
|
||||
|
||||
|
@ -96,6 +99,7 @@ public protocol SessionMessagingKitStorageProtocol {
|
|||
func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread]
|
||||
|
||||
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64]
|
||||
func removeReceivedMessageTimestamps(_ timestamps: Set<UInt64>, using transaction: Any)
|
||||
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any)
|
||||
/// Returns the ID of the thread.
|
||||
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String?
|
||||
|
|
|
@ -44,9 +44,9 @@ public class Dependencies {
|
|||
set { _genericHash = newValue }
|
||||
}
|
||||
|
||||
private var _ed25519: Ed25519Type.Type?
|
||||
public var ed25519: Ed25519Type.Type {
|
||||
get { getValueSettingIfNull(&_ed25519) { Ed25519.self } }
|
||||
private var _ed25519: Ed25519Type?
|
||||
public var ed25519: Ed25519Type {
|
||||
get { getValueSettingIfNull(&_ed25519) { Ed25519Wrapper() } }
|
||||
set { _ed25519 = newValue }
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class Dependencies {
|
|||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
ed25519: Ed25519Type.Type? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
standardUserDefaults: UserDefaultsType? = nil,
|
||||
|
@ -111,7 +111,7 @@ public class Dependencies {
|
|||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
ed25519: Ed25519Type.Type? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
standardUserDefaults: UserDefaultsType? = nil,
|
||||
|
|
|
@ -15,7 +15,8 @@ class SOGSMessageSpec: QuickSpec {
|
|||
var messageJson: String!
|
||||
var messageData: Data!
|
||||
var decoder: JSONDecoder!
|
||||
var testSign: TestSign!
|
||||
var mockSign: MockSign!
|
||||
var mockEd25519: MockEd25519!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
beforeEach {
|
||||
|
@ -29,19 +30,24 @@ class SOGSMessageSpec: QuickSpec {
|
|||
"whisper_mods": false,
|
||||
|
||||
"data": "VGVzdERhdGE=",
|
||||
"signature": "VGVzdERhdGE="
|
||||
"signature": "VGVzdFNpZ25hdHVyZQ=="
|
||||
}
|
||||
"""
|
||||
messageData = messageJson.data(using: .utf8)!
|
||||
testSign = TestSign()
|
||||
mockSign = MockSign()
|
||||
mockEd25519 = MockEd25519()
|
||||
dependencies = Dependencies(
|
||||
sign: testSign,
|
||||
ed25519: TestEd25519.self
|
||||
sign: mockSign,
|
||||
ed25519: mockEd25519
|
||||
)
|
||||
decoder = JSONDecoder()
|
||||
decoder.userInfo = [ Dependencies.userInfoKey: dependencies as Any ]
|
||||
}
|
||||
|
||||
afterEach {
|
||||
mockSign = nil
|
||||
}
|
||||
|
||||
context("when decoding") {
|
||||
it("defaults the whisper values to false") {
|
||||
messageJson = """
|
||||
|
@ -91,7 +97,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
"whisper_mods": false,
|
||||
|
||||
"data": "VGVzdERhdGE=",
|
||||
"signature": "VGVzdERhdGE="
|
||||
"signature": "VGVzdFNpZ25hdHVyZQ=="
|
||||
}
|
||||
"""
|
||||
messageData = messageJson.data(using: .utf8)!
|
||||
|
@ -113,7 +119,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
"whisper_mods": false,
|
||||
|
||||
"data": "Test!!!",
|
||||
"signature": "VGVzdERhdGE="
|
||||
"signature": "VGVzdFNpZ25hdHVyZQ=="
|
||||
}
|
||||
"""
|
||||
messageData = messageJson.data(using: .utf8)!
|
||||
|
@ -166,7 +172,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
"whisper_mods": false,
|
||||
|
||||
"data": "VGVzdERhdGE=",
|
||||
"signature": "VGVzdERhdGE="
|
||||
"signature": "VGVzdFNpZ25hdHVyZQ=="
|
||||
}
|
||||
"""
|
||||
messageData = messageJson.data(using: .utf8)!
|
||||
|
@ -190,14 +196,14 @@ class SOGSMessageSpec: QuickSpec {
|
|||
"whisper_mods": false,
|
||||
|
||||
"data": "VGVzdERhdGE=",
|
||||
"signature": "VGVzdERhdGE="
|
||||
"signature": "VGVzdFNpZ25hdHVyZQ=="
|
||||
}
|
||||
"""
|
||||
messageData = messageJson.data(using: .utf8)!
|
||||
}
|
||||
|
||||
it("succeeds if it succeeds verification") {
|
||||
testSign.mockData[.verify] = true
|
||||
mockSign.when { $0.verify(message: any(), publicKey: any(), signature: any()) }.thenReturn(true)
|
||||
|
||||
expect {
|
||||
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
@ -205,8 +211,23 @@ class SOGSMessageSpec: QuickSpec {
|
|||
.toNot(beNil())
|
||||
}
|
||||
|
||||
it("provides the correct values as parameters") {
|
||||
mockSign.when { $0.verify(message: any(), publicKey: any(), signature: any()) }.thenReturn(true)
|
||||
|
||||
_ = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
||||
expect(mockSign)
|
||||
.to(call(matchingParameters: true) {
|
||||
$0.verify(
|
||||
message: Data(base64Encoded: "VGVzdERhdGE=")!.bytes,
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
signature: Data(base64Encoded: "VGVzdFNpZ25hdHVyZQ==")!.bytes
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
it("throws if it fails verification") {
|
||||
testSign.mockData[.verify] = false
|
||||
mockSign.when { $0.verify(message: any(), publicKey: any(), signature: any()) }.thenReturn(false)
|
||||
|
||||
expect {
|
||||
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
@ -217,13 +238,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
|
||||
context("that is unblinded") {
|
||||
it("succeeds if it succeeds verification") {
|
||||
TestEd25519.mockData[
|
||||
.verifySignature(
|
||||
signature: Data(base64Encoded: "VGVzdERhdGE=")!,
|
||||
publicKey: Data(hex: TestConstants.publicKey),
|
||||
data: Data(base64Encoded: "VGVzdERhdGE=")!
|
||||
)
|
||||
] = true
|
||||
mockEd25519.when { try $0.verifySignature(any(), publicKey: any(), data: any()) }.thenReturn(true)
|
||||
|
||||
expect {
|
||||
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
@ -231,14 +246,23 @@ class SOGSMessageSpec: QuickSpec {
|
|||
.toNot(beNil())
|
||||
}
|
||||
|
||||
it("provides the correct values as parameters") {
|
||||
mockEd25519.when { try $0.verifySignature(any(), publicKey: any(), data: any()) }.thenReturn(true)
|
||||
|
||||
_ = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
||||
expect(mockEd25519)
|
||||
.to(call(matchingParameters: true) {
|
||||
try $0.verifySignature(
|
||||
Data(base64Encoded: "VGVzdFNpZ25hdHVyZQ==")!,
|
||||
publicKey: Data(hex: TestConstants.publicKey),
|
||||
data: Data(base64Encoded: "VGVzdERhdGE=")!
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
it("throws if it fails verification") {
|
||||
TestEd25519.mockData[
|
||||
.verifySignature(
|
||||
signature: Data(base64Encoded: "VGVzdERhdGE=")!,
|
||||
publicKey: Data(hex: TestConstants.publicKey),
|
||||
data: Data(base64Encoded: "VGVzdERhdGE=")!
|
||||
)
|
||||
] = false
|
||||
mockEd25519.when { try $0.verifySignature(any(), publicKey: any(), data: any()) }.thenReturn(false)
|
||||
|
||||
expect {
|
||||
try decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
|
|
@ -78,11 +78,11 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
var testStorage: TestStorage!
|
||||
var testSodium: TestSodium!
|
||||
var testAeadXChaCha20Poly1305Ietf: TestAeadXChaCha20Poly1305Ietf!
|
||||
var testGenericHash: TestGenericHash!
|
||||
var testSign: TestSign!
|
||||
var mockStorage: MockStorage!
|
||||
var mockSodium: MockSodium!
|
||||
var mockAeadXChaCha20Poly1305Ietf: MockAeadXChaCha20Poly1305Ietf!
|
||||
var mockGenericHash: MockGenericHash!
|
||||
var mockSign: MockSign!
|
||||
var testUserDefaults: TestUserDefaults!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
|
@ -94,64 +94,110 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
// MARK: - Configuration
|
||||
|
||||
beforeEach {
|
||||
testStorage = TestStorage()
|
||||
testSodium = TestSodium()
|
||||
testAeadXChaCha20Poly1305Ietf = TestAeadXChaCha20Poly1305Ietf()
|
||||
testGenericHash = TestGenericHash()
|
||||
testSign = TestSign()
|
||||
mockStorage = MockStorage()
|
||||
mockSodium = MockSodium()
|
||||
mockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
|
||||
mockGenericHash = MockGenericHash()
|
||||
mockSign = MockSign()
|
||||
testUserDefaults = TestUserDefaults()
|
||||
dependencies = Dependencies(
|
||||
api: TestApi.self,
|
||||
storage: testStorage,
|
||||
sodium: testSodium,
|
||||
aeadXChaCha20Poly1305Ietf: testAeadXChaCha20Poly1305Ietf,
|
||||
sign: testSign,
|
||||
genericHash: testGenericHash,
|
||||
ed25519: TestEd25519.self,
|
||||
storage: mockStorage,
|
||||
sodium: mockSodium,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
sign: mockSign,
|
||||
genericHash: mockGenericHash,
|
||||
ed25519: MockEd25519(),
|
||||
nonceGenerator16: TestNonce16Generator(),
|
||||
nonceGenerator24: TestNonce24Generator(),
|
||||
standardUserDefaults: testUserDefaults,
|
||||
date: Date(timeIntervalSince1970: 1234567890)
|
||||
)
|
||||
|
||||
testStorage.mockData[.allOpenGroups] = [
|
||||
"0": OpenGroup(
|
||||
server: "testServer",
|
||||
room: "testRoom",
|
||||
publicKey: TestConstants.publicKey,
|
||||
name: "Test",
|
||||
groupDescription: nil,
|
||||
imageID: nil,
|
||||
infoUpdates: 0
|
||||
mockStorage
|
||||
.when { $0.write(with: { _ in }) }
|
||||
.then { args in (args.first as? ((Any) -> Void))?(any()) }
|
||||
.thenReturn(Promise.value(()))
|
||||
mockStorage
|
||||
.when { $0.write(with: { _ in }, completion: { }) }
|
||||
.then { args in
|
||||
(args.first as? ((Any) -> Void))?(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)!
|
||||
)
|
||||
)
|
||||
]
|
||||
testStorage.mockData[.openGroupPublicKeys] = [
|
||||
"testServer": TestConstants.publicKey
|
||||
]
|
||||
testStorage.mockData[.userKeyPair] = try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
)
|
||||
testStorage.mockData[.userEdKeyPair] = Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
)
|
||||
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: any()) }.thenReturn(())
|
||||
|
||||
testGenericHash.mockData[.hashOutputLength] = []
|
||||
testSodium.mockData[.blindedKeyPair] = Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
)
|
||||
testSodium.mockData[.sogsSignature] = "TestSogsSignature".bytes
|
||||
testSign.mockData[.signature] = "TestSignature".bytes
|
||||
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 {
|
||||
testStorage = nil
|
||||
testSodium = nil
|
||||
testAeadXChaCha20Poly1305Ietf = nil
|
||||
testGenericHash = nil
|
||||
testSign = nil
|
||||
mockStorage = nil
|
||||
mockSodium = nil
|
||||
mockAeadXChaCha20Poly1305Ietf = nil
|
||||
mockGenericHash = nil
|
||||
mockSign = nil
|
||||
testUserDefaults = nil
|
||||
dependencies = nil
|
||||
|
||||
|
@ -280,7 +326,7 @@ 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") {
|
||||
testStorage.mockData[.openGroupSequenceNumber] = ["testServer.testRoom": Int64(123)]
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
|
||||
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
|
@ -303,7 +349,7 @@ 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") {
|
||||
testStorage.mockData[.openGroupSequenceNumber] = ["testServer.testRoom": Int64(123)]
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
|
||||
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
|
@ -326,7 +372,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("retrieves recent messages if there was a last message and there has already been a poll this session") {
|
||||
testStorage.mockData[.openGroupSequenceNumber] = ["testServer.testRoom": Int64(123)]
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
|
||||
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
|
@ -370,7 +416,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("retrieves inbox messages since the last message if there was one") {
|
||||
testStorage.mockData[.openGroupInboxLatestMessageId] = ["testServer": Int64(124)]
|
||||
mockStorage.when { $0.getOpenGroupInboxLatestMessageId(for: any()) }.thenReturn(124)
|
||||
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
|
@ -414,7 +460,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("retrieves outbox messages since the last message if there was one") {
|
||||
testStorage.mockData[.openGroupOutboxLatestMessageId] = ["testServer": Int64(125)]
|
||||
mockStorage.when { $0.getOpenGroupOutboxLatestMessageId(for: any()) }.thenReturn(125)
|
||||
|
||||
OpenGroupAPI
|
||||
.poll(
|
||||
|
@ -439,7 +485,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("and given an invalid response") {
|
||||
it("does not update the poll state") {
|
||||
testStorage.mockData[.openGroupSequenceNumber] = ["testServer.testRoom": Int64(123)]
|
||||
mockStorage.when { $0.getOpenGroupSequenceNumber(for: any(), on: any()) }.thenReturn(123)
|
||||
|
||||
OpenGroupAPI.poll("testServer", hasPerformedInitialPoll: false, timeSinceLastPoll: 0, using: dependencies)
|
||||
.get { result in pollResponse = result }
|
||||
|
@ -1028,7 +1074,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
messageData = LocalTestApi.data
|
||||
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||
|
||||
testStorage.mockData[.userEdKeyPair] = Box.KeyPair(publicKey: [], secretKey: [])
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(Box.KeyPair(publicKey: [], secretKey: []))
|
||||
}
|
||||
|
||||
afterEach {
|
||||
|
@ -1092,15 +1138,22 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
expect(testStorage.mockData[.receivedMessageTimestamp] as? UInt64).to(equal(321000))
|
||||
expect(mockStorage)
|
||||
.to(call(matchingParameters: true) {
|
||||
$0.addReceivedMessageTimestamp(321000, using: any())
|
||||
})
|
||||
}
|
||||
|
||||
context("when unblinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs the message correctly") {
|
||||
|
@ -1136,7 +1189,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails to sign if there is no public key") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil)
|
||||
|
||||
var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)?
|
||||
|
||||
|
@ -1166,10 +1219,14 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("when blinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs the message correctly") {
|
||||
|
@ -1205,7 +1262,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails to sign if there is no public key") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil)
|
||||
|
||||
var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.Message)?
|
||||
|
||||
|
@ -1286,7 +1343,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||
|
||||
testStorage.mockData[.userEdKeyPair] = Box.KeyPair(publicKey: [], secretKey: [])
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(Box.KeyPair(publicKey: [], secretKey: []))
|
||||
}
|
||||
|
||||
it("correctly sends the update") {
|
||||
|
@ -1321,10 +1378,14 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("when unblinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs the message correctly") {
|
||||
|
@ -1359,7 +1420,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails to sign if there is no public key") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil)
|
||||
|
||||
var response: (info: OnionRequestResponseInfoType, data: Data?)?
|
||||
|
||||
|
@ -1388,10 +1449,14 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("when blinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs the message correctly") {
|
||||
|
@ -1426,7 +1491,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails to sign if there is no public key") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil)
|
||||
|
||||
var response: (info: OnionRequestResponseInfoType, data: Data?)?
|
||||
|
||||
|
@ -1483,6 +1548,47 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
}
|
||||
|
||||
context("when deleting all messages for a user") {
|
||||
var response: (info: OnionRequestResponseInfoType, data: Data?)?
|
||||
|
||||
beforeEach {
|
||||
class LocalTestApi: TestApi {
|
||||
override class var mockResponse: Data? { return Data() }
|
||||
}
|
||||
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||
}
|
||||
|
||||
afterEach {
|
||||
response = nil
|
||||
}
|
||||
|
||||
it("generates the request and handles the response correctly") {
|
||||
OpenGroupAPI
|
||||
.messagesDeleteAll(
|
||||
"testUserId",
|
||||
in: "testRoom",
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
.get { result in response = result }
|
||||
.catch { requestError in error = requestError }
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(response)
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
|
||||
// Validate request data
|
||||
let requestData: TestApi.RequestData? = (response?.info as? TestResponseInfo)?.requestData
|
||||
expect(requestData?.httpMethod).to(equal("DELETE"))
|
||||
expect(requestData?.server).to(equal("testServer"))
|
||||
expect(requestData?.urlString).to(equal("testServer/room/testRoom/all/testUserId"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Pinning
|
||||
|
||||
context("when pinning a message") {
|
||||
|
@ -1759,7 +1865,10 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
expect(testStorage.mockData[.receivedMessageTimestamp] as? UInt64).to(equal(321000))
|
||||
expect(mockStorage)
|
||||
.to(call(matchingParameters: true) {
|
||||
$0.addReceivedMessageTimestamp(321000, using: any())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2086,121 +2195,11 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
}
|
||||
|
||||
context("when deleting a users messages") {
|
||||
var response: (info: OnionRequestResponseInfoType, data: OpenGroupAPI.UserDeleteMessagesResponse)?
|
||||
var messageData: OpenGroupAPI.UserDeleteMessagesResponse!
|
||||
|
||||
beforeEach {
|
||||
class LocalTestApi: TestApi {
|
||||
static let data: OpenGroupAPI.UserDeleteMessagesResponse = OpenGroupAPI.UserDeleteMessagesResponse(
|
||||
id: "testId",
|
||||
messagesDeleted: 10
|
||||
)
|
||||
|
||||
override class var mockResponse: Data? { return try! JSONEncoder().encode(data) }
|
||||
}
|
||||
messageData = LocalTestApi.data
|
||||
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||
}
|
||||
|
||||
afterEach {
|
||||
response = nil
|
||||
}
|
||||
|
||||
it("generates the request and handles the response correctly") {
|
||||
OpenGroupAPI
|
||||
.userDeleteMessages(
|
||||
"testUserId",
|
||||
from: nil,
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
.get { result in response = result }
|
||||
.catch { requestError in error = requestError }
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(response)
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
|
||||
// Validate the response data
|
||||
expect(response?.data).to(equal(messageData))
|
||||
|
||||
// Validate request data
|
||||
let requestData: TestApi.RequestData? = (response?.info as? TestResponseInfo)?.requestData
|
||||
expect(requestData?.httpMethod).to(equal("POST"))
|
||||
expect(requestData?.server).to(equal("testServer"))
|
||||
expect(requestData?.urlString).to(equal("testServer/user/testUserId/deleteMessages"))
|
||||
}
|
||||
|
||||
it("does a global delete if no room tokens are provided") {
|
||||
OpenGroupAPI
|
||||
.userDeleteMessages(
|
||||
"testUserId",
|
||||
from: nil,
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
.get { result in response = result }
|
||||
.catch { requestError in error = requestError }
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(response)
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
|
||||
// Validate request data
|
||||
let requestData: TestApi.RequestData? = (response?.info as? TestResponseInfo)?.requestData
|
||||
let requestBody: OpenGroupAPI.UserDeleteMessagesRequest = try! JSONDecoder().decode(OpenGroupAPI.UserDeleteMessagesRequest.self, from: requestData!.body!)
|
||||
|
||||
expect(requestBody.global).to(beTrue())
|
||||
expect(requestBody.rooms).to(beNil())
|
||||
}
|
||||
|
||||
it("does room specific bans if room tokens are provided") {
|
||||
OpenGroupAPI
|
||||
.userDeleteMessages(
|
||||
"testUserId",
|
||||
from: ["testRoom"],
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
.get { result in response = result }
|
||||
.catch { requestError in error = requestError }
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(response)
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
|
||||
// Validate request data
|
||||
let requestData: TestApi.RequestData? = (response?.info as? TestResponseInfo)?.requestData
|
||||
let requestBody: OpenGroupAPI.UserDeleteMessagesRequest = try! JSONDecoder().decode(OpenGroupAPI.UserDeleteMessagesRequest.self, from: requestData!.body!)
|
||||
|
||||
expect(requestBody.global).to(beNil())
|
||||
expect(requestBody.rooms).to(equal(["testRoom"]))
|
||||
}
|
||||
}
|
||||
|
||||
context("when banning and deleting all messages for a user") {
|
||||
var response: [OnionRequestResponseInfoType]?
|
||||
|
||||
beforeEach {
|
||||
class LocalTestApi: TestApi {
|
||||
static let deleteMessagesData: OpenGroupAPI.UserDeleteMessagesResponse = OpenGroupAPI.UserDeleteMessagesResponse(
|
||||
id: "123",
|
||||
messagesDeleted: 10
|
||||
)
|
||||
|
||||
override class var mockResponse: Data? {
|
||||
let responses: [Data] = [
|
||||
try! JSONEncoder().encode(
|
||||
|
@ -2212,10 +2211,10 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
)
|
||||
),
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
OpenGroupAPI.BatchSubResponse<NoResponse>(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: deleteMessagesData,
|
||||
body: nil,
|
||||
failedToParseBody: false
|
||||
)
|
||||
)
|
||||
|
@ -2233,9 +2232,9 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
it("generates the request and handles the response correctly") {
|
||||
OpenGroupAPI
|
||||
.userBanAndDeleteAllMessage(
|
||||
.userBanAndDeleteAllMessages(
|
||||
"testUserId",
|
||||
from: nil,
|
||||
in: "testRoom",
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -2257,11 +2256,11 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
expect(requestData?.urlString).to(equal("testServer/sequence"))
|
||||
}
|
||||
|
||||
it("does a global ban and delete if no room tokens are provided") {
|
||||
it("bans the user from the specified room rather than globally") {
|
||||
OpenGroupAPI
|
||||
.userBanAndDeleteAllMessage(
|
||||
.userBanAndDeleteAllMessages(
|
||||
"testUserId",
|
||||
from: nil,
|
||||
in: "testRoom",
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -2282,59 +2281,13 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
with: requestData!.body!,
|
||||
options: [.fragmentsAllowed]
|
||||
)
|
||||
let anyArray: [Any] = jsonObject as! [Any]
|
||||
let dataArray: [Data] = anyArray.compactMap {
|
||||
try! JSONSerialization.data(withJSONObject: ($0 as! [String: Any])["json"]!)
|
||||
}
|
||||
let firstJsonObject: Any = ((jsonObject as! [Any]).first as! [String: Any])["json"]!
|
||||
let firstJsonData: Data = try! JSONSerialization.data(withJSONObject: firstJsonObject)
|
||||
let firstRequestBody: OpenGroupAPI.UserBanRequest = try! JSONDecoder()
|
||||
.decode(OpenGroupAPI.UserBanRequest.self, from: dataArray.first!)
|
||||
let lastRequestBody: OpenGroupAPI.UserDeleteMessagesRequest = try! JSONDecoder()
|
||||
.decode(OpenGroupAPI.UserDeleteMessagesRequest.self, from: dataArray.last!)
|
||||
|
||||
expect(firstRequestBody.global).to(beTrue())
|
||||
expect(firstRequestBody.rooms).to(beNil())
|
||||
expect(lastRequestBody.global).to(beTrue())
|
||||
expect(lastRequestBody.rooms).to(beNil())
|
||||
}
|
||||
|
||||
it("does room specific bans and deletes if room tokens are provided") {
|
||||
OpenGroupAPI
|
||||
.userBanAndDeleteAllMessage(
|
||||
"testUserId",
|
||||
from: ["testRoom"],
|
||||
on: "testServer",
|
||||
using: dependencies
|
||||
)
|
||||
.get { result in response = result }
|
||||
.catch { requestError in error = requestError }
|
||||
.retainUntilComplete()
|
||||
|
||||
expect(response)
|
||||
.toEventuallyNot(
|
||||
beNil(),
|
||||
timeout: .milliseconds(100)
|
||||
)
|
||||
expect(error?.localizedDescription).to(beNil())
|
||||
|
||||
// Validate request data
|
||||
let requestData: TestApi.RequestData? = (response?.first as? TestResponseInfo)?.requestData
|
||||
let jsonObject: Any = try! JSONSerialization.jsonObject(
|
||||
with: requestData!.body!,
|
||||
options: [.fragmentsAllowed]
|
||||
)
|
||||
let anyArray: [Any] = jsonObject as! [Any]
|
||||
let dataArray: [Data] = anyArray.compactMap {
|
||||
try! JSONSerialization.data(withJSONObject: ($0 as! [String: Any])["json"]!)
|
||||
}
|
||||
let firstRequestBody: OpenGroupAPI.UserBanRequest = try! JSONDecoder()
|
||||
.decode(OpenGroupAPI.UserBanRequest.self, from: dataArray.first!)
|
||||
let lastRequestBody: OpenGroupAPI.UserDeleteMessagesRequest = try! JSONDecoder()
|
||||
.decode(OpenGroupAPI.UserDeleteMessagesRequest.self, from: dataArray.last!)
|
||||
.decode(OpenGroupAPI.UserBanRequest.self, from: firstJsonData)
|
||||
|
||||
expect(firstRequestBody.global).to(beNil())
|
||||
expect(firstRequestBody.rooms).to(equal(["testRoom"]))
|
||||
expect(lastRequestBody.global).to(beNil())
|
||||
expect(lastRequestBody.rooms).to(equal(["testRoom"]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2352,7 +2305,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when there is no userEdKeyPair") {
|
||||
testStorage.mockData[.userEdKeyPair] = nil
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil)
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
@ -2369,7 +2322,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when there is no serverPublicKey") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn(nil)
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
@ -2386,7 +2339,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when the serverPublicKey is not a hex string") {
|
||||
testStorage.mockData[.openGroupPublicKeys] = ["testServer": "TestString!!!"]
|
||||
mockStorage.when { $0.getOpenGroupPublicKey(for: any()) }.thenReturn("TestString!!!")
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
@ -2404,10 +2357,14 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("when unblinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs correctly") {
|
||||
|
@ -2438,7 +2395,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when the signature is not generated") {
|
||||
testSign.mockData[.signature] = nil
|
||||
mockSign.when { $0.signature(message: any(), secretKey: any()) }.thenReturn(nil)
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
@ -2457,10 +2414,14 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
|
||||
context("when blinded") {
|
||||
beforeEach {
|
||||
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
mockStorage
|
||||
.when { $0.getOpenGroupServer(name: any()) }
|
||||
.thenReturn(
|
||||
OpenGroupAPI.Server(
|
||||
name: "testServer",
|
||||
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blind], missing: [])
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
it("signs correctly") {
|
||||
|
@ -2490,7 +2451,9 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when the blindedKeyPair is not generated") {
|
||||
testSodium.mockData[.blindedKeyPair] = nil
|
||||
mockSodium
|
||||
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(nil)
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
@ -2507,7 +2470,9 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("fails when the sogsSignature is not generated") {
|
||||
testSodium.mockData[.sogsSignature] = nil
|
||||
mockSodium
|
||||
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(nil)
|
||||
|
||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||
.get { result in response = result }
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
|
@ -15,15 +15,24 @@ class SodiumProtocolsSpec: QuickSpec {
|
|||
let testValue: [UInt8] = [1, 2, 3]
|
||||
|
||||
it("provides the default values in it's extensions") {
|
||||
let testAead: TestAeadXChaCha20Poly1305Ietf = TestAeadXChaCha20Poly1305Ietf()
|
||||
testAead.mockData[.encrypt] = testValue
|
||||
testAead.mockData[.decrypt] = testValue
|
||||
let mockAead: MockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
|
||||
mockAead
|
||||
.when { $0.encrypt(message: any(), secretKey: any(), nonce: any(), additionalData: any()) }
|
||||
.thenReturn(testValue)
|
||||
mockAead
|
||||
.when { $0.decrypt(authenticatedCipherText: any(), secretKey: any(), nonce: any(), additionalData: any()) }
|
||||
.thenReturn(testValue)
|
||||
|
||||
expect(testAead.encrypt(message: [], secretKey: [], nonce: [])).to(equal(testValue))
|
||||
expect(testAead.encrypt(message: [], secretKey: [], nonce: [], additionalData: nil)).to(equal(testValue))
|
||||
expect(testAead.decrypt(authenticatedCipherText: [], secretKey: [], nonce: [])).to(equal(testValue))
|
||||
expect(testAead.decrypt(authenticatedCipherText: [], secretKey: [], nonce: [], additionalData: nil))
|
||||
.to(equal(testValue))
|
||||
_ = mockAead.encrypt(message: [], secretKey: [], nonce: [])
|
||||
_ = mockAead.decrypt(authenticatedCipherText: [], secretKey: [], nonce: [])
|
||||
|
||||
expect(mockAead)
|
||||
.to(call { $0.encrypt(message: any(), secretKey: any(), nonce: any(), additionalData: any()) })
|
||||
|
||||
expect(mockAead)
|
||||
.to(call {
|
||||
$0.decrypt(authenticatedCipherText: any(), secretKey: any(), nonce: any(), additionalData: any())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,16 +40,21 @@ class SodiumProtocolsSpec: QuickSpec {
|
|||
let testValue: [UInt8] = [1, 2, 3]
|
||||
|
||||
it("provides the default values in it's extensions") {
|
||||
let testGenericHash: TestGenericHash = TestGenericHash()
|
||||
testGenericHash.mockData[.hash] = testValue
|
||||
testGenericHash.mockData[.hashSaltPersonal] = testValue
|
||||
let mockGenericHash: MockGenericHash = MockGenericHash()
|
||||
mockGenericHash.when { $0.hash(message: any(), key: any()) }.thenReturn(testValue)
|
||||
mockGenericHash
|
||||
.when { $0.hashSaltPersonal(message: any(), outputLength: any(), key: any(), salt: any(), personal: any()) }
|
||||
.thenReturn(testValue)
|
||||
|
||||
expect(testGenericHash.hash(message: [])).to(equal(testValue))
|
||||
expect(testGenericHash.hash(message: [], key: nil)).to(equal(testValue))
|
||||
expect(testGenericHash.hashSaltPersonal(message: [], outputLength: 0, salt: [], personal: []))
|
||||
.to(equal(testValue))
|
||||
expect(testGenericHash.hashSaltPersonal(message: [], outputLength: 0, key: nil, salt: [], personal: []))
|
||||
.to(equal(testValue))
|
||||
_ = mockGenericHash.hash(message: [])
|
||||
_ = mockGenericHash.hashSaltPersonal(message: [], outputLength: 0, salt: [], personal: [])
|
||||
|
||||
expect(mockGenericHash)
|
||||
.to(call { $0.hash(message: any(), key: any()) })
|
||||
expect(mockGenericHash)
|
||||
.to(call {
|
||||
$0.hashSaltPersonal(message: any(), outputLength: any(), key: any(), salt: any(), personal: any())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Sodium
|
||||
|
||||
extension Box.KeyPair: Mocked {
|
||||
static var mockValue: Box.KeyPair = Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
)
|
||||
}
|
|
@ -6,28 +6,15 @@ import Sodium
|
|||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable {
|
||||
class MockAeadXChaCha20Poly1305Ietf: Mock<AeadXChaCha20Poly1305IetfType>, AeadXChaCha20Poly1305IetfType {
|
||||
var KeyBytes: Int = 32
|
||||
var ABytes: Int = 16
|
||||
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case encrypt
|
||||
case decrypt
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - SignType
|
||||
|
||||
func encrypt(message: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? {
|
||||
return (mockData[.encrypt] as? Bytes)
|
||||
return accept(args: [message, secretKey, nonce, additionalData]) as? Bytes
|
||||
}
|
||||
|
||||
func decrypt(authenticatedCipherText: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? {
|
||||
return (mockData[.decrypt] as? Bytes)
|
||||
return accept(args: [authenticatedCipherText, secretKey, nonce, additionalData]) as? Bytes
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockEd25519: Mock<Ed25519Type>, Ed25519Type {
|
||||
func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool {
|
||||
return accept(args: [signature, publicKey, data]) as! Bool
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockGenericHash: Mock<GenericHashType>, GenericHashType {
|
||||
func hash(message: Bytes, key: Bytes?) -> Bytes? {
|
||||
return accept(args: [message, key]) as? Bytes
|
||||
}
|
||||
|
||||
func hash(message: Bytes, outputLength: Int) -> Bytes? {
|
||||
return accept(args: [message, outputLength]) as? Bytes
|
||||
}
|
||||
|
||||
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes? {
|
||||
return accept(args: [message, outputLength, key, salt, personal]) as? Bytes
|
||||
}
|
||||
}
|
|
@ -6,32 +6,18 @@ import Sodium
|
|||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestSign: SignType, Mockable {
|
||||
class MockSign: Mock<SignType>, SignType {
|
||||
var PublicKeyBytes: Int = 32
|
||||
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case signature
|
||||
case verify
|
||||
case toX25519
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - SignType
|
||||
|
||||
func signature(message: Bytes, secretKey: Bytes) -> Bytes? {
|
||||
return (mockData[.signature] as? Bytes)
|
||||
return accept(args: [message, secretKey]) as? Bytes
|
||||
}
|
||||
|
||||
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool {
|
||||
return (mockData[.verify] as! Bool)
|
||||
return accept(args: [message, publicKey, signature]) as! Bool
|
||||
}
|
||||
|
||||
func toX25519(ed25519PublicKey: Bytes) -> Bytes? {
|
||||
return (mockData[.toX25519] as? Bytes)
|
||||
return accept(args: [ed25519PublicKey]) as? Bytes
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockSodium: Mock<SodiumType>, SodiumType {
|
||||
func getGenericHash() -> GenericHashType { return accept() as! GenericHashType }
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return accept() as! AeadXChaCha20Poly1305IetfType }
|
||||
func getSign() -> SignType { return accept() as! SignType }
|
||||
|
||||
func generateBlindingFactor(serverPublicKey: String) -> Bytes? {
|
||||
return accept(args: [serverPublicKey]) as? Bytes
|
||||
}
|
||||
|
||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||
return accept(args: [serverPublicKey, edKeyPair, genericHash]) as? Box.KeyPair
|
||||
}
|
||||
|
||||
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes? {
|
||||
return accept(args: [message, secretKey, ka, kA]) as? Bytes
|
||||
}
|
||||
|
||||
func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? {
|
||||
return accept(args: [lhsKeyBytes, rhsKeyBytes]) as? Bytes
|
||||
}
|
||||
|
||||
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
|
||||
return accept(args: [a, otherBlindedPublicKey, kA, kB, genericHash]) as? Bytes
|
||||
}
|
||||
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
|
||||
return accept(args: [sessionId, blindedSessionId, serverPublicKey]) as! Bool
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
// 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 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 getUserClosedGroupPublicKeys() -> Set<String> { return accept() as! Set<String> }
|
||||
func getZombieMembers(for groupPublicKey: String) -> Set<String> { return accept() 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() 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 updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set<String>, using transaction: Any) {
|
||||
accept(args: [messageIDs, transaction])
|
||||
}
|
||||
|
||||
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 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])
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestEd25519: Ed25519Type, StaticMockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case verifySignature(signature: Data, publicKey: Data, data: Data) // TODO: Test the uniqueness of this
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
static var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - SignType
|
||||
|
||||
static func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool {
|
||||
return (mockData[.verifySignature(signature: signature, publicKey: publicKey, data: data)] as! Bool)
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestGenericHash: GenericHashType, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case hash
|
||||
case hashOutputLength
|
||||
case hashSaltPersonal
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - SignType
|
||||
|
||||
func hash(message: Bytes, key: Bytes?) -> Bytes? {
|
||||
return (mockData[.hash] as? Bytes)
|
||||
}
|
||||
|
||||
func hash(message: Bytes, outputLength: Int) -> Bytes? {
|
||||
return (mockData[.hashOutputLength] as? Bytes)
|
||||
}
|
||||
|
||||
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes? {
|
||||
return (mockData[.hashSaltPersonal] as? Bytes)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
|
@ -1,59 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestSodium: SodiumType, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case genericHash
|
||||
case aeadXChaCha20Poly1305Ietf
|
||||
case sign
|
||||
case blindingFactor
|
||||
case blindedKeyPair
|
||||
case sogsSignature
|
||||
case combinedKeys
|
||||
case sharedBlindedEncryptionKey
|
||||
case sessionIdMatches
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - SodiumType
|
||||
|
||||
func getGenericHash() -> GenericHashType { return (mockData[.genericHash] as! GenericHashType) }
|
||||
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType {
|
||||
return (mockData[.aeadXChaCha20Poly1305Ietf] as! AeadXChaCha20Poly1305IetfType)
|
||||
}
|
||||
|
||||
func getSign() -> SignType { return (mockData[.sign] as! SignType) }
|
||||
|
||||
func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return (mockData[.blindingFactor] as? Bytes) }
|
||||
|
||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||
return (mockData[.blindedKeyPair] as? Box.KeyPair)
|
||||
}
|
||||
|
||||
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes? {
|
||||
return (mockData[.sogsSignature] as? Bytes)
|
||||
}
|
||||
|
||||
func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? {
|
||||
return (mockData[.combinedKeys] as? Bytes)
|
||||
}
|
||||
|
||||
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
|
||||
return (mockData[.sharedBlindedEncryptionKey] as? Bytes)
|
||||
}
|
||||
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
|
||||
return ((mockData[.sessionIdMatches] as? Bool) ?? false)
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
|
||||
// MARK: - Mockable
|
||||
|
||||
enum DataKey: Hashable {
|
||||
case allOpenGroups
|
||||
case openGroupPublicKeys
|
||||
case userKeyPair
|
||||
case userEdKeyPair
|
||||
case openGroup
|
||||
case openGroupServer
|
||||
case openGroupImage
|
||||
case openGroupUserCount
|
||||
case openGroupSequenceNumber
|
||||
case openGroupInboxLatestMessageId
|
||||
case openGroupOutboxLatestMessageId
|
||||
case receivedMessageTimestamp
|
||||
}
|
||||
|
||||
typealias Key = DataKey
|
||||
|
||||
var mockData: [DataKey: Any] = [:]
|
||||
|
||||
// MARK: - Shared
|
||||
|
||||
@discardableResult func write(with block: @escaping (Any) -> Void) -> Promise<Void> {
|
||||
block(()) // TODO: Pass Transaction type to prevent force-cast crashes throughout codebase
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
@discardableResult func write(with block: @escaping (Any) -> Void, completion: @escaping () -> Void) -> Promise<Void> {
|
||||
block(()) // TODO: Pass Transaction type to prevent force-cast crashes throughout codebase
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
func writeSync(with block: @escaping (Any) -> Void) {
|
||||
block(()) // TODO: Pass Transaction type to prevent force-cast crashes throughout codebase
|
||||
}
|
||||
|
||||
// MARK: - General
|
||||
|
||||
func getUserPublicKey() -> String? { return nil }
|
||||
func getUserKeyPair() -> ECKeyPair? { return (mockData[.userKeyPair] as? ECKeyPair) }
|
||||
func getUserED25519KeyPair() -> Box.KeyPair? { return (mockData[.userEdKeyPair] as? Box.KeyPair) }
|
||||
func getUser() -> Contact? { return nil }
|
||||
func getAllContacts() -> Set<Contact> { return Set() }
|
||||
func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set<Contact> { return Set() }
|
||||
|
||||
// MARK: - Blinded Id cache
|
||||
|
||||
func getBlindedIdMapping(with blindedId: String) -> BlindedIdMapping? { return nil }
|
||||
func getBlindedIdMapping(with blindedId: String, using transaction: YapDatabaseReadTransaction) -> BlindedIdMapping? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func cacheBlindedIdMapping(_ mapping: BlindedIdMapping) {}
|
||||
func cacheBlindedIdMapping(_ mapping: BlindedIdMapping, using transaction: YapDatabaseReadWriteTransaction) {}
|
||||
func enumerateBlindedIdMapping(with block: @escaping (BlindedIdMapping, UnsafeMutablePointer<ObjCBool>) -> ()) {}
|
||||
func enumerateBlindedIdMapping(using transaction: YapDatabaseReadTransaction, with block: @escaping (BlindedIdMapping, UnsafeMutablePointer<ObjCBool>) -> ()) {
|
||||
}
|
||||
|
||||
// MARK: - Closed Groups
|
||||
|
||||
func getUserClosedGroupPublicKeys() -> Set<String> { return Set() }
|
||||
func getZombieMembers(for groupPublicKey: String) -> Set<String> { return Set() }
|
||||
func setZombieMembers(for groupPublicKey: String, to zombies: Set<String>, using transaction: Any) {}
|
||||
func isClosedGroup(_ publicKey: String) -> Bool { return false }
|
||||
|
||||
// MARK: - Jobs
|
||||
|
||||
func persist(_ job: Job, using transaction: Any) {}
|
||||
func markJobAsSucceeded(_ job: Job, using transaction: Any) {}
|
||||
func markJobAsFailed(_ job: Job, using transaction: Any) {}
|
||||
func getAllPendingJobs(of type: Job.Type) -> [Job] { return [] }
|
||||
func getAttachmentUploadJob(for attachmentID: String) -> AttachmentUploadJob? { return nil }
|
||||
func getMessageSendJob(for messageSendJobID: String) -> MessageSendJob? { return nil }
|
||||
func getMessageSendJob(for messageSendJobID: String, using transaction: Any) -> MessageSendJob? { return nil }
|
||||
func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) {}
|
||||
func isJobCanceled(_ job: Job) -> Bool { return true }
|
||||
|
||||
// MARK: - Open Groups
|
||||
|
||||
func getAllOpenGroups() -> [String: OpenGroup] { return (mockData[.allOpenGroups] as! [String: OpenGroup]) }
|
||||
func getThreadID(for v2OpenGroupID: String) -> String? { return nil }
|
||||
func updateMessageIDCollectionByPruningMessagesWithIDs(_ messageIDs: Set<String>, using transaction: Any) {}
|
||||
|
||||
func getOpenGroupImage(for room: String, on server: String) -> Data? { return (mockData[.openGroupImage] as? Data) }
|
||||
func setOpenGroupImage(to data: Data, for room: String, on server: String, using transaction: Any) {
|
||||
mockData[.openGroupImage] = data
|
||||
}
|
||||
|
||||
func getOpenGroup(for threadID: String) -> OpenGroup? { return (mockData[.openGroup] as? OpenGroup) }
|
||||
func setOpenGroup(_ openGroup: OpenGroup, for threadID: String, using transaction: Any) { mockData[.openGroup] = openGroup }
|
||||
func getOpenGroupServer(name: String) -> OpenGroupAPI.Server? { return mockData[.openGroupServer] as? OpenGroupAPI.Server }
|
||||
func setOpenGroupServer(_ server: OpenGroupAPI.Server, using transaction: Any) { mockData[.openGroupServer] = server }
|
||||
|
||||
func getUserCount(forOpenGroupWithID openGroupID: String) -> UInt64? {
|
||||
return (mockData[.openGroupUserCount] as? UInt64)
|
||||
}
|
||||
|
||||
func setUserCount(to newValue: UInt64, forOpenGroupWithID openGroupID: String, using transaction: Any) {
|
||||
mockData[.openGroupUserCount] = newValue
|
||||
}
|
||||
|
||||
func getOpenGroupSequenceNumber(for room: String, on server: String) -> Int64? {
|
||||
let data: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
|
||||
return data["\(server).\(room)"]
|
||||
}
|
||||
|
||||
func setOpenGroupSequenceNumber(for room: String, on server: String, to newValue: Int64, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
|
||||
updatedData["\(server).\(room)"] = newValue
|
||||
mockData[.openGroupSequenceNumber] = updatedData
|
||||
}
|
||||
|
||||
func removeOpenGroupSequenceNumber(for room: String, on server: String, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:])
|
||||
updatedData["\(server).\(room)"] = nil
|
||||
mockData[.openGroupSequenceNumber] = updatedData
|
||||
}
|
||||
|
||||
func getOpenGroupInboxLatestMessageId(for server: String) -> Int64? {
|
||||
let data: [String: Int64] = ((mockData[.openGroupInboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
return data[server]
|
||||
}
|
||||
|
||||
func setOpenGroupInboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupInboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
updatedData[server] = newValue
|
||||
mockData[.openGroupInboxLatestMessageId] = updatedData
|
||||
}
|
||||
|
||||
func removeOpenGroupInboxLatestMessageId(for server: String, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupInboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
updatedData[server] = nil
|
||||
mockData[.openGroupInboxLatestMessageId] = updatedData
|
||||
}
|
||||
|
||||
func getOpenGroupOutboxLatestMessageId(for server: String) -> Int64? {
|
||||
let data: [String: Int64] = ((mockData[.openGroupOutboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
return data[server]
|
||||
}
|
||||
|
||||
func setOpenGroupOutboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupOutboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
updatedData[server] = newValue
|
||||
mockData[.openGroupOutboxLatestMessageId] = updatedData
|
||||
}
|
||||
|
||||
func removeOpenGroupOutboxLatestMessageId(for server: String, using transaction: Any) {
|
||||
var updatedData: [String: Int64] = ((mockData[.openGroupOutboxLatestMessageId] as? [String: Int64]) ?? [:])
|
||||
updatedData[server] = nil
|
||||
mockData[.openGroupOutboxLatestMessageId] = updatedData
|
||||
}
|
||||
|
||||
// MARK: - Open Group Public Keys
|
||||
|
||||
func getOpenGroupPublicKey(for server: String) -> String? {
|
||||
guard let publicKeyMap: [String: String] = mockData[.openGroupPublicKeys] as? [String: String] else {
|
||||
return (mockData[.openGroupPublicKeys] as? String)
|
||||
}
|
||||
|
||||
return publicKeyMap[server]
|
||||
}
|
||||
|
||||
func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {}
|
||||
|
||||
// MARK: - Message Handling
|
||||
|
||||
func getAllMessageRequestThreads() -> [String: TSContactThread] { return [:] }
|
||||
func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread] { return [:] }
|
||||
|
||||
func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] {
|
||||
return ((mockData[.receivedMessageTimestamp] as? UInt64).map { [$0] } ?? [])
|
||||
}
|
||||
|
||||
func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {
|
||||
mockData[.receivedMessageTimestamp] = timestamp
|
||||
}
|
||||
|
||||
func getOrCreateThread(for publicKey: String, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil }
|
||||
func persist(_ message: VisibleMessage, quotedMessage: TSQuotedMessage?, linkPreview: OWSLinkPreview?, groupPublicKey: String?, openGroupID: String?, using transaction: Any) -> String? { return nil }
|
||||
func persist(_ attachments: [VisibleMessage.Attachment], using transaction: Any) -> [String] { return [] }
|
||||
func setAttachmentState(to state: TSAttachmentPointerState, for pointer: TSAttachmentPointer, associatedWith tsIncomingMessageID: String, using transaction: Any) {}
|
||||
func persist(_ stream: TSAttachmentStream, associatedWith tsIncomingMessageID: String, using transaction: Any) {}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
|
@ -0,0 +1,181 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Mocked
|
||||
|
||||
protocol Mocked { static var mockValue: Self { get } }
|
||||
|
||||
func any<R: Mocked>() -> R { R.mockValue }
|
||||
func any<R: FixedWidthInteger>() -> R { unsafeBitCast(0, to: R.self) }
|
||||
func any<R>() -> [R] { [] }
|
||||
func any<K: Hashable, V>() -> [K: V] { [:] }
|
||||
func any() -> Any { 0 }
|
||||
func any() -> Float { 0 }
|
||||
func any() -> Double { 0 }
|
||||
func any() -> String { "" }
|
||||
func any() -> Data { Data() }
|
||||
|
||||
// MARK: - Mock<T>
|
||||
|
||||
public class Mock<T> {
|
||||
private let functionHandler: MockFunctionHandler
|
||||
internal let functionConsumer: FunctionConsumer
|
||||
|
||||
internal required init(functionHandler: MockFunctionHandler? = nil) {
|
||||
self.functionConsumer = FunctionConsumer()
|
||||
self.functionHandler = (functionHandler ?? self.functionConsumer)
|
||||
}
|
||||
|
||||
@discardableResult internal func accept(funcName: String = #function, args: [Any?] = []) -> Any? {
|
||||
return accept(funcName: funcName, checkArgs: args, actionArgs: args)
|
||||
}
|
||||
|
||||
@discardableResult internal func accept(funcName: String = #function, checkArgs: [Any?], actionArgs: [Any?]) -> Any? {
|
||||
return functionHandler.accept(funcName, parameterSummary: summary(for: checkArgs), actionArgs: actionArgs)
|
||||
}
|
||||
|
||||
internal func when<R>(_ callBlock: @escaping (T) throws -> R) -> MockFunctionBuilder<T, R> {
|
||||
let builder: MockFunctionBuilder<T, R> = MockFunctionBuilder(callBlock, mockInit: type(of: self).init)
|
||||
functionConsumer.functionBuilders.append(builder.build)
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
private func summary(for argument: Any) -> String {
|
||||
switch argument {
|
||||
case let string as String: return string
|
||||
case let array as [Any]: return "[\(array.map { summary(for: $0) }.joined(separator: ", "))]"
|
||||
|
||||
case let dict as [String: Any]:
|
||||
return "[\(dict.map { key, value in "\(summary(for: key)):\(summary(for: value))" }.joined(separator: ", "))]"
|
||||
|
||||
default: return String(describing: argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MockFunctionHandler
|
||||
|
||||
protocol MockFunctionHandler {
|
||||
func accept(_ functionName: String, parameterSummary: String, actionArgs: [Any?]) -> Any?
|
||||
}
|
||||
|
||||
// MARK: - MockFunction
|
||||
|
||||
internal class MockFunction {
|
||||
var name: String
|
||||
var parameterSummary: String
|
||||
var actions: [([Any?]) -> Void]
|
||||
var returnValue: Any?
|
||||
|
||||
init(name: String, parameterSummary: String, actions: [([Any?]) -> Void], returnValue: Any?) {
|
||||
self.name = name
|
||||
self.parameterSummary = parameterSummary
|
||||
self.actions = actions
|
||||
self.returnValue = returnValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MockFunctionBuilder
|
||||
|
||||
internal class MockFunctionBuilder<T, R>: MockFunctionHandler {
|
||||
private let callBlock: (T) throws -> R
|
||||
private let mockInit: (MockFunctionHandler?) -> Mock<T>
|
||||
private var functionName: String?
|
||||
private var parameterSummary: String?
|
||||
private var actions: [([Any?]) -> Void] = []
|
||||
private var returnValue: R?
|
||||
internal var returnValueGenerator: ((String, String) -> R?)?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(_ callBlock: @escaping (T) throws -> R, mockInit: @escaping (MockFunctionHandler?) -> Mock<T>) {
|
||||
self.callBlock = callBlock
|
||||
self.mockInit = mockInit
|
||||
}
|
||||
|
||||
// MARK: - Behaviours
|
||||
|
||||
@discardableResult func then(_ block: @escaping ([Any?]) -> Void) -> MockFunctionBuilder<T, R> {
|
||||
actions.append(block)
|
||||
return self
|
||||
}
|
||||
|
||||
func thenReturn(_ value: R?) {
|
||||
returnValue = value
|
||||
}
|
||||
|
||||
// MARK: - MockFunctionHandler
|
||||
|
||||
func accept(_ functionName: String, parameterSummary: String, actionArgs: [Any?]) -> Any? {
|
||||
self.functionName = functionName
|
||||
self.parameterSummary = parameterSummary
|
||||
return (returnValue ?? returnValueGenerator?(functionName, parameterSummary))
|
||||
}
|
||||
|
||||
// MARK: - Build
|
||||
|
||||
func build() throws -> MockFunction {
|
||||
let completionMock = mockInit(self) as! T
|
||||
_ = try callBlock(completionMock)
|
||||
|
||||
guard let name: String = functionName, let parameterSummary: String = parameterSummary else {
|
||||
preconditionFailure("Attempted to build the MockFunction before it was called")
|
||||
}
|
||||
|
||||
return MockFunction(name: name, parameterSummary: parameterSummary, actions: actions, returnValue: returnValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - FunctionConsumer
|
||||
|
||||
internal class FunctionConsumer: MockFunctionHandler {
|
||||
var trackCalls: Bool = true
|
||||
var functionBuilders: [() throws -> MockFunction?] = []
|
||||
var functionHandlers: [String: [String: MockFunction]] = [:]
|
||||
var calls: [String: [String]] = [:]
|
||||
|
||||
func accept(_ functionName: String, parameterSummary: String, actionArgs: [Any?]) -> Any? {
|
||||
if !functionBuilders.isEmpty {
|
||||
functionBuilders
|
||||
.compactMap { try? $0() }
|
||||
.forEach { function in
|
||||
functionHandlers[function.name] = (functionHandlers[function.name] ?? [:])
|
||||
.setting(function.parameterSummary, function)
|
||||
}
|
||||
|
||||
functionBuilders.removeAll()
|
||||
}
|
||||
|
||||
guard let expectation: MockFunction = firstFunction(for: functionName, matchingParameterSummaryIfPossible: parameterSummary) else {
|
||||
preconditionFailure("No expectations found for \(functionName)")
|
||||
}
|
||||
|
||||
// Record the call so it can be validated later (assuming we are tracking calls)
|
||||
if trackCalls {
|
||||
calls[functionName] = (calls[functionName] ?? []).appending(parameterSummary)
|
||||
}
|
||||
|
||||
for action in expectation.actions {
|
||||
action(actionArgs)
|
||||
}
|
||||
|
||||
return expectation.returnValue
|
||||
}
|
||||
|
||||
func firstFunction(for name: String, matchingParameterSummaryIfPossible parameterSummary: String) -> MockFunction? {
|
||||
guard let possibleExpectations: [String: MockFunction] = functionHandlers[name] else { return nil }
|
||||
|
||||
guard let expectation: MockFunction = possibleExpectations[parameterSummary] else {
|
||||
// A `nil` response might be value but in a lot of places we will need to force-cast
|
||||
// so try to find a non-nil response first
|
||||
return (
|
||||
possibleExpectations.values.first(where: { $0.returnValue != nil }) ??
|
||||
possibleExpectations.values.first
|
||||
)
|
||||
}
|
||||
|
||||
return expectation
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Nimble
|
||||
|
||||
public enum CallAmount {
|
||||
case atLeast(times: Int)
|
||||
case exactly(times: Int)
|
||||
case noMoreThan(times: Int)
|
||||
}
|
||||
|
||||
fileprivate func timeStr(_ value: Int) -> String {
|
||||
return "\(value) time\(value == 1 ? "" : "s")"
|
||||
}
|
||||
|
||||
/// Validates whether the function called in `functionBlock` has been called according to the parameter constraints
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - amount: An enum constraining the number of times the function can be called (Default is `.atLeast(times: 1)`
|
||||
///
|
||||
/// - matchingParameters: A boolean indicating whether the parameters for the function call need to match exactly
|
||||
///
|
||||
/// - exclusive: A boolean indicating whether no other functions should be called
|
||||
///
|
||||
/// - functionBlock: A closure in which the function to be validated should be called
|
||||
public func call<M, T, R>(
|
||||
_ amount: CallAmount = .atLeast(times: 1),
|
||||
matchingParameters: Bool = false,
|
||||
exclusive: Bool = false,
|
||||
functionBlock: @escaping (T) throws -> R
|
||||
) -> Predicate<M> where M: Mock<T> {
|
||||
return Predicate.define { actualExpression in
|
||||
let callInfo: CallInfo = generateCallInfo(actualExpression, functionBlock)
|
||||
let matchingParameterRecords: [String] = callInfo.desiredFunctionCalls
|
||||
.filter { !matchingParameters || callInfo.hasMatchingParameters($0) }
|
||||
let exclusiveCallsValid: Bool = (!exclusive || callInfo.allFunctionsCalled.count <= 1) // '<=' to support '0' case
|
||||
let (numParamMatchingCallsValid, timesError): (Bool, String?) = {
|
||||
switch amount {
|
||||
case .atLeast(let times):
|
||||
return (
|
||||
(matchingParameterRecords.count >= times),
|
||||
(times <= 1 ? nil : "at least \(timeStr(times))")
|
||||
)
|
||||
|
||||
case .exactly(let times):
|
||||
return (
|
||||
(matchingParameterRecords.count == times),
|
||||
"exactly \(timeStr(times))"
|
||||
)
|
||||
|
||||
case .noMoreThan(let times):
|
||||
return (
|
||||
(matchingParameterRecords.count <= times),
|
||||
(times <= 0 ? nil : "no more than \(timeStr(times))")
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
let result = (
|
||||
numParamMatchingCallsValid &&
|
||||
exclusiveCallsValid
|
||||
)
|
||||
let matchingParametersError: String? = (matchingParameters ?
|
||||
"matching the parameters\(callInfo.desiredParameters.map { ": \($0)" } ?? "")" :
|
||||
nil
|
||||
)
|
||||
let distinctParameterCombinations: Set<String> = Set(callInfo.desiredFunctionCalls)
|
||||
let actualMessage: String
|
||||
|
||||
if callInfo.caughtException != nil {
|
||||
actualMessage = "a thrown assertion (might not have been called or has no mocked return value)"
|
||||
}
|
||||
else if callInfo.function == nil {
|
||||
actualMessage = "no call details"
|
||||
}
|
||||
else if callInfo.desiredFunctionCalls.isEmpty {
|
||||
actualMessage = "no calls"
|
||||
}
|
||||
else if !exclusiveCallsValid {
|
||||
let otherFunctionsCalled: [String] = callInfo.allFunctionsCalled.filter { $0 != callInfo.functionName }
|
||||
|
||||
actualMessage = "calls to other functions: [\(otherFunctionsCalled.joined(separator: ", "))]"
|
||||
}
|
||||
else {
|
||||
let onlyMadeMatchingCalls: Bool = (matchingParameterRecords.count == callInfo.desiredFunctionCalls.count)
|
||||
|
||||
switch (numParamMatchingCallsValid, onlyMadeMatchingCalls, distinctParameterCombinations.count) {
|
||||
case (false, false, 1):
|
||||
// No calls with the matching parameter requirements but only one parameter combination
|
||||
// so include the param info
|
||||
actualMessage = "called \(timeStr(callInfo.desiredFunctionCalls.count)) with different parameters: \(callInfo.desiredFunctionCalls[0])"
|
||||
|
||||
case (false, true, _):
|
||||
actualMessage = "called \(timeStr(callInfo.desiredFunctionCalls.count))"
|
||||
|
||||
case (false, false, _):
|
||||
actualMessage = "called \(timeStr(matchingParameterRecords.count)) with matching parameters, \(timeStr(callInfo.desiredFunctionCalls.count)) total"
|
||||
|
||||
default: actualMessage = ""
|
||||
}
|
||||
}
|
||||
|
||||
return PredicateResult(
|
||||
bool: result,
|
||||
message: .expectedCustomValueTo(
|
||||
[
|
||||
"call '\(callInfo.functionName)'\(exclusive ? " exclusively" : "")",
|
||||
timesError,
|
||||
matchingParametersError
|
||||
]
|
||||
.compactMap { $0 }
|
||||
.joined(separator: " "),
|
||||
actual: actualMessage
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Shared Code
|
||||
|
||||
fileprivate struct CallInfo {
|
||||
let didError: Bool
|
||||
let caughtException: BadInstructionException?
|
||||
let function: MockFunction?
|
||||
let allFunctionsCalled: [String]
|
||||
let desiredFunctionCalls: [String]
|
||||
|
||||
var functionName: String { "\((function?.name).map { "\($0)" } ?? "a function")" }
|
||||
var desiredParameters: String? { function?.parameterSummary }
|
||||
|
||||
static var error: CallInfo {
|
||||
CallInfo(
|
||||
didError: true,
|
||||
caughtException: nil,
|
||||
function: nil,
|
||||
allFunctionsCalled: [],
|
||||
desiredFunctionCalls: []
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
didError: Bool = false,
|
||||
caughtException: BadInstructionException?,
|
||||
function: MockFunction?,
|
||||
allFunctionsCalled: [String],
|
||||
desiredFunctionCalls: [String]
|
||||
) {
|
||||
self.didError = didError
|
||||
self.caughtException = caughtException
|
||||
self.function = function
|
||||
self.allFunctionsCalled = allFunctionsCalled
|
||||
self.desiredFunctionCalls = desiredFunctionCalls
|
||||
}
|
||||
|
||||
func hasMatchingParameters(_ parameters: String) -> Bool {
|
||||
return (parameters == (function?.parameterSummary ?? "FALLBACK_NOT_FOUND"))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func generateCallInfo<M, T, R>(_ actualExpression: Expression<M>, _ functionBlock: @escaping (T) throws -> R) -> CallInfo where M: Mock<T> {
|
||||
var maybeFunction: MockFunction?
|
||||
var allFunctionsCalled: [String] = []
|
||||
var desiredFunctionCalls: [String] = []
|
||||
let builderCreator: ((M) -> MockFunctionBuilder<T, R>) = { validInstance in
|
||||
let builder: MockFunctionBuilder<T, R> = MockFunctionBuilder(functionBlock, mockInit: type(of: validInstance).init)
|
||||
builder.returnValueGenerator = { name, parameterSummary in
|
||||
validInstance.functionConsumer
|
||||
.firstFunction(for: name, matchingParameterSummaryIfPossible: parameterSummary)?
|
||||
.returnValue as? R
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
|
||||
#if (arch(x86_64) || arch(arm64)) && (canImport(Darwin) || canImport(Glibc))
|
||||
var didError: Bool = false
|
||||
let caughtException: BadInstructionException? = catchBadInstruction {
|
||||
do {
|
||||
guard let validInstance: M = try actualExpression.evaluate() else {
|
||||
didError = true
|
||||
return
|
||||
}
|
||||
|
||||
allFunctionsCalled = Array(validInstance.functionConsumer.calls.keys)
|
||||
|
||||
let builder: MockFunctionBuilder<T, R> = builderCreator(validInstance)
|
||||
validInstance.functionConsumer.trackCalls = false
|
||||
maybeFunction = try? builder.build()
|
||||
desiredFunctionCalls = (validInstance.functionConsumer.calls[maybeFunction?.name ?? ""] ?? [])
|
||||
validInstance.functionConsumer.trackCalls = true
|
||||
}
|
||||
catch {
|
||||
didError = true
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure to switch this back on in case an assertion was thrown (which would meant this
|
||||
// wouldn't have been reset)
|
||||
(try? actualExpression.evaluate())?.functionConsumer.trackCalls = true
|
||||
|
||||
guard !didError else { return CallInfo.error }
|
||||
#else
|
||||
let caughtException: BadInstructionException? = nil
|
||||
|
||||
// Just hope for the best and if there is a force-cast there's not much we can do
|
||||
guard let validInstance: M = try? actualExpression.evaluate() else { return CallInfo.error }
|
||||
|
||||
allFunctionsCalled = Array(validInstance.functionConsumer.calls.keys)
|
||||
|
||||
let builder: MockExpectationBuilder<T, R> = builderCreator(validInstance)
|
||||
validInstance.functionConsumer.trackCalls = false
|
||||
maybeFunction = try? builder.build()
|
||||
desiredFunctionCalls = (validInstance.functionConsumer.calls[maybeFunction?.name ?? ""] ?? [])
|
||||
validInstance.functionConsumer.trackCalls = true
|
||||
#endif
|
||||
|
||||
return CallInfo(
|
||||
caughtException: caughtException,
|
||||
function: maybeFunction,
|
||||
allFunctionsCalled: allFunctionsCalled,
|
||||
desiredFunctionCalls: desiredFunctionCalls
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue