Fixing the broken unit tests, resolved the remaining TODOs
# Conflicts: # Session.xcodeproj/project.pbxproj # SessionMessagingKitTests/Open Groups/Models/BatchRequestInfoSpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupAPISpec.swift # SessionMessagingKitTests/Open Groups/OpenGroupManagerSpec.swift # SessionMessagingKitTests/_TestUtilities/TestOnionRequestAPI.swift # SessionTests/Conversations/Settings/ThreadSettingsViewModelSpec.swift # SessionUtilitiesKit/Networking/BatchResponse.swift # SessionUtilitiesKitTests/Networking/BatchResponseSpec.swift
This commit is contained in:
parent
5033738994
commit
ca4ce52402
|
@ -809,6 +809,7 @@
|
|||
FD9004142818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9004132818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift */; };
|
||||
FD9004152818B46300ABAAF6 /* JobRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7432804EF1B004C14C5 /* JobRunner.swift */; };
|
||||
FD9004162818B46700ABAAF6 /* JobRunnerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */; };
|
||||
FD9B30F3293EA0BF008DEE3E /* BatchResponseSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD9B30F2293EA0BF008DEE3E /* BatchResponseSpec.swift */; };
|
||||
FDA8EAFE280E8B78002B68E5 /* FailedMessageSendsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFD280E8B78002B68E5 /* FailedMessageSendsJob.swift */; };
|
||||
FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */; };
|
||||
FDA8EB10280F8238002B68E5 /* Codable+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */; };
|
||||
|
@ -1919,6 +1920,7 @@
|
|||
FD8ECFA0293D8FDD00C0D1BB /* URLResponse+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLResponse+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FD90040E2818AB6D00ABAAF6 /* GetSnodePoolJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSnodePoolJob.swift; sourceTree = "<group>"; };
|
||||
FD9004132818AD0B00ABAAF6 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
|
||||
FD9B30F2293EA0BF008DEE3E /* BatchResponseSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchResponseSpec.swift; sourceTree = "<group>"; };
|
||||
FDA8EAFD280E8B78002B68E5 /* FailedMessageSendsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedMessageSendsJob.swift; sourceTree = "<group>"; };
|
||||
FDA8EAFF280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedAttachmentDownloadsJob.swift; sourceTree = "<group>"; };
|
||||
FDA8EB0F280F8238002B68E5 /* Codable+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Codable+Utilities.swift"; sourceTree = "<group>"; };
|
||||
|
@ -3968,7 +3970,7 @@
|
|||
children = (
|
||||
FD37EA1228AB3F60003AE748 /* Database */,
|
||||
FD83B9B927CF20A5005E1583 /* General */,
|
||||
FD8ECF832934507500C0D1BB /* Networking */,
|
||||
FD9B30F1293EA0AF008DEE3E /* Networking */,
|
||||
);
|
||||
path = SessionUtilitiesKitTests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4065,6 +4067,14 @@
|
|||
path = JobRunner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD9B30F1293EA0AF008DEE3E /* Networking */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD9B30F2293EA0BF008DEE3E /* BatchResponseSpec.swift */,
|
||||
);
|
||||
path = Networking;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FDC2909227D710A9005DAE71 /* Types */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -5916,6 +5926,7 @@
|
|||
FD23EA6328ED0B260058676E /* CombineExtensions.swift in Sources */,
|
||||
FD8ECF852934508B00C0D1BB /* BatchResponseSpec.swift in Sources */,
|
||||
FD2AAAEE28ED3E1100A49611 /* MockGeneralCache.swift in Sources */,
|
||||
FD9B30F3293EA0BF008DEE3E /* BatchResponseSpec.swift in Sources */,
|
||||
FD37EA1528AB42CB003AE748 /* IdentitySpec.swift in Sources */,
|
||||
FD1A94FE2900D2EA000D73D3 /* PersistableRecordUtilitiesSpec.swift in Sources */,
|
||||
FDC290AA27D9B6FD005DAE71 /* Mock.swift in Sources */,
|
||||
|
|
|
@ -53,6 +53,7 @@ public enum PushRegistrationError: Error {
|
|||
|
||||
return registerUserNotificationSettings()
|
||||
.setFailureType(to: Error.self)
|
||||
.receive(on: DispatchQueue.main) // MUST be on main thread
|
||||
.flatMap { _ -> AnyPublisher<(pushToken: String, voipToken: String), Error> in
|
||||
#if targetEnvironment(simulator)
|
||||
return Fail(error: PushRegistrationError.pushNotSupported(description: "Push not supported on simulators"))
|
||||
|
|
|
@ -188,7 +188,6 @@ extension SyncPushTokensJob {
|
|||
}
|
||||
.sinkUntilComplete(
|
||||
receiveCompletion: { result in
|
||||
// TODO: Test these are called correctly
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure(let error): failure(error)
|
||||
|
|
|
@ -26,8 +26,7 @@ public protocol OGMCacheType {
|
|||
|
||||
// MARK: - OpenGroupManager
|
||||
|
||||
@objc(SNOpenGroupManager)
|
||||
public final class OpenGroupManager: NSObject {
|
||||
public final class OpenGroupManager {
|
||||
// MARK: - Cache
|
||||
|
||||
public class Cache: OGMCacheType {
|
||||
|
@ -61,7 +60,7 @@ public final class OpenGroupManager: NSObject {
|
|||
|
||||
// MARK: - Variables
|
||||
|
||||
@objc public static let shared: OpenGroupManager = OpenGroupManager()
|
||||
public static let shared: OpenGroupManager = OpenGroupManager()
|
||||
|
||||
/// Note: This should not be accessed directly but rather via the 'OGMDependencies' type
|
||||
fileprivate let mutableCache: Atomic<OGMCacheType> = Atomic(Cache())
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Combine
|
||||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
|
@ -19,97 +19,117 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
// MARK: - BatchSubRequest
|
||||
// MARK: - BatchRequest.Child
|
||||
|
||||
describe("a BatchRequest.Child") {
|
||||
var subRequest: OpenGroupAPI.BatchRequest.Child!
|
||||
var request: OpenGroupAPI.BatchRequest!
|
||||
|
||||
context("when initializing") {
|
||||
it("sets the headers to nil if there aren't any") {
|
||||
subRequest = OpenGroupAPI.BatchRequest.Child(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
request = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
expect(subRequest.headers).to(beNil())
|
||||
expect(request.requests.first?.headers).to(beNil())
|
||||
}
|
||||
|
||||
it("converts the headers to HTTP headers") {
|
||||
subRequest = OpenGroupAPI.BatchRequest.Child(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [.authorization: "testAuth"],
|
||||
body: nil
|
||||
)
|
||||
request = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [.authorization: "testAuth"],
|
||||
body: nil
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
expect(subRequest.headers).to(equal(["Authorization": "testAuth"]))
|
||||
expect(request.requests.first?.headers).to(equal(["Authorization": "testAuth"]))
|
||||
}
|
||||
}
|
||||
|
||||
context("when encoding") {
|
||||
it("successfully encodes a string body") {
|
||||
subRequest = OpenGroupAPI.BatchRequest.Child(
|
||||
request: Request<String, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: "testBody"
|
||||
)
|
||||
request = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: Request<String, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: "testBody"
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
let childRequestData: Data = try! JSONEncoder().encode(request.requests[0])
|
||||
let childRequestString: String? = String(data: childRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
expect(childRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"b64\":\"testBody\"}"))
|
||||
}
|
||||
|
||||
it("successfully encodes a byte body") {
|
||||
subRequest = OpenGroupAPI.BatchRequest.Child(
|
||||
request: Request<[UInt8], OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: [1, 2, 3]
|
||||
)
|
||||
request = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: Request<[UInt8], OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: [1, 2, 3]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
let childRequestData: Data = try! JSONEncoder().encode(request.requests[0])
|
||||
let childRequestString: String? = String(data: childRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
expect(childRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"bytes\":[1,2,3]}"))
|
||||
}
|
||||
|
||||
it("successfully encodes a JSON body") {
|
||||
subRequest = OpenGroupAPI.BatchRequest.Child(
|
||||
request: Request<TestType, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: TestType(stringValue: "testValue")
|
||||
)
|
||||
request = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: Request<TestType, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: TestType(stringValue: "testValue")
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
let childRequestData: Data = try! JSONEncoder().encode(request.requests[0])
|
||||
let childRequestString: String? = String(data: childRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
expect(childRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"json\":{\"stringValue\":\"testValue\"}}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BatchRequestInfo<T, R>
|
||||
// MARK: - BatchRequest.Info
|
||||
|
||||
describe("a BatchRequest.Info") {
|
||||
var request: Request<TestType, OpenGroupAPI.Endpoint>!
|
||||
|
@ -143,27 +163,17 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
expect(requestInfo.endpoint.path).to(equal(request.endpoint.path))
|
||||
expect(requestInfo.responseType == HTTP.BatchSubResponse<TestType>.self).to(beTrue())
|
||||
}
|
||||
|
||||
it("exposes the endpoint correctly") {
|
||||
let requestInfo: OpenGroupAPI.BatchRequest.Info = OpenGroupAPI.BatchRequest.Info(
|
||||
request: request
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
// MARK: --Decodable
|
||||
|
||||
describe("a Decodable") {
|
||||
it("decodes correctly") {
|
||||
let jsonData: Data = "{\"stringValue\":\"testValue\"}".data(using: .utf8)!
|
||||
let result: TestType? = try? TestType.decoded(from: jsonData)
|
||||
|
||||
expect(requestInfo.endpoint.path).to(equal(request.endpoint.path))
|
||||
}
|
||||
|
||||
it("generates a sub request correctly") {
|
||||
let batchRequest: OpenGroupAPI.BatchRequest = OpenGroupAPI.BatchRequest(
|
||||
requests: [
|
||||
OpenGroupAPI.BatchRequest.Info(
|
||||
request: request
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
expect(batchRequest.requests[0].method).to(equal(request.method))
|
||||
expect(batchRequest.requests[0].path).to(equal(request.urlPathAndParamsString))
|
||||
expect(batchRequest.requests[0].headers).to(beNil())
|
||||
expect(result).to(equal(TestType(stringValue: "testValue")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import PromiseKit
|
||||
import Foundation
|
||||
import Combine
|
||||
import GRDB
|
||||
import Sodium
|
||||
import SessionSnodeKit
|
||||
|
@ -779,7 +780,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
var didComplete: Bool = false // Prevent multi-threading test bugs
|
||||
|
||||
mockStorage
|
||||
.writeAsync { db in
|
||||
.writePublisherFlatMap { db in
|
||||
openGroupManager
|
||||
.add(
|
||||
db,
|
||||
|
@ -790,8 +791,9 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.map { _ -> Void in didComplete = true }
|
||||
.retainUntilComplete()
|
||||
.subscribe(on: DispatchQueue.main)
|
||||
.receiveOnMain(immediately: true)
|
||||
.sinkUntilComplete(receiveCompletion: { _ in didComplete = true })
|
||||
|
||||
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
|
||||
expect(
|
||||
|
@ -810,7 +812,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
var didComplete: Bool = false // Prevent multi-threading test bugs
|
||||
|
||||
mockStorage
|
||||
.writeAsync { db in
|
||||
.writePublisherFlatMap { db in
|
||||
openGroupManager
|
||||
.add(
|
||||
db,
|
||||
|
@ -821,8 +823,9 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.map { _ -> Void in didComplete = true }
|
||||
.retainUntilComplete()
|
||||
.subscribe(on: DispatchQueue.main)
|
||||
.receiveOnMain(immediately: true)
|
||||
.sinkUntilComplete(receiveCompletion: { _ in didComplete = true })
|
||||
|
||||
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
|
||||
expect(mockOGMCache)
|
||||
|
@ -847,7 +850,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
var didComplete: Bool = false // Prevent multi-threading test bugs
|
||||
|
||||
mockStorage
|
||||
.writeAsync { db in
|
||||
.writePublisherFlatMap { db in
|
||||
openGroupManager
|
||||
.add(
|
||||
db,
|
||||
|
@ -860,8 +863,9 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.map { _ -> Void in didComplete = true }
|
||||
.retainUntilComplete()
|
||||
.subscribe(on: DispatchQueue.main)
|
||||
.receiveOnMain(immediately: true)
|
||||
.sinkUntilComplete(receiveCompletion: { _ in didComplete = true })
|
||||
|
||||
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
|
||||
expect(
|
||||
|
@ -901,7 +905,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
var error: Error?
|
||||
|
||||
mockStorage
|
||||
.writeAsync { db in
|
||||
.writePublisherFlatMap { db in
|
||||
openGroupManager
|
||||
.add(
|
||||
db,
|
||||
|
@ -912,8 +916,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.catch { error = $0 }
|
||||
.retainUntilComplete()
|
||||
.subscribe(on: DispatchQueue.main)
|
||||
.receiveOnMain(immediately: true)
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(error?.localizedDescription)
|
||||
.toEventually(
|
||||
|
@ -1242,8 +1248,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
).insert(db)
|
||||
}
|
||||
|
||||
mockOGMCache.when { $0.groupImagePromises }
|
||||
.thenReturn([OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Promise.value(Data())])
|
||||
mockOGMCache.when { $0.groupImagePublishers }
|
||||
.thenReturn([
|
||||
OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Just(Data()).setFailureType(to: Error.self).eraseToAnyPublisher()
|
||||
])
|
||||
|
||||
mockStorage.write { db in
|
||||
try OpenGroupManager.handlePollInfo(
|
||||
|
@ -1679,8 +1687,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.updateAll(db, OpenGroup.Columns.imageData.set(to: nil))
|
||||
}
|
||||
|
||||
mockOGMCache.when { $0.groupImagePromises }
|
||||
.thenReturn([OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Promise.value(imageData)])
|
||||
mockOGMCache.when { $0.groupImagePublishers }
|
||||
.thenReturn([
|
||||
OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Just(imageData).setFailureType(to: Error.self).eraseToAnyPublisher()
|
||||
])
|
||||
}
|
||||
|
||||
it("uses the provided room image id if available") {
|
||||
|
@ -1952,8 +1962,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
it("does nothing if it fails to retrieve the room image") {
|
||||
var didComplete: Bool = false // Prevent multi-threading test bugs
|
||||
|
||||
mockOGMCache.when { $0.groupImagePromises }
|
||||
.thenReturn([OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Promise(error: HTTPError.generic)])
|
||||
mockOGMCache.when { $0.groupImagePublishers }
|
||||
.thenReturn([
|
||||
OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): Fail(error: HTTPError.generic).eraseToAnyPublisher()
|
||||
])
|
||||
|
||||
testPollInfo = OpenGroupAPI.RoomPollInfo(
|
||||
token: "testRoom",
|
||||
|
@ -3248,11 +3260,11 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("returns the cached promise if there is one") {
|
||||
let (promise, _) = Promise<[OpenGroupAPI.Room]>.pending()
|
||||
mockOGMCache.when { $0.defaultRoomsPromise }.thenReturn(promise)
|
||||
let publisher = Future { _ in }.eraseToAnyPublisher()
|
||||
mockOGMCache.when { $0.defaultRoomsPublisher }.thenReturn(publisher)
|
||||
|
||||
expect(OpenGroupManager.getDefaultRoomsIfNeeded(using: dependencies))
|
||||
.to(equal(promise))
|
||||
.to(equal(publisher))
|
||||
}
|
||||
|
||||
it("stores the open group information") {
|
||||
|
@ -3494,12 +3506,12 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("retrieves the image retrieval promise from the cache if it exists") {
|
||||
let (promise, _) = Promise<Data>.pending()
|
||||
let publisher = Future<Data, Error> { _ in }.eraseToAnyPublisher()
|
||||
mockOGMCache
|
||||
.when { $0.groupImagePromises }
|
||||
.thenReturn([OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): promise])
|
||||
.when { $0.groupImagePublishers }
|
||||
.thenReturn([OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): publisher])
|
||||
|
||||
let promise2 = mockStorage.read { db in
|
||||
let publisher2 = mockStorage.read { db in
|
||||
OpenGroupManager
|
||||
.roomImage(
|
||||
db,
|
||||
|
@ -3509,7 +3521,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
using: dependencies
|
||||
)
|
||||
}
|
||||
expect(promise2).to(equal(promise))
|
||||
expect(publisher2).to(equal(publisher))
|
||||
}
|
||||
|
||||
it("does not save the fetched image to storage") {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Combine
|
||||
import SessionUtilitiesKit
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockOGMCache: Mock<OGMCacheType>, OGMCacheType {
|
||||
var defaultRoomsPromise: Promise<[OpenGroupAPI.Room]>? {
|
||||
get { return accept() as? Promise<[OpenGroupAPI.Room]> }
|
||||
var defaultRoomsPublisher: AnyPublisher<[OpenGroupAPI.Room], Error>? {
|
||||
get { return accept() as? AnyPublisher<[OpenGroupAPI.Room], Error> }
|
||||
set { accept(args: [newValue]) }
|
||||
}
|
||||
|
||||
var groupImagePromises: [String: Promise<Data>] {
|
||||
get { return accept() as! [String: Promise<Data>] }
|
||||
var groupImagePublishers: [String: AnyPublisher<Data, Error>] {
|
||||
get { return accept() as! [String: AnyPublisher<Data, Error>] }
|
||||
set { accept(args: [newValue]) }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Combine
|
||||
import SessionSnodeKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
|
@ -23,7 +23,7 @@ class TestOnionRequestAPI: OnionRequestAPIType {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ResponseInfo: ResponseInfoType {
|
||||
let requestData: RequestData
|
||||
let code: Int
|
||||
|
@ -38,41 +38,45 @@ class TestOnionRequestAPI: OnionRequestAPIType {
|
|||
|
||||
class var mockResponse: Data? { return nil }
|
||||
|
||||
static func sendOnionRequest(_ request: URLRequest, to server: String, with x25519PublicKey: String) -> Promise<(ResponseInfoType, Data?)> {
|
||||
static func sendOnionRequest(_ request: URLRequest, to server: String, with x25519PublicKey: String) -> AnyPublisher<(ResponseInfoType, Data?), Error> {
|
||||
let responseInfo: ResponseInfo = ResponseInfo(
|
||||
requestData: RequestData(
|
||||
urlString: request.url?.absoluteString,
|
||||
httpMethod: (request.httpMethod ?? "GET"),
|
||||
headers: (request.allHTTPHeaderFields ?? [:]),
|
||||
body: request.httpBody,
|
||||
destination: OnionRequestAPIDestination.server(
|
||||
host: request.url!.host!,
|
||||
target: OnionRequestAPIVersion.v4.rawValue,
|
||||
x25519PublicKey: x25519PublicKey,
|
||||
scheme: request.url!.scheme,
|
||||
port: request.url!.port.map { UInt16($0) }
|
||||
)
|
||||
server: server,
|
||||
version: .v4,
|
||||
publicKey: x25519PublicKey
|
||||
),
|
||||
code: 200,
|
||||
headers: [:]
|
||||
)
|
||||
|
||||
return Promise.value((responseInfo, mockResponse))
|
||||
return Just((responseInfo, mockResponse))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
static func sendOnionRequest(_ payload: Data, to snode: Snode) -> Promise<(ResponseInfoType, Data?)> {
|
||||
static func sendOnionRequest(_ payload: Data, to snode: Snode) -> AnyPublisher<(ResponseInfoType, Data?), Error> {
|
||||
let responseInfo: ResponseInfo = ResponseInfo(
|
||||
requestData: RequestData(
|
||||
urlString: "\(snode.address):\(snode.port)/onion_req/v2",
|
||||
urlString: nil,
|
||||
httpMethod: "POST",
|
||||
headers: [:],
|
||||
snodeMethod: nil,
|
||||
body: payload,
|
||||
destination: OnionRequestAPIDestination.snode(snode)
|
||||
|
||||
server: "",
|
||||
version: .v3,
|
||||
publicKey: snode.x25519PublicKey
|
||||
),
|
||||
code: 200,
|
||||
headers: [:]
|
||||
)
|
||||
|
||||
return Promise.value((responseInfo, mockResponse))
|
||||
return Just((responseInfo, mockResponse))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
|
|||
AppReadiness.runNowOrWhenAppDidBecomeReady {
|
||||
let openGroupPollingPublishers: [AnyPublisher<Void, Error>] = self.pollForOpenGroups()
|
||||
defer {
|
||||
// TODO: Test this
|
||||
Publishers
|
||||
.MergeMany(openGroupPollingPublishers)
|
||||
.sinkUntilComplete(
|
||||
|
|
|
@ -14,30 +14,24 @@ public protocol OnionRequestAPIType {
|
|||
/// See the "Onion Requests" section of [The Session Whitepaper](https://arxiv.org/pdf/2002.04609.pdf) for more information.
|
||||
public enum OnionRequestAPI: OnionRequestAPIType {
|
||||
private static var buildPathsPublisher: Atomic<AnyPublisher<[[Snode]], Error>?> = Atomic(nil)
|
||||
|
||||
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
|
||||
private static var pathFailureCount: [[Snode]: UInt] = [:]
|
||||
|
||||
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
|
||||
private static var snodeFailureCount: [Snode: UInt] = [:]
|
||||
|
||||
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
|
||||
public static var guardSnodes: Set<Snode> = []
|
||||
private static var pathFailureCount: Atomic<[[Snode]: UInt]> = Atomic([:])
|
||||
private static var snodeFailureCount: Atomic<[Snode: UInt]> = Atomic([:])
|
||||
public static var guardSnodes: Atomic<Set<Snode>> = Atomic([])
|
||||
|
||||
// Not a set to ensure we consistently show the same path to the user
|
||||
private static var _paths: [[Snode]]?
|
||||
private static var _paths: Atomic<[[Snode]]?> = Atomic(nil)
|
||||
public static var paths: [[Snode]] {
|
||||
get {
|
||||
if let paths: [[Snode]] = _paths { return paths }
|
||||
if let paths: [[Snode]] = _paths.wrappedValue { return paths }
|
||||
|
||||
let results: [[Snode]]? = Storage.shared.read { db in
|
||||
try? Snode.fetchAllOnionRequestPaths(db)
|
||||
}
|
||||
|
||||
if results?.isEmpty == false { _paths = results }
|
||||
if results?.isEmpty == false { _paths.mutate { $0 = results } }
|
||||
return (results ?? [])
|
||||
}
|
||||
set { _paths = newValue }
|
||||
set { _paths.mutate { $0 = newValue } }
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
|
@ -94,8 +88,8 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
/// Finds `targetGuardSnodeCount` guard snodes to use for path building. The returned promise errors out with
|
||||
/// `Error.insufficientSnodes` if not enough (reliable) snodes are available.
|
||||
private static func getGuardSnodes(reusing reusableGuardSnodes: [Snode]) -> AnyPublisher<Set<Snode>, Error> {
|
||||
guard guardSnodes.count < targetGuardSnodeCount else {
|
||||
return Just(guardSnodes)
|
||||
guard guardSnodes.wrappedValue.count < targetGuardSnodeCount else {
|
||||
return Just(guardSnodes.wrappedValue)
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -141,7 +135,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
.map { output in Set(output) }
|
||||
.handleEvents(
|
||||
receiveOutput: { output in
|
||||
OnionRequestAPI.guardSnodes = output
|
||||
OnionRequestAPI.guardSnodes.mutate { $0 = output }
|
||||
}
|
||||
)
|
||||
.eraseToAnyPublisher()
|
||||
|
@ -222,10 +216,12 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
var cancellable: [AnyCancellable] = []
|
||||
|
||||
if !paths.isEmpty {
|
||||
guardSnodes.formUnion([ paths[0][0] ])
|
||||
|
||||
if paths.count >= 2 {
|
||||
guardSnodes.formUnion([ paths[1][0] ])
|
||||
guardSnodes.mutate {
|
||||
$0.formUnion([ paths[0][0] ])
|
||||
|
||||
if paths.count >= 2 {
|
||||
$0.formUnion([ paths[1][0] ])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,20 +305,14 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
}
|
||||
|
||||
private static func dropGuardSnode(_ snode: Snode) {
|
||||
#if DEBUG
|
||||
dispatchPrecondition(condition: .onQueue(Threading.workQueue))
|
||||
#endif
|
||||
guardSnodes = guardSnodes.filter { $0 != snode }
|
||||
guardSnodes.mutate { snodes in snodes = snodes.filter { $0 != snode } }
|
||||
}
|
||||
|
||||
private static func drop(_ snode: Snode) throws {
|
||||
#if DEBUG
|
||||
dispatchPrecondition(condition: .onQueue(Threading.workQueue))
|
||||
#endif
|
||||
// We repair the path here because we can do it sync. In the case where we drop a whole
|
||||
// path we leave the re-building up to getPath(excluding:) because re-building the path
|
||||
// in that case is async.
|
||||
OnionRequestAPI.snodeFailureCount[snode] = 0
|
||||
OnionRequestAPI.snodeFailureCount.mutate { $0[snode] = 0 }
|
||||
var oldPaths = paths
|
||||
guard let pathIndex = oldPaths.firstIndex(where: { $0.contains(snode) }) else { return }
|
||||
var path = oldPaths[pathIndex]
|
||||
|
@ -344,10 +334,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
}
|
||||
|
||||
private static func drop(_ path: [Snode]) {
|
||||
#if DEBUG
|
||||
dispatchPrecondition(condition: .onQueue(Threading.workQueue))
|
||||
#endif
|
||||
OnionRequestAPI.pathFailureCount[path] = 0
|
||||
OnionRequestAPI.pathFailureCount.mutate { $0[path] = 0 }
|
||||
var paths = OnionRequestAPI.paths
|
||||
guard let pathIndex = paths.firstIndex(of: path) else { return }
|
||||
paths.remove(at: pathIndex)
|
||||
|
@ -533,7 +520,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
func handleUnspecificError() {
|
||||
guard let path = path else { return }
|
||||
|
||||
var pathFailureCount = OnionRequestAPI.pathFailureCount[path] ?? 0
|
||||
var pathFailureCount: UInt = (OnionRequestAPI.pathFailureCount.wrappedValue[path] ?? 0)
|
||||
pathFailureCount += 1
|
||||
|
||||
if pathFailureCount >= pathFailureThreshold {
|
||||
|
@ -545,7 +532,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
drop(path)
|
||||
}
|
||||
else {
|
||||
OnionRequestAPI.pathFailureCount[path] = pathFailureCount
|
||||
OnionRequestAPI.pathFailureCount.mutate { $0[path] = pathFailureCount }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,7 +553,7 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
let ed25519PublicKey = message[message.index(message.startIndex, offsetBy: prefix.count)..<message.endIndex]
|
||||
|
||||
if let path = path, let snode = path.first(where: { $0.ed25519PublicKey == ed25519PublicKey }) {
|
||||
var snodeFailureCount = OnionRequestAPI.snodeFailureCount[snode] ?? 0
|
||||
var snodeFailureCount: UInt = (OnionRequestAPI.snodeFailureCount.wrappedValue[snode] ?? 0)
|
||||
snodeFailureCount += 1
|
||||
|
||||
if snodeFailureCount >= snodeFailureThreshold {
|
||||
|
@ -579,7 +566,8 @@ public enum OnionRequestAPI: OnionRequestAPIType {
|
|||
}
|
||||
}
|
||||
else {
|
||||
OnionRequestAPI.snodeFailureCount[snode] = snodeFailureCount
|
||||
OnionRequestAPI.snodeFailureCount
|
||||
.mutate { $0[snode] = snodeFailureCount }
|
||||
}
|
||||
} else {
|
||||
// Do nothing
|
||||
|
|
|
@ -209,10 +209,6 @@ class ThreadSettingsViewModelSpec: QuickSpec {
|
|||
beforeEach {
|
||||
viewModel.rightNavItems.firstValue()??.first?.action?()
|
||||
viewModel.textChanged("TestNew", for: .nickname)
|
||||
// TODO: Enter edit mode by pressing on the first item
|
||||
// viewModel.tableData.first?
|
||||
// .elements.first?
|
||||
// .onTap?()
|
||||
}
|
||||
|
||||
it("enters the editing state") {
|
||||
|
@ -337,12 +333,7 @@ class ThreadSettingsViewModelSpec: QuickSpec {
|
|||
context("when entering edit mode") {
|
||||
beforeEach {
|
||||
viewModel.rightNavItems.firstValue()??.first?.action?()
|
||||
viewModel.textChanged("TestUserNew", for: .nickname)
|
||||
|
||||
// TODO: Enter edit mode by pressing on the first item
|
||||
// viewModel.tableData.first?
|
||||
// .elements.first?
|
||||
// .onTap?()
|
||||
viewModel.textChanged("TestNew", for: .nickname)
|
||||
}
|
||||
|
||||
it("enters the editing state") {
|
||||
|
|
|
@ -20,6 +20,11 @@ extension Optional {
|
|||
public func defaulting(to value: Wrapped) -> Wrapped {
|
||||
return (self ?? value)
|
||||
}
|
||||
|
||||
public mutating func setting(to value: Wrapped) -> Wrapped {
|
||||
self = value
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where Wrapped == String {
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Combine
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionUtilitiesKit
|
||||
|
||||
class BatchRequestInfoSpec: QuickSpec {
|
||||
class BatchResponseSpec: QuickSpec {
|
||||
struct TestType: Codable, Equatable {
|
||||
let stringValue: String
|
||||
}
|
||||
|
||||
struct TestType2: Codable, Equatable {
|
||||
let intValue: Int
|
||||
let stringValue2: String
|
||||
|
@ -136,9 +135,9 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - --Promise
|
||||
// MARK: - --Combine
|
||||
|
||||
describe("a (ResponseInfoType, Data?) Promise") {
|
||||
describe("a (ResponseInfoType, Data?) Publisher") {
|
||||
var responseInfo: ResponseInfoType!
|
||||
var testType: TestType!
|
||||
var testType2: TestType2!
|
||||
|
@ -146,8 +145,8 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
|
||||
beforeEach {
|
||||
responseInfo = HTTP.ResponseInfo(code: 200, headers: [:])
|
||||
testType = TestType(stringValue: "Test")
|
||||
testType2 = TestType2(intValue: 1, stringValue2: "Test2")
|
||||
testType = TestType(stringValue: "test1")
|
||||
testType2 = TestType2(intValue: 123, stringValue2: "test2")
|
||||
data = """
|
||||
[\([
|
||||
try! JSONEncoder().encode(
|
||||
|
@ -173,46 +172,79 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
}
|
||||
|
||||
it("decodes valid data correctly") {
|
||||
let result = Promise.value((responseInfo, data))
|
||||
var result: HTTP.BatchResponse?
|
||||
Just((responseInfo, data))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [
|
||||
HTTP.BatchSubResponse<TestType>.self,
|
||||
HTTP.BatchSubResponse<TestType2>.self
|
||||
])
|
||||
.sinkUntilComplete(
|
||||
receiveValue: { result = $0 }
|
||||
)
|
||||
|
||||
expect(result.value).toNot(beNil())
|
||||
expect((result.value?[0].1 as? HTTP.BatchSubResponse<TestType>)?.body)
|
||||
expect(result).toNot(beNil())
|
||||
expect((result?[0].1 as? HTTP.BatchSubResponse<TestType>)?.body)
|
||||
.to(equal(testType))
|
||||
expect((result.value?[1].1 as? HTTP.BatchSubResponse<TestType2>)?.body)
|
||||
expect((result?[1].1 as? HTTP.BatchSubResponse<TestType2>)?.body)
|
||||
.to(equal(testType2))
|
||||
}
|
||||
|
||||
it("fails if there is no data") {
|
||||
let result = Promise.value((responseInfo, nil)).decoded(as: [])
|
||||
var error: Error?
|
||||
Just((responseInfo, nil))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [])
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
expect(error?.localizedDescription)
|
||||
.to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the data is not JSON") {
|
||||
let result = Promise.value((responseInfo, Data([1, 2, 3]))).decoded(as: [])
|
||||
var error: Error?
|
||||
Just((responseInfo, Data([1, 2, 3])))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [])
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
expect(error?.localizedDescription)
|
||||
.to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the data is not a JSON array") {
|
||||
let result = Promise.value((responseInfo, "{}".data(using: .utf8))).decoded(as: [])
|
||||
var error: Error?
|
||||
Just((responseInfo, "{}".data(using: .utf8)))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [])
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
expect(error?.localizedDescription)
|
||||
.to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the JSON array does not have the same number of items as the expected types") {
|
||||
let result = Promise.value((responseInfo, data))
|
||||
var error: Error?
|
||||
Just((responseInfo, data))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [
|
||||
HTTP.BatchSubResponse<TestType>.self,
|
||||
HTTP.BatchSubResponse<TestType2>.self,
|
||||
HTTP.BatchSubResponse<TestType2>.self
|
||||
])
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
expect(error?.localizedDescription)
|
||||
.to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if one of the JSON array values fails to decode") {
|
||||
|
@ -230,13 +262,20 @@ class BatchRequestInfoSpec: QuickSpec {
|
|||
.map { String(data: $0, encoding: .utf8)! }
|
||||
.joined(separator: ",")),{"test": "test"}]
|
||||
""".data(using: .utf8)!
|
||||
let result = Promise.value((responseInfo, data))
|
||||
|
||||
var error: Error?
|
||||
Just((responseInfo, data))
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
.decoded(as: [
|
||||
HTTP.BatchSubResponse<TestType>.self,
|
||||
HTTP.BatchSubResponse<TestType2>.self
|
||||
])
|
||||
.mapError { error.setting(to: $0) }
|
||||
.sinkUntilComplete()
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
expect(error?.localizedDescription)
|
||||
.to(equal(HTTPError.parsingFailed.localizedDescription))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtilitiesKit
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
|
|
@ -1,28 +1,19 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import GRDB
|
||||
import PromiseKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
class SynchronousStorage: Storage {
|
||||
override func writeAsync<T>(updates: @escaping (Database) throws -> T) {
|
||||
super.write(updates: updates)
|
||||
}
|
||||
|
||||
override func writeAsync<T>(updates: @escaping (Database) throws -> T, completion: @escaping (Database, Swift.Result<T, Error>) throws -> Void) {
|
||||
super.write { db in
|
||||
do {
|
||||
var result: T?
|
||||
try db.inTransaction {
|
||||
result = try updates(db)
|
||||
return .commit
|
||||
}
|
||||
try? completion(db, .success(result!))
|
||||
}
|
||||
catch {
|
||||
try? completion(db, .failure(error))
|
||||
}
|
||||
override func writePublisher<T>(updates: @escaping (Database) throws -> T) -> AnyPublisher<T, Error> {
|
||||
guard let result: T = super.write(updates: updates) else {
|
||||
return Fail(error: StorageError.generic)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return Just(result)
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue