mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 23:33:36 -06:00
[PM-13607] Implement SDK client-managed state repository registration (#1738)
Co-authored-by: Matt Czech <matt@livefront.com>
This commit is contained in:
parent
ee379aa6d2
commit
ae99fe362d
@ -8,6 +8,7 @@ class MockPlatformClientService: PlatformClientService {
|
||||
var fingerprintResult: Result<String, Error> = .success("a-fingerprint-phrase-string-placeholder")
|
||||
var featureFlags: [String: Bool] = [:]
|
||||
var loadFlagsError: Error?
|
||||
var stateMock = MockStateClient()
|
||||
var userFingerprintCalled = false
|
||||
|
||||
func fido2() -> ClientFido2Service {
|
||||
@ -25,6 +26,10 @@ class MockPlatformClientService: PlatformClientService {
|
||||
featureFlags = flags
|
||||
}
|
||||
|
||||
func state() -> StateClientProtocol {
|
||||
stateMock
|
||||
}
|
||||
|
||||
func userFingerprint(material fingerprintMaterial: String) throws -> String {
|
||||
fingerprintMaterialString = fingerprintMaterial
|
||||
userFingerprintCalled = true
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import BitwardenSdk
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
|
||||
final class MockStateClient: StateClientProtocol {
|
||||
var registerCipherRepositoryReceivedStore: CipherRepository?
|
||||
|
||||
func registerCipherRepository(store: CipherRepository) {
|
||||
registerCipherRepositoryReceivedStore = store
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,9 @@ protocol PlatformClientService: AnyObject {
|
||||
/// - Parameter flags: Flags to load.
|
||||
func loadFlags(_ flags: [String: Bool]) throws
|
||||
|
||||
/// Returns an object to handle state.
|
||||
func state() -> StateClientProtocol
|
||||
|
||||
/// Fingerprint using logged in user's public key
|
||||
/// - Parameter material: Fingerprint material to use
|
||||
/// - Returns: User fingerprint
|
||||
@ -38,6 +41,10 @@ extension PlatformClient: PlatformClientService {
|
||||
try loadFlags(flags: flags)
|
||||
}
|
||||
|
||||
func state() -> StateClientProtocol {
|
||||
state() as StateClient
|
||||
}
|
||||
|
||||
func userFingerprint(material fingerprintMaterial: String) throws -> String {
|
||||
try userFingerprint(fingerprintMaterial: fingerprintMaterial)
|
||||
}
|
||||
|
||||
@ -92,6 +92,7 @@ class MockClientCiphers: CiphersClientProtocol {
|
||||
}
|
||||
|
||||
var decryptFido2CredentialsResult = [BitwardenSdk.Fido2CredentialView]()
|
||||
var decryptListWithFailuresResult: DecryptCipherListResult?
|
||||
var encryptCipherResult: Result<EncryptionContext, Error>?
|
||||
var encryptError: Error?
|
||||
var encryptedCiphers = [CipherView]()
|
||||
@ -115,6 +116,13 @@ class MockClientCiphers: CiphersClientProtocol {
|
||||
ciphers.map(CipherListView.init)
|
||||
}
|
||||
|
||||
func decryptListWithFailures(ciphers: [Cipher]) -> DecryptCipherListResult {
|
||||
decryptListWithFailuresResult ?? DecryptCipherListResult(
|
||||
successes: ciphers.map(CipherListView.init),
|
||||
failures: []
|
||||
)
|
||||
}
|
||||
|
||||
func encrypt(cipherView: CipherView) throws -> BitwardenSdk.EncryptionContext {
|
||||
encryptedCiphers.append(cipherView)
|
||||
if let encryptError {
|
||||
|
||||
@ -123,7 +123,7 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/bitwarden/sdk-swift",
|
||||
"state" : {
|
||||
"revision" : "0c3baf9d372cd941146616a3d842b78c96a1170f"
|
||||
"revision" : "33a7bb7334aeea402256633cbbffe7fb03501e40"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -8,6 +8,7 @@ class MockPlatformClientService: PlatformClientService {
|
||||
var fingerprintResult: Result<String, Error> = .success("a-fingerprint-phrase-string-placeholder")
|
||||
var featureFlags: [String: Bool] = [:]
|
||||
var loadFlagsError: Error?
|
||||
var stateMock = MockStateClient()
|
||||
var userFingerprintCalled = false
|
||||
|
||||
func fido2() -> ClientFido2Service {
|
||||
@ -25,6 +26,10 @@ class MockPlatformClientService: PlatformClientService {
|
||||
featureFlags = flags
|
||||
}
|
||||
|
||||
func state() -> StateClientProtocol {
|
||||
stateMock
|
||||
}
|
||||
|
||||
func userFingerprint(material fingerprintMaterial: String) throws -> String {
|
||||
fingerprintMaterialString = fingerprintMaterial
|
||||
userFingerprintCalled = true
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import BitwardenSdk
|
||||
|
||||
@testable import BitwardenShared
|
||||
|
||||
final class MockStateClient: StateClientProtocol {
|
||||
var registerCipherRepositoryReceivedStore: CipherRepository?
|
||||
|
||||
func registerCipherRepository(store: CipherRepository) {
|
||||
registerCipherRepositoryReceivedStore = store
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,9 @@ class ClientBuilderTests: BitwardenTestCase {
|
||||
|
||||
errorReporter = MockErrorReporter()
|
||||
mockPlatform = MockPlatformClientService()
|
||||
subject = DefaultClientBuilder(errorReporter: errorReporter)
|
||||
subject = DefaultClientBuilder(
|
||||
errorReporter: errorReporter
|
||||
)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@ -30,11 +32,11 @@ class ClientBuilderTests: BitwardenTestCase {
|
||||
|
||||
// MARK: Tests
|
||||
|
||||
/// `buildClient()` creates a client and loads feature flags.
|
||||
/// `buildClient(for:)` creates a client and loads feature flags.
|
||||
func test_buildClient() {
|
||||
let client = subject.buildClient()
|
||||
let builtClient = subject.buildClient()
|
||||
|
||||
XCTAssertNotNil(client)
|
||||
XCTAssertNotNil(builtClient)
|
||||
XCTAssertNotNil(mockPlatform.featureFlags)
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +144,9 @@ actor DefaultClientService: ClientService {
|
||||
/// The service used by the application to report non-fatal errors.
|
||||
private let errorReporter: ErrorReporter
|
||||
|
||||
/// The factory to create SDK repositories.
|
||||
private let sdkRepositoryFactory: SdkRepositoryFactory
|
||||
|
||||
/// Basic client behavior settings.
|
||||
private let settings: ClientSettings?
|
||||
|
||||
@ -161,6 +164,7 @@ actor DefaultClientService: ClientService {
|
||||
/// - clientBuilder: A helper object that builds a Bitwarden SDK `Client`.
|
||||
/// - configService: The service to get server-specified configuration.
|
||||
/// - errorReporter: The service used by the application to report non-fatal errors.
|
||||
/// - sdkRepositoryFactory: The factory to create SDK repositories.
|
||||
/// - settings: The settings to apply to the client. Defaults to `nil`.
|
||||
/// - stateService: The service used by the application to manage account state.
|
||||
///
|
||||
@ -168,12 +172,14 @@ actor DefaultClientService: ClientService {
|
||||
clientBuilder: ClientBuilder,
|
||||
configService: ConfigService,
|
||||
errorReporter: ErrorReporter,
|
||||
sdkRepositoryFactory: SdkRepositoryFactory,
|
||||
settings: ClientSettings? = nil,
|
||||
stateService: StateService
|
||||
) {
|
||||
self.clientBuilder = clientBuilder
|
||||
self.configService = configService
|
||||
self.errorReporter = errorReporter
|
||||
self.sdkRepositoryFactory = sdkRepositoryFactory
|
||||
self.settings = settings
|
||||
self.stateService = stateService
|
||||
|
||||
@ -254,9 +260,7 @@ actor DefaultClientService: ClientService {
|
||||
// If not, create one, map it to the user, then return it.
|
||||
let newClient = await createAndMapClient(for: userId)
|
||||
|
||||
// Get the current config and load the flags.
|
||||
let config = await configService.getConfig()
|
||||
await loadFlags(config, for: newClient)
|
||||
await configureNewClient(newClient, for: userId)
|
||||
|
||||
return newClient
|
||||
}
|
||||
@ -275,6 +279,20 @@ actor DefaultClientService: ClientService {
|
||||
return clientBuilder.buildClient()
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures a new SDK client.
|
||||
/// - Parameters:
|
||||
/// - client: The SDK client to configure.
|
||||
/// - userId: The user ID the SDK client instance belongs to.
|
||||
func configureNewClient(_ client: BitwardenSdkClient, for userId: String) async {
|
||||
client.platform().state().registerCipherRepository(
|
||||
store: sdkRepositoryFactory.makeCipherRepository(userId: userId)
|
||||
)
|
||||
|
||||
// Get the current config and load the flags.
|
||||
let config = await configService.getConfig()
|
||||
await loadFlags(config, for: client)
|
||||
}
|
||||
|
||||
/// Creates a new client and maps it to an ID.
|
||||
///
|
||||
@ -315,7 +333,6 @@ actor DefaultClientService: ClientService {
|
||||
///
|
||||
protocol ClientBuilder {
|
||||
/// Creates a `BitwardenSdkClient`.
|
||||
///
|
||||
/// - Returns: A new `BitwardenSdkClient`.
|
||||
///
|
||||
func buildClient() -> BitwardenSdkClient
|
||||
@ -341,8 +358,10 @@ class DefaultClientBuilder: ClientBuilder {
|
||||
/// - Parameters:
|
||||
/// - errorReporter: The service used by the application to report non-fatal errors.
|
||||
/// - settings: The settings applied to the client.
|
||||
///
|
||||
init(errorReporter: ErrorReporter, settings: ClientSettings? = nil) {
|
||||
init(
|
||||
errorReporter: ErrorReporter,
|
||||
settings: ClientSettings? = nil
|
||||
) {
|
||||
self.errorReporter = errorReporter
|
||||
self.settings = settings
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ final class ClientServiceTests: BitwardenTestCase { // swiftlint:disable:this ty
|
||||
var clientBuilder: MockClientBuilder!
|
||||
var configService: MockConfigService!
|
||||
var errorReporter: MockErrorReporter!
|
||||
var sdkRepositoryFactory: MockSdkRepositoryFactory!
|
||||
var stateService: MockStateService!
|
||||
var subject: DefaultClientService!
|
||||
var vaultTimeoutService: MockVaultTimeoutService!
|
||||
@ -24,11 +25,14 @@ final class ClientServiceTests: BitwardenTestCase { // swiftlint:disable:this ty
|
||||
clientBuilder = MockClientBuilder()
|
||||
configService = MockConfigService()
|
||||
errorReporter = MockErrorReporter()
|
||||
sdkRepositoryFactory = MockSdkRepositoryFactory()
|
||||
sdkRepositoryFactory.makeCipherRepositoryUserIdReturnValue = MockSdkCipherRepository()
|
||||
stateService = MockStateService()
|
||||
subject = DefaultClientService(
|
||||
clientBuilder: clientBuilder,
|
||||
configService: configService,
|
||||
errorReporter: errorReporter,
|
||||
sdkRepositoryFactory: sdkRepositoryFactory,
|
||||
stateService: stateService
|
||||
)
|
||||
vaultTimeoutService = MockVaultTimeoutService()
|
||||
@ -40,6 +44,7 @@ final class ClientServiceTests: BitwardenTestCase { // swiftlint:disable:this ty
|
||||
clientBuilder = nil
|
||||
configService = nil
|
||||
errorReporter = nil
|
||||
sdkRepositoryFactory = nil
|
||||
stateService = nil
|
||||
subject = nil
|
||||
vaultTimeoutService = nil
|
||||
@ -215,6 +220,17 @@ final class ClientServiceTests: BitwardenTestCase { // swiftlint:disable:this ty
|
||||
)
|
||||
}
|
||||
|
||||
/// `client(for:)` registers the SDK cipher repository.
|
||||
func test_client_registersCipherRepository() async throws {
|
||||
stateService.activeAccount = .fixture(profile: .fixture(userId: "1"))
|
||||
|
||||
let auth = try await subject.auth()
|
||||
let client = try XCTUnwrap(clientBuilder.clients.first)
|
||||
XCTAssertIdentical(auth, client.authClient)
|
||||
XCTAssertTrue(sdkRepositoryFactory.makeCipherRepositoryUserIdCalled)
|
||||
XCTAssertNotNil(client.platformClient.stateMock.registerCipherRepositoryReceivedStore)
|
||||
}
|
||||
|
||||
/// `configPublisher` loads flags into the SDK.
|
||||
@MainActor
|
||||
func test_configPublisher_loadFlags() async throws {
|
||||
|
||||
@ -17,6 +17,9 @@ protocol PlatformClientService: AnyObject {
|
||||
/// - Parameter flags: Flags to load.
|
||||
func loadFlags(_ flags: [String: Bool]) throws
|
||||
|
||||
/// Returns an object to handle state.
|
||||
func state() -> StateClientProtocol
|
||||
|
||||
/// Fingerprint using logged in user's public key
|
||||
/// - Parameter material: Fingerprint material to use
|
||||
/// - Returns: User fingerprint
|
||||
@ -38,6 +41,10 @@ extension PlatformClient: PlatformClientService {
|
||||
try loadFlags(flags: flags)
|
||||
}
|
||||
|
||||
func state() -> StateClientProtocol {
|
||||
state() as StateClient
|
||||
}
|
||||
|
||||
func userFingerprint(material fingerprintMaterial: String) throws -> String {
|
||||
try userFingerprint(fingerprintMaterial: fingerprintMaterial)
|
||||
}
|
||||
|
||||
@ -455,11 +455,24 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le
|
||||
timeProvider: timeProvider
|
||||
)
|
||||
|
||||
let clientBuilder = DefaultClientBuilder(errorReporter: errorReporter)
|
||||
let cipherService = DefaultCipherService(
|
||||
cipherAPIService: apiService,
|
||||
cipherDataStore: dataStore,
|
||||
fileAPIService: apiService,
|
||||
stateService: stateService
|
||||
)
|
||||
|
||||
let clientBuilder = DefaultClientBuilder(
|
||||
errorReporter: errorReporter
|
||||
)
|
||||
let clientService = DefaultClientService(
|
||||
clientBuilder: clientBuilder,
|
||||
configService: configService,
|
||||
errorReporter: errorReporter,
|
||||
sdkRepositoryFactory: DefaultSdkRepositoryFactory(
|
||||
cipherDataStore: dataStore,
|
||||
errorReporter: errorReporter
|
||||
),
|
||||
stateService: stateService
|
||||
)
|
||||
|
||||
@ -495,13 +508,6 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le
|
||||
stateService: stateService
|
||||
)
|
||||
|
||||
let cipherService = DefaultCipherService(
|
||||
cipherAPIService: apiService,
|
||||
cipherDataStore: dataStore,
|
||||
fileAPIService: apiService,
|
||||
stateService: stateService
|
||||
)
|
||||
|
||||
let eventService = DefaultEventService(
|
||||
cipherService: cipherService,
|
||||
errorReporter: errorReporter,
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
|
||||
/// `CipherRepository` implementation to be used on SDK client-managed state.
|
||||
final class SdkCipherRepository: BitwardenSdk.CipherRepository {
|
||||
/// The data store for managing the persisted ciphers for the user.
|
||||
let cipherDataStore: CipherDataStore
|
||||
/// The service used by the application to report non-fatal errors.
|
||||
let errorReporter: ErrorReporter
|
||||
/// The user ID of the SDK instance this repository belongs to.
|
||||
let userId: String
|
||||
|
||||
/// Initializes a `SdkCipherRepository`.
|
||||
/// - Parameters:
|
||||
/// - cipherDataStore: The data store for managing the persisted ciphers for the user.
|
||||
/// - errorReporter: The service used by the application to report non-fatal errors.
|
||||
/// - userId: The user ID of the SDK instance this repository belongs to
|
||||
init(
|
||||
cipherDataStore: CipherDataStore,
|
||||
errorReporter: ErrorReporter,
|
||||
userId: String
|
||||
) {
|
||||
self.cipherDataStore = cipherDataStore
|
||||
self.errorReporter = errorReporter
|
||||
self.userId = userId
|
||||
}
|
||||
|
||||
func get(id: String) async throws -> BitwardenSdk.Cipher? {
|
||||
try await cipherDataStore.fetchCipher(withId: id, userId: userId)
|
||||
}
|
||||
|
||||
func has(id: String) async throws -> Bool {
|
||||
let cipher = try await cipherDataStore.fetchCipher(withId: id, userId: userId)
|
||||
return cipher != nil
|
||||
}
|
||||
|
||||
func list() async throws -> [BitwardenSdk.Cipher] {
|
||||
try await cipherDataStore.fetchAllCiphers(userId: userId)
|
||||
}
|
||||
|
||||
func remove(id: String) async throws {
|
||||
try await cipherDataStore.deleteCipher(id: id, userId: userId)
|
||||
}
|
||||
|
||||
func set(id: String, value: BitwardenSdk.Cipher) async throws {
|
||||
guard id == value.id else {
|
||||
throw BitwardenError.dataError("CipherRepository: Trying to update a cipher with mismatch IDs")
|
||||
}
|
||||
try await cipherDataStore.upsertCipher(value, userId: userId)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import BitwardenKitMocks
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
|
||||
// MARK: - SdkCipherRepositoryTests
|
||||
|
||||
class SdkCipherRepositoryTests: BitwardenTestCase {
|
||||
// MARK: Properties
|
||||
|
||||
var cipherDataStore: MockCipherDataStore!
|
||||
var errorReporter: MockErrorReporter!
|
||||
var subject: SdkCipherRepository!
|
||||
let expectedUserId = "1"
|
||||
|
||||
// MARK: Setup & Teardown
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
cipherDataStore = MockCipherDataStore()
|
||||
errorReporter = MockErrorReporter()
|
||||
subject = SdkCipherRepository(
|
||||
cipherDataStore: cipherDataStore,
|
||||
errorReporter: errorReporter,
|
||||
userId: expectedUserId
|
||||
)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
cipherDataStore = nil
|
||||
errorReporter = nil
|
||||
subject = nil
|
||||
}
|
||||
|
||||
// MARK: Tests
|
||||
|
||||
/// `get(id:)` fetches the cipher by its ID.
|
||||
func test_get() async throws {
|
||||
cipherDataStore.fetchCipherResult = .fixture(id: "1")
|
||||
let cipher = try await subject.get(id: "1")
|
||||
XCTAssertNotNil(cipher)
|
||||
XCTAssertEqual(cipher?.id, "1")
|
||||
XCTAssertEqual(cipherDataStore.fetchCipherUserId, expectedUserId)
|
||||
}
|
||||
|
||||
/// `has(id:)` returns whether there is a cipher with the ID.
|
||||
func test_has() async throws {
|
||||
cipherDataStore.fetchCipherResult = .fixture(id: "1")
|
||||
|
||||
let hasCipher1 = try await subject.has(id: "1")
|
||||
XCTAssertTrue(hasCipher1)
|
||||
XCTAssertEqual(cipherDataStore.fetchCipherUserId, expectedUserId)
|
||||
|
||||
cipherDataStore.fetchCipherResult = nil
|
||||
|
||||
let hasCipher2 = try await subject.has(id: "2")
|
||||
XCTAssertFalse(hasCipher2)
|
||||
}
|
||||
|
||||
/// `list()` returns a list of ciphers.
|
||||
func test_list() async throws {
|
||||
cipherDataStore.fetchAllCiphersResult = .success([.fixture(id: "1"), .fixture(id: "2")])
|
||||
|
||||
let ciphers = try await subject.list()
|
||||
XCTAssertEqual(ciphers.map(\.id), ["1", "2"])
|
||||
XCTAssertEqual(cipherDataStore.fetchAllCiphersUserId, expectedUserId)
|
||||
}
|
||||
|
||||
/// `remove(id:)` deletes the cipher from local storage by ID.
|
||||
func test_remove() async throws {
|
||||
try await subject.remove(id: "1")
|
||||
XCTAssertEqual(cipherDataStore.deleteCipherId, "1")
|
||||
XCTAssertEqual(cipherDataStore.deleteCipherUserId, expectedUserId)
|
||||
}
|
||||
|
||||
/// `set(id:value:)` updates the cipher with local storage.
|
||||
func test_set() async throws {
|
||||
try await subject.set(id: "1", value: .fixture(id: "1"))
|
||||
XCTAssertEqual(cipherDataStore.upsertCipherValue?.id, "1")
|
||||
XCTAssertEqual(cipherDataStore.upsertCipherUserId, expectedUserId)
|
||||
}
|
||||
|
||||
/// `set(id:value:)` doesn't update the cipher with local storage when the ID being passed
|
||||
/// doesn't match the ID of the `value` and throws an error.
|
||||
func test_set_nonMatchingIds() async throws {
|
||||
do {
|
||||
try await subject.set(id: "1", value: .fixture(id: "5"))
|
||||
} catch {
|
||||
XCTAssertEqual(
|
||||
(error as NSError).userInfo["ErrorMessage"] as? String,
|
||||
"CipherRepository: Trying to update a cipher with mismatch IDs"
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertNil(cipherDataStore.upsertCipherValue)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
|
||||
/// A factory to create SDK repositories.
|
||||
protocol SdkRepositoryFactory { // sourcery: AutoMockable
|
||||
/// Makes a `BitwardenSdk.CipherRepository` for the given `userId`.
|
||||
/// - Parameter userId: The user ID to use in the repository which belongs to the SDK instance
|
||||
/// the repository will be registered in.
|
||||
/// - Returns: The repository for the given `userId`.
|
||||
func makeCipherRepository(userId: String) -> BitwardenSdk.CipherRepository
|
||||
}
|
||||
|
||||
/// Default implementation of `SdkRepositoryFactory`.
|
||||
struct DefaultSdkRepositoryFactory: SdkRepositoryFactory {
|
||||
// MARK: Properties
|
||||
|
||||
/// The data store for managing the persisted ciphers for the user.
|
||||
private let cipherDataStore: CipherDataStore
|
||||
/// The service used by the application to report non-fatal errors.
|
||||
private let errorReporter: ErrorReporter
|
||||
|
||||
// MARK: Init
|
||||
|
||||
/// Initializes a `DefaultSdkRepositoryFactory`.
|
||||
/// - Parameters:
|
||||
/// - cipherDataStore: The data store for managing the persisted ciphers for the user.
|
||||
/// - errorReporter: The service used by the application to report non-fatal errors.
|
||||
init(cipherDataStore: CipherDataStore, errorReporter: ErrorReporter) {
|
||||
self.cipherDataStore = cipherDataStore
|
||||
self.errorReporter = errorReporter
|
||||
}
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
func makeCipherRepository(userId: String) -> BitwardenSdk.CipherRepository {
|
||||
SdkCipherRepository(
|
||||
cipherDataStore: cipherDataStore,
|
||||
errorReporter: errorReporter,
|
||||
userId: userId
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
import BitwardenKitMocks
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
|
||||
// MARK: - SdkRepositoryFactoryTests
|
||||
|
||||
class SdkRepositoryFactoryTests: BitwardenTestCase {
|
||||
// MARK: Properties
|
||||
|
||||
var cipherDataStore: MockCipherDataStore!
|
||||
var errorReporter: MockErrorReporter!
|
||||
var subject: SdkRepositoryFactory!
|
||||
|
||||
// MARK: Setup & Teardown
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
cipherDataStore = MockCipherDataStore()
|
||||
errorReporter = MockErrorReporter()
|
||||
subject = DefaultSdkRepositoryFactory(cipherDataStore: cipherDataStore, errorReporter: errorReporter)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
cipherDataStore = nil
|
||||
errorReporter = nil
|
||||
subject = nil
|
||||
}
|
||||
|
||||
// MARK: Tests
|
||||
|
||||
/// `makeCipherRepository(userId:)` makes a cipher repository for the given user ID.
|
||||
func test_makeCipherRepository() {
|
||||
let repository = subject.makeCipherRepository(userId: "1")
|
||||
XCTAssertTrue(repository is SdkCipherRepository)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import BitwardenKitMocks
|
||||
import BitwardenSdk
|
||||
|
||||
@testable import BitwardenShared
|
||||
|
||||
final class MockSdkCipherRepository: BitwardenSdk.CipherRepository {
|
||||
var getResult: Result<BitwardenSdk.Cipher, Error> = .success(.fixture())
|
||||
var hasResult: Result<Bool, Error> = .success(true)
|
||||
var listResult: Result<[BitwardenSdk.Cipher], Error> = .success([])
|
||||
var removeResult: Result<Void, Error> = .success(())
|
||||
var removeReceivedId: String?
|
||||
var setReceivedId: String?
|
||||
var setReceivedCipher: Cipher?
|
||||
var setResult: Result<Void, Error> = .success(())
|
||||
|
||||
func get(id: String) async throws -> BitwardenSdk.Cipher? {
|
||||
try getResult.get()
|
||||
}
|
||||
|
||||
func has(id: String) async throws -> Bool {
|
||||
try hasResult.get()
|
||||
}
|
||||
|
||||
func list() async throws -> [BitwardenSdk.Cipher] {
|
||||
try listResult.get()
|
||||
}
|
||||
|
||||
func remove(id: String) async throws {
|
||||
removeReceivedId = id
|
||||
try removeResult.get()
|
||||
}
|
||||
|
||||
func set(id: String, value: BitwardenSdk.Cipher) async throws {
|
||||
setReceivedId = id
|
||||
setReceivedCipher = value
|
||||
try setResult.get()
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,7 @@ class MockCipherDataStore: CipherDataStore {
|
||||
|
||||
var fetchCipherId: String?
|
||||
var fetchCipherResult: Cipher?
|
||||
var fetchCipherUserId: String?
|
||||
|
||||
var cipherSubjectByUserId: [String: CurrentValueSubject<[Cipher], Error>] = [:]
|
||||
|
||||
@ -45,8 +46,9 @@ class MockCipherDataStore: CipherDataStore {
|
||||
return try fetchAllCiphersResult.get()
|
||||
}
|
||||
|
||||
func fetchCipher(withId id: String, userId _: String) async -> Cipher? {
|
||||
func fetchCipher(withId id: String, userId: String) async -> Cipher? {
|
||||
fetchCipherId = id
|
||||
fetchCipherUserId = userId
|
||||
return fetchCipherResult
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +107,7 @@ class MockClientCiphers: CiphersClientProtocol {
|
||||
|
||||
var decryptFido2CredentialsResult = [BitwardenSdk.Fido2CredentialView]()
|
||||
var decryptListError: Error?
|
||||
var decryptListWithFailuresResult: DecryptCipherListResult?
|
||||
var decryptListErrorWhenCiphers: (([Cipher]) -> Error?)?
|
||||
var decryptListReceivedCiphersInvocations: [[Cipher]] = []
|
||||
var encryptCipherResult: Result<EncryptionContext, Error>?
|
||||
@ -139,6 +140,13 @@ class MockClientCiphers: CiphersClientProtocol {
|
||||
return ciphers.map(CipherListView.init)
|
||||
}
|
||||
|
||||
func decryptListWithFailures(ciphers: [Cipher]) -> DecryptCipherListResult {
|
||||
decryptListWithFailuresResult ?? DecryptCipherListResult(
|
||||
successes: ciphers.map(CipherListView.init),
|
||||
failures: []
|
||||
)
|
||||
}
|
||||
|
||||
func encrypt(cipherView: CipherView) throws -> EncryptionContext {
|
||||
encryptedCiphers.append(cipherView)
|
||||
if let encryptError {
|
||||
|
||||
@ -23,7 +23,7 @@ include:
|
||||
packages:
|
||||
BitwardenSdk:
|
||||
url: https://github.com/bitwarden/sdk-swift
|
||||
revision: 0c3baf9d372cd941146616a3d842b78c96a1170f
|
||||
revision: 33a7bb7334aeea402256633cbbffe7fb03501e40
|
||||
branch: unstable
|
||||
Firebase:
|
||||
url: https://github.com/firebase/firebase-ios-sdk
|
||||
|
||||
@ -23,7 +23,7 @@ include:
|
||||
packages:
|
||||
BitwardenSdk:
|
||||
url: https://github.com/bitwarden/sdk-swift
|
||||
revision: 0c3baf9d372cd941146616a3d842b78c96a1170f
|
||||
revision: 33a7bb7334aeea402256633cbbffe7fb03501e40
|
||||
branch: unstable
|
||||
Firebase:
|
||||
url: https://github.com/firebase/firebase-ios-sdk
|
||||
|
||||
@ -24,7 +24,7 @@ include:
|
||||
packages:
|
||||
BitwardenSdk:
|
||||
url: https://github.com/bitwarden/sdk-swift
|
||||
revision: 0c3baf9d372cd941146616a3d842b78c96a1170f
|
||||
revision: 33a7bb7334aeea402256633cbbffe7fb03501e40
|
||||
branch: unstable
|
||||
Firebase:
|
||||
url: https://github.com/firebase/firebase-ios-sdk
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user