Skip to content
Snippets Groups Projects
Commit 1b94869c authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Add AuthCallbackHandler

parent 0c1316ca
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!76Messenger example - auth requests handling
This commit is part of merge request !76. Comments created here will be created in the context of that merge request.
Showing
with 441 additions and 0 deletions
...@@ -57,6 +57,7 @@ let package = Package( ...@@ -57,6 +57,7 @@ let package = Package(
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
.product(name: "XXClient", package: "elixxir-dapps-sdk-swift"), .product(name: "XXClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXDatabase", package: "client-ios-db"), .product(name: "XXDatabase", package: "client-ios-db"),
.product(name: "XXMessengerClient", package: "elixxir-dapps-sdk-swift"),
.product(name: "XXModels", package: "client-ios-db"), .product(name: "XXModels", package: "client-ios-db"),
], ],
swiftSettings: swiftSettings swiftSettings: swiftSettings
......
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXMessengerClient
import XXModels
public struct AuthCallbackHandler {
public typealias OnError = (Error) -> Void
public var run: (@escaping OnError) -> Cancellable
public func callAsFunction(onError: @escaping OnError) -> Cancellable {
run(onError)
}
}
extension AuthCallbackHandler {
public static func live(
messenger: Messenger,
handleRequest: AuthCallbackHandlerRequest,
handleConfirm: AuthCallbackHandlerConfirm,
handleReset: AuthCallbackHandlerReset
) -> AuthCallbackHandler {
AuthCallbackHandler { onError in
messenger.registerAuthCallbacks(.init { callback in
do {
switch callback {
case .request(let contact, _, _, _):
try handleRequest(contact)
case .confirm(let contact, _, _, _):
try handleConfirm(contact)
case .reset(let contact, _, _, _):
try handleReset(contact)
}
} catch {
onError(error)
}
})
}
}
}
extension AuthCallbackHandler {
public static let unimplemented = AuthCallbackHandler(
run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXModels
public struct AuthCallbackHandlerConfirm {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerConfirm {
public static func live(db: DBManagerGetDB) -> AuthCallbackHandlerConfirm {
AuthCallbackHandlerConfirm { xxContact in
let id = try xxContact.getId()
guard var dbContact = try db().fetchContacts(.init(id: [id])).first else {
return
}
dbContact.authStatus = .friend
dbContact = try db().saveContact(dbContact)
}
}
}
extension AuthCallbackHandlerConfirm {
public static let unimplemented = AuthCallbackHandlerConfirm(
run: XCTUnimplemented("\(Self.self)")
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXModels
public struct AuthCallbackHandlerRequest {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerRequest {
public static func live(
db: DBManagerGetDB,
now: @escaping () -> Date
) -> AuthCallbackHandlerRequest {
AuthCallbackHandlerRequest { xxContact in
let id = try xxContact.getId()
guard try db().fetchContacts(.init(id: [id])).isEmpty else {
return
}
var dbContact = XXModels.Contact(id: id)
dbContact.marshaled = xxContact.data
dbContact.username = try xxContact.getFact(.username)?.value
dbContact.email = try xxContact.getFact(.email)?.value
dbContact.phone = try xxContact.getFact(.phone)?.value
dbContact.authStatus = .verificationInProgress
dbContact.createdAt = now()
dbContact = try db().saveContact(dbContact)
}
}
}
extension AuthCallbackHandlerRequest {
public static let unimplemented = AuthCallbackHandlerRequest(
run: XCTUnimplemented("\(Self.self)")
)
}
import Foundation
import XCTestDynamicOverlay
import XXClient
import XXModels
public struct AuthCallbackHandlerReset {
public var run: (XXClient.Contact) throws -> Void
public func callAsFunction(_ contact: XXClient.Contact) throws {
try run(contact)
}
}
extension AuthCallbackHandlerReset {
public static func live(db: DBManagerGetDB) -> AuthCallbackHandlerReset {
AuthCallbackHandlerReset { xxContact in
let id = try xxContact.getId()
guard var dbContact = try db().fetchContacts(.init(id: [id])).first else {
return
}
dbContact.authStatus = .stranger
dbContact = try db().saveContact(dbContact)
}
}
}
extension AuthCallbackHandlerReset {
public static let unimplemented = AuthCallbackHandlerReset(
run: XCTUnimplemented("\(Self.self)")
)
}
import CustomDump
import XCTest
import XXModels
import XXClient
@testable import AppCore
final class AuthCallbackHandlerConfirmTests: XCTestCase {
func testConfirm() throws {
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let dbContact = XXModels.Contact(
id: "id".data(using: .utf8)!,
authStatus: .requested
)
let confirm = AuthCallbackHandlerConfirm.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try confirm(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
var expectedSavedContact = dbContact
expectedSavedContact.authStatus = .friend
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
}
func testConfirmWhenContactNotInDatabase() throws {
let confirm = AuthCallbackHandlerConfirm.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [] }
return db
}
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try confirm(contact)
}
}
import CustomDump
import XCTest
import XXModels
import XXClient
import XCTestDynamicOverlay
@testable import AppCore
final class AuthCallbackHandlerRequestTests: XCTestCase {
func testRequestFromNewContact() throws {
let now = Date()
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return []
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
},
now: { now }
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
xxContact.getFactsFromContact.run = { _ in
[
Fact(type: .username, value: "username"),
Fact(type: .email, value: "email"),
Fact(type: .phone, value: "phone"),
]
}
try request(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
XCTAssertNoDifference(didSaveContact, [.init(
id: "id".data(using: .utf8)!,
marshaled: "contact".data(using: .utf8)!,
username: "username",
email: "email",
phone: "phone",
authStatus: .verificationInProgress,
createdAt: now
)])
}
func testRequestWhenContactInDatabase() throws {
let request = AuthCallbackHandlerRequest.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [.init(id: "id".data(using: .utf8)!)] }
return db
},
now: XCTUnimplemented("now", placeholder: Date())
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try request(contact)
}
}
import CustomDump
import XCTest
import XXModels
import XXClient
@testable import AppCore
final class AuthCallbackHandlerResetTests: XCTestCase {
func testReset() throws {
var didFetchContacts: [XXModels.Contact.Query] = []
var didSaveContact: [XXModels.Contact] = []
let dbContact = XXModels.Contact(
id: "id".data(using: .utf8)!,
authStatus: .friend
)
let reset = AuthCallbackHandlerReset.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { query in
didFetchContacts.append(query)
return [dbContact]
}
db.saveContact.run = { contact in
didSaveContact.append(contact)
return contact
}
return db
}
)
var xxContact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
xxContact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try reset(xxContact)
XCTAssertNoDifference(didFetchContacts, [.init(id: ["id".data(using: .utf8)!])])
var expectedSavedContact = dbContact
expectedSavedContact.authStatus = .stranger
XCTAssertNoDifference(didSaveContact, [expectedSavedContact])
}
func testResetWhenContactNotInDatabase() throws {
let reset = AuthCallbackHandlerReset.live(
db: .init {
var db: Database = .failing
db.fetchContacts.run = { _ in [] }
return db
}
)
var contact = XXClient.Contact.unimplemented("contact".data(using: .utf8)!)
contact.getIdFromContact.run = { _ in "id".data(using: .utf8)! }
try reset(contact)
}
}
import CustomDump
import XCTest
import XXClient
import XXMessengerClient
@testable import AppCore
final class AuthCallbackHandlerTests: XCTestCase {
func testCallbackHandling() throws {
struct TestState: Equatable {
var didRegisterAuthCallbacks = 0
var didCancelAuthCallbacks = 0
var didHandleRequest: [Contact] = []
var didHandleConfirm: [Contact] = []
var didHandleReset: [Contact] = []
}
var registeredAuthCallbacks: [AuthCallbacks] = []
var state = TestState()
var expectedState = state
var messenger: Messenger = .unimplemented
messenger.registerAuthCallbacks.run = { callbacks in
state.didRegisterAuthCallbacks += 1
registeredAuthCallbacks.append(callbacks)
return Cancellable { state.didCancelAuthCallbacks += 1 }
}
let handle = AuthCallbackHandler.live(
messenger: messenger,
handleRequest: .init { state.didHandleRequest.append($0) },
handleConfirm: .init { state.didHandleConfirm.append($0) },
handleReset: .init { state.didHandleReset.append($0) }
)
var cancellable: Any? = handle(onError: { error in
XCTFail("Unexpected error: \(error)")
})
expectedState.didRegisterAuthCallbacks += 1
XCTAssertNoDifference(state, expectedState)
let contact1 = XXClient.Contact.unimplemented("1".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.request(contact: contact1, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleRequest.append(contact1)
XCTAssertNoDifference(state, expectedState)
let contact2 = XXClient.Contact.unimplemented("2".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.confirm(contact: contact2, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleConfirm.append(contact2)
XCTAssertNoDifference(state, expectedState)
let contact3 = XXClient.Contact.unimplemented("3".data(using: .utf8)!)
registeredAuthCallbacks.first?.handle(
.reset(contact: contact3, receptionId: Data(), ephemeralId: 0, roundId: 0)
)
expectedState.didHandleReset.append(contact3)
XCTAssertNoDifference(state, expectedState)
cancellable = nil
expectedState.didCancelAuthCallbacks += 1
XCTAssertNoDifference(state, expectedState)
_ = cancellable
}
func testCallbackHandlingFailure() {
enum Failure: Error, Equatable {
case request
case confirm
case reset
}
var registeredAuthCallbacks: [AuthCallbacks] = []
var errors: [Error] = []
var messenger: Messenger = .unimplemented
messenger.registerAuthCallbacks.run = { callbacks in
registeredAuthCallbacks.append(callbacks)
return Cancellable {}
}
let handle = AuthCallbackHandler.live(
messenger: messenger,
handleRequest: .init { _ in throw Failure.request },
handleConfirm: .init { _ in throw Failure.confirm },
handleReset: .init { _ in throw Failure.reset }
)
let cancellable = handle(onError: { errors.append($0) })
registeredAuthCallbacks.first?.handle(
.request(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
registeredAuthCallbacks.first?.handle(
.confirm(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
registeredAuthCallbacks.first?.handle(
.reset(contact: .unimplemented(Data()), receptionId: Data(), ephemeralId: 0, roundId: 0)
)
XCTAssertNoDifference(
errors.map { $0 as? Failure },
[.request, .confirm, .reset]
)
_ = cancellable
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment