mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 10:48:47 +03:00
BIT-653: Adding data source for passphrase generation (#273)
This commit is contained in:
parent
3a07bbd3da
commit
1f337e94f0
14 changed files with 266 additions and 72 deletions
|
@ -1,6 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.disk
|
||||
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
|
||||
/**
|
||||
* Primary access point for disk information related to generation.
|
||||
|
@ -8,12 +8,12 @@ import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerat
|
|||
interface GeneratorDiskSource {
|
||||
|
||||
/**
|
||||
* Retrieves a user's password generation options using a [userId].
|
||||
* Retrieves a user's passcode generation options using a [userId].
|
||||
*/
|
||||
fun getPasswordGenerationOptions(userId: String): PasswordGenerationOptions?
|
||||
fun getPasscodeGenerationOptions(userId: String): PasscodeGenerationOptions?
|
||||
|
||||
/**
|
||||
* Stores a user's password generation options using a [userId].
|
||||
* Stores a user's passcode generation options using a [userId].
|
||||
*/
|
||||
fun storePasswordGenerationOptions(userId: String, options: PasswordGenerationOptions?)
|
||||
fun storePasscodeGenerationOptions(userId: String, options: PasscodeGenerationOptions?)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.tools.generator.datasource.disk
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
@ -17,14 +17,14 @@ class GeneratorDiskSourceImpl(
|
|||
) : BaseDiskSource(sharedPreferences),
|
||||
GeneratorDiskSource {
|
||||
|
||||
override fun getPasswordGenerationOptions(userId: String): PasswordGenerationOptions? {
|
||||
override fun getPasscodeGenerationOptions(userId: String): PasscodeGenerationOptions? {
|
||||
val key = getPasswordGenerationOptionsKey(userId)
|
||||
return getString(key)?.let { json.decodeFromString(it) }
|
||||
}
|
||||
|
||||
override fun storePasswordGenerationOptions(
|
||||
override fun storePasscodeGenerationOptions(
|
||||
userId: String,
|
||||
options: PasswordGenerationOptions?,
|
||||
options: PasscodeGenerationOptions?,
|
||||
) {
|
||||
val key = getPasswordGenerationOptionsKey(userId)
|
||||
putString(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
|
||||
/**
|
||||
|
@ -11,4 +12,9 @@ interface GeneratorSdkSource {
|
|||
* Generates a password returning a [String] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun generatePassword(request: PasswordGeneratorRequest): Result<String>
|
||||
|
||||
/**
|
||||
* Generates a passphrase returning a [String] wrapped in a [Result].
|
||||
*/
|
||||
suspend fun generatePassphrase(request: PassphraseGeneratorRequest): Result<String>
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.bitwarden.sdk.ClientGenerators
|
||||
|
||||
|
@ -17,4 +18,10 @@ class GeneratorSdkSourceImpl(
|
|||
): Result<String> = runCatching {
|
||||
clientGenerator.password(request)
|
||||
}
|
||||
|
||||
override suspend fun generatePassphrase(
|
||||
request: PassphraseGeneratorRequest,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.passphrase(request)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
|
||||
/**
|
||||
* Responsible for managing generator data.
|
||||
|
@ -17,12 +19,19 @@ interface GeneratorRepository {
|
|||
): GeneratedPasswordResult
|
||||
|
||||
/**
|
||||
* Get the [PasswordGenerationOptions] for the current user.
|
||||
* Attempt to generate a passphrase.
|
||||
*/
|
||||
fun getPasswordGenerationOptions(): PasswordGenerationOptions?
|
||||
suspend fun generatePassphrase(
|
||||
passphraseGeneratorRequest: PassphraseGeneratorRequest,
|
||||
): GeneratedPassphraseResult
|
||||
|
||||
/**
|
||||
* Save the [PasswordGenerationOptions] for the current user.
|
||||
* Get the [PasscodeGenerationOptions] for the current user.
|
||||
*/
|
||||
fun savePasswordGenerationOptions(options: PasswordGenerationOptions)
|
||||
fun getPasscodeGenerationOptions(): PasscodeGenerationOptions?
|
||||
|
||||
/**
|
||||
* Save the [PasscodeGenerationOptions] for the current user.
|
||||
*/
|
||||
fun savePasscodeGenerationOptions(options: PasscodeGenerationOptions)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
|
@ -28,13 +30,23 @@ class GeneratorRepositoryImpl constructor(
|
|||
onFailure = { GeneratedPasswordResult.InvalidRequest },
|
||||
)
|
||||
|
||||
override fun getPasswordGenerationOptions(): PasswordGenerationOptions? {
|
||||
override suspend fun generatePassphrase(
|
||||
passphraseGeneratorRequest: PassphraseGeneratorRequest,
|
||||
): GeneratedPassphraseResult =
|
||||
generatorSdkSource
|
||||
.generatePassphrase(passphraseGeneratorRequest)
|
||||
.fold(
|
||||
onSuccess = { GeneratedPassphraseResult.Success(it) },
|
||||
onFailure = { GeneratedPassphraseResult.InvalidRequest },
|
||||
)
|
||||
|
||||
override fun getPasscodeGenerationOptions(): PasscodeGenerationOptions? {
|
||||
val userId = authDiskSource.userState?.activeUserId
|
||||
return userId?.let { generatorDiskSource.getPasswordGenerationOptions(it) }
|
||||
return userId?.let { generatorDiskSource.getPasscodeGenerationOptions(it) }
|
||||
}
|
||||
|
||||
override fun savePasswordGenerationOptions(options: PasswordGenerationOptions) {
|
||||
override fun savePasscodeGenerationOptions(options: PasscodeGenerationOptions) {
|
||||
val userId = authDiskSource.userState?.activeUserId
|
||||
userId?.let { generatorDiskSource.storePasswordGenerationOptions(it, options) }
|
||||
userId?.let { generatorDiskSource.storePasscodeGenerationOptions(it, options) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository.model
|
||||
|
||||
/**
|
||||
* Represents the outcome of a generator operation.
|
||||
*/
|
||||
sealed class GeneratedPassphraseResult {
|
||||
/**
|
||||
* Operation succeeded with a value.
|
||||
*/
|
||||
data class Success(val generatedString: String) : GeneratedPassphraseResult()
|
||||
|
||||
/**
|
||||
* There was an error during the operation.
|
||||
*/
|
||||
data object InvalidRequest : GeneratedPassphraseResult()
|
||||
}
|
|
@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
|
|||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* A data class representing the configuration options for password generation.
|
||||
* A data class representing the configuration options for both password and passphrase generation.
|
||||
*
|
||||
* @property length The total length of the generated password.
|
||||
* @property allowAmbiguousChar Indicates whether ambiguous characters are allowed in the password.
|
||||
|
@ -16,9 +16,16 @@ import kotlinx.serialization.Serializable
|
|||
* @property minLowercase The minimum number of lowercase characters required in the password.
|
||||
* @property allowSpecial Indicates whether special characters are allowed in the password.
|
||||
* @property minSpecial The minimum number of special characters required in the password.
|
||||
* @property numWords The number of words in the generated passphrase.
|
||||
* @property wordSeparator The character used to separate words in the passphrase.
|
||||
* @property allowCapitalize Indicates whether to use capitals in the passphrase.
|
||||
* @property allowIncludeNumber Indicates whether to include numbers in the passphrase.
|
||||
*/
|
||||
@Serializable
|
||||
data class PasswordGenerationOptions(
|
||||
data class PasscodeGenerationOptions(
|
||||
|
||||
// Password-specific options
|
||||
|
||||
@SerialName("length")
|
||||
val length: Int,
|
||||
|
||||
|
@ -35,17 +42,31 @@ data class PasswordGenerationOptions(
|
|||
val hasUppercase: Boolean,
|
||||
|
||||
@SerialName("minUppercase")
|
||||
val minUppercase: Int?,
|
||||
val minUppercase: Int? = null,
|
||||
|
||||
@SerialName("lowercase")
|
||||
val hasLowercase: Boolean,
|
||||
|
||||
@SerialName("minLowercase")
|
||||
val minLowercase: Int?,
|
||||
val minLowercase: Int? = null,
|
||||
|
||||
@SerialName("special")
|
||||
val allowSpecial: Boolean,
|
||||
|
||||
@SerialName("minSpecial")
|
||||
val minSpecial: Int,
|
||||
|
||||
// Passphrase-specific options
|
||||
|
||||
@SerialName("numWords")
|
||||
val numWords: Int,
|
||||
|
||||
@SerialName("wordSeparator")
|
||||
val wordSeparator: String,
|
||||
|
||||
@SerialName("capitalize")
|
||||
val allowCapitalize: Boolean,
|
||||
|
||||
@SerialName("includeNumber")
|
||||
val allowIncludeNumber: Boolean,
|
||||
)
|
|
@ -9,7 +9,7 @@ import com.bitwarden.core.PasswordGeneratorRequest
|
|||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
|
@ -104,7 +104,7 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is Password -> {
|
||||
val options = generatorRepository.getPasswordGenerationOptions()
|
||||
val options = generatorRepository.getPasscodeGenerationOptions()
|
||||
val password = if (options != null) {
|
||||
Password(
|
||||
length = options.length,
|
||||
|
@ -134,19 +134,39 @@ class GeneratorViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun savePasswordOptionsToDisk(password: Password) {
|
||||
val options = PasswordGenerationOptions(
|
||||
val options = generatorRepository
|
||||
.getPasscodeGenerationOptions() ?: generatePasscodeDefaultOptions()
|
||||
val newOptions = options.copy(
|
||||
length = password.length,
|
||||
allowAmbiguousChar = password.avoidAmbiguousChars,
|
||||
hasNumbers = password.useNumbers,
|
||||
minNumber = password.minNumbers,
|
||||
hasUppercase = password.useCapitals,
|
||||
minUppercase = null,
|
||||
hasLowercase = password.useLowercase,
|
||||
minLowercase = null,
|
||||
allowSpecial = password.useSpecialChars,
|
||||
minSpecial = password.minSpecial,
|
||||
)
|
||||
generatorRepository.savePasswordGenerationOptions(options)
|
||||
generatorRepository.savePasscodeGenerationOptions(newOptions)
|
||||
}
|
||||
|
||||
private fun generatePasscodeDefaultOptions(): PasscodeGenerationOptions {
|
||||
val defaultPassword = Password()
|
||||
val defaultPassphrase = Passphrase()
|
||||
|
||||
return PasscodeGenerationOptions(
|
||||
length = defaultPassword.length,
|
||||
allowAmbiguousChar = defaultPassword.avoidAmbiguousChars,
|
||||
hasNumbers = defaultPassword.useNumbers,
|
||||
minNumber = defaultPassword.minNumbers,
|
||||
hasUppercase = defaultPassword.useCapitals,
|
||||
hasLowercase = defaultPassword.useLowercase,
|
||||
allowSpecial = defaultPassword.useSpecialChars,
|
||||
minSpecial = defaultPassword.minSpecial,
|
||||
allowCapitalize = defaultPassphrase.capitalize,
|
||||
allowIncludeNumber = defaultPassphrase.includeNumber,
|
||||
wordSeparator = defaultPassphrase.wordSeparator.toString(),
|
||||
numWords = defaultPassphrase.numWords,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun generatePassword(password: Password) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.disk
|
||||
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeSharedPreferences
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
@ -25,9 +25,9 @@ class GeneratorDiskSourceTest {
|
|||
)
|
||||
|
||||
@Test
|
||||
fun `getPasswordGenerationOptions should return correct options when available`() {
|
||||
fun `getPasscodeGenerationOptions should return correct options when available`() {
|
||||
val userId = "user123"
|
||||
val options = PasswordGenerationOptions(
|
||||
val options = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -38,29 +38,33 @@ class GeneratorDiskSourceTest {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
val key = "bwPreferencesStorage_passwordGenerationOptions_$userId"
|
||||
fakeSharedPreferences.edit().putString(key, json.encodeToString(options)).apply()
|
||||
|
||||
val result = generatorDiskSource.getPasswordGenerationOptions(userId)
|
||||
val result = generatorDiskSource.getPasscodeGenerationOptions(userId)
|
||||
|
||||
assertEquals(options, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPasswordGenerationOptions should return null when options are not available`() {
|
||||
fun `getPasscodeGenerationOptions should return null when options are not available`() {
|
||||
val userId = "user123"
|
||||
|
||||
val result = generatorDiskSource.getPasswordGenerationOptions(userId)
|
||||
val result = generatorDiskSource.getPasscodeGenerationOptions(userId)
|
||||
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `storePasswordGenerationOptions should correctly store options`() {
|
||||
fun `storePasscodeGenerationOptions should correctly store options`() {
|
||||
val userId = "user123"
|
||||
val options = PasswordGenerationOptions(
|
||||
val options = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -71,11 +75,15 @@ class GeneratorDiskSourceTest {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
val key = "bwPreferencesStorage_passwordGenerationOptions_$userId"
|
||||
|
||||
generatorDiskSource.storePasswordGenerationOptions(userId, options)
|
||||
generatorDiskSource.storePasscodeGenerationOptions(userId, options)
|
||||
|
||||
val storedValue = fakeSharedPreferences.getString(key, null)
|
||||
assertNotNull(storedValue)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.sdk
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.bitwarden.sdk.ClientGenerators
|
||||
import io.mockk.coEvery
|
||||
|
@ -42,4 +43,28 @@ class GeneratorSdkSourceTest {
|
|||
clientGenerators.password(request)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generatePassphrase should call SDK and return a Result with the generated passphrase`() = runBlocking {
|
||||
val request = PassphraseGeneratorRequest(
|
||||
numWords = 4.toUByte(),
|
||||
wordSeparator = "-",
|
||||
capitalize = true,
|
||||
includeNumber = true,
|
||||
)
|
||||
val expectedResult = "Generated-Passphrase123"
|
||||
|
||||
coEvery {
|
||||
clientGenerators.passphrase(request)
|
||||
} returns expectedResult
|
||||
|
||||
val result = generatorSdkSource.generatePassphrase(request)
|
||||
|
||||
assertEquals(Result.success(expectedResult), result)
|
||||
|
||||
coVerify {
|
||||
clientGenerators.passphrase(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
|
@ -12,8 +13,9 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserD
|
|||
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import io.mockk.Runs
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.coEvery
|
||||
|
@ -93,9 +95,49 @@ class GeneratorRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `getPasswordGenerationOptions should return options when available`() = runTest {
|
||||
fun `generatePassphrase should emit Success result with the generated passphrase`() = runTest {
|
||||
val request = PassphraseGeneratorRequest(
|
||||
numWords = 5.toUByte(),
|
||||
capitalize = true,
|
||||
includeNumber = true,
|
||||
wordSeparator = '-'.toString(),
|
||||
)
|
||||
val expectedResult = "Generated-Passphrase-123!"
|
||||
coEvery {
|
||||
generatorSdkSource.generatePassphrase(request)
|
||||
} returns Result.success(expectedResult)
|
||||
|
||||
val result = repository.generatePassphrase(request)
|
||||
|
||||
assertEquals(expectedResult, (result as GeneratedPassphraseResult.Success).generatedString)
|
||||
coVerify { generatorSdkSource.generatePassphrase(request) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `generatePassphrase should emit InvalidRequest result when SDK throws exception`() =
|
||||
runTest {
|
||||
val request = PassphraseGeneratorRequest(
|
||||
numWords = 5.toUByte(),
|
||||
capitalize = true,
|
||||
includeNumber = true,
|
||||
wordSeparator = '-'.toString(),
|
||||
)
|
||||
val exception = RuntimeException("An error occurred")
|
||||
coEvery { generatorSdkSource.generatePassphrase(request) } returns Result.failure(
|
||||
exception,
|
||||
)
|
||||
|
||||
val result = repository.generatePassphrase(request)
|
||||
|
||||
assertTrue(result is GeneratedPassphraseResult.InvalidRequest)
|
||||
coVerify { generatorSdkSource.generatePassphrase(request) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPasscodeGenerationOptions should return options when available`() = runTest {
|
||||
val userId = "activeUserId"
|
||||
val expectedOptions = PasswordGenerationOptions(
|
||||
val expectedOptions = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -106,47 +148,51 @@ class GeneratorRepositoryTest {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
coEvery { authDiskSource.userState } returns USER_STATE
|
||||
|
||||
coEvery {
|
||||
generatorDiskSource.getPasswordGenerationOptions(userId)
|
||||
generatorDiskSource.getPasscodeGenerationOptions(userId)
|
||||
} returns expectedOptions
|
||||
|
||||
val result = repository.getPasswordGenerationOptions()
|
||||
val result = repository.getPasscodeGenerationOptions()
|
||||
|
||||
assertEquals(expectedOptions, result)
|
||||
coVerify { generatorDiskSource.getPasswordGenerationOptions(userId) }
|
||||
coVerify { generatorDiskSource.getPasscodeGenerationOptions(userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPasswordGenerationOptions should return null when there is no active user`() = runTest {
|
||||
fun `getPasscodeGenerationOptions should return null when there is no active user`() = runTest {
|
||||
coEvery { authDiskSource.userState } returns null
|
||||
|
||||
val result = repository.getPasswordGenerationOptions()
|
||||
val result = repository.getPasscodeGenerationOptions()
|
||||
|
||||
assertNull(result)
|
||||
coVerify(exactly = 0) { generatorDiskSource.getPasswordGenerationOptions(any()) }
|
||||
coVerify(exactly = 0) { generatorDiskSource.getPasscodeGenerationOptions(any()) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getPasswordGenerationOptions should return null when no data is stored for active user`() = runTest {
|
||||
fun `getPasscodeGenerationOptions should return null when no data is stored for active user`() = runTest {
|
||||
val userId = "activeUserId"
|
||||
coEvery { authDiskSource.userState } returns USER_STATE
|
||||
coEvery { generatorDiskSource.getPasswordGenerationOptions(userId) } returns null
|
||||
coEvery { generatorDiskSource.getPasscodeGenerationOptions(userId) } returns null
|
||||
|
||||
val result = repository.getPasswordGenerationOptions()
|
||||
val result = repository.getPasscodeGenerationOptions()
|
||||
|
||||
assertNull(result)
|
||||
coVerify { generatorDiskSource.getPasswordGenerationOptions(userId) }
|
||||
coVerify { generatorDiskSource.getPasscodeGenerationOptions(userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `savePasswordGenerationOptions should store options correctly`() = runTest {
|
||||
fun `savePasscodeGenerationOptions should store options correctly`() = runTest {
|
||||
val userId = "activeUserId"
|
||||
val optionsToSave = PasswordGenerationOptions(
|
||||
val optionsToSave = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -157,23 +203,27 @@ class GeneratorRepositoryTest {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
coEvery { authDiskSource.userState } returns USER_STATE
|
||||
|
||||
coEvery {
|
||||
generatorDiskSource.storePasswordGenerationOptions(userId, optionsToSave)
|
||||
generatorDiskSource.storePasscodeGenerationOptions(userId, optionsToSave)
|
||||
} just Runs
|
||||
|
||||
repository.savePasswordGenerationOptions(optionsToSave)
|
||||
repository.savePasscodeGenerationOptions(optionsToSave)
|
||||
|
||||
coVerify { generatorDiskSource.storePasswordGenerationOptions(userId, optionsToSave) }
|
||||
coVerify { generatorDiskSource.storePasscodeGenerationOptions(userId, optionsToSave) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `savePasswordGenerationOptions should not store options when there is no active user`() = runTest {
|
||||
val optionsToSave = PasswordGenerationOptions(
|
||||
fun `savePasscodeGenerationOptions should not store options when there is no active user`() = runTest {
|
||||
val optionsToSave = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -184,13 +234,17 @@ class GeneratorRepositoryTest {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
coEvery { authDiskSource.userState } returns null
|
||||
|
||||
repository.savePasswordGenerationOptions(optionsToSave)
|
||||
repository.savePasscodeGenerationOptions(optionsToSave)
|
||||
|
||||
coVerify(exactly = 0) { generatorDiskSource.storePasswordGenerationOptions(any(), any()) }
|
||||
coVerify(exactly = 0) { generatorDiskSource.storePasscodeGenerationOptions(any(), any()) }
|
||||
}
|
||||
|
||||
private val USER_STATE = UserStateJson(
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.repository.util
|
||||
|
||||
import com.bitwarden.core.PassphraseGeneratorRequest
|
||||
import com.bitwarden.core.PasswordGeneratorRequest
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPassphraseResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
|
||||
/**
|
||||
* A fake implementation of [GeneratorRepository] for testing purposes.
|
||||
|
@ -13,7 +15,11 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
private var generatePasswordResult: GeneratedPasswordResult = GeneratedPasswordResult.Success(
|
||||
generatedString = "updatedText",
|
||||
)
|
||||
private var passwordGenerationOptions: PasswordGenerationOptions? = null
|
||||
private var generatePassphraseResult: GeneratedPassphraseResult =
|
||||
GeneratedPassphraseResult.Success(
|
||||
generatedString = "updatedPassphrase",
|
||||
)
|
||||
private var passcodeGenerationOptions: PasscodeGenerationOptions? = null
|
||||
|
||||
override suspend fun generatePassword(
|
||||
passwordGeneratorRequest: PasswordGeneratorRequest,
|
||||
|
@ -21,12 +27,18 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
return generatePasswordResult
|
||||
}
|
||||
|
||||
override fun getPasswordGenerationOptions(): PasswordGenerationOptions? {
|
||||
return passwordGenerationOptions
|
||||
override suspend fun generatePassphrase(
|
||||
passphraseGeneratorRequest: PassphraseGeneratorRequest,
|
||||
): GeneratedPassphraseResult {
|
||||
return generatePassphraseResult
|
||||
}
|
||||
|
||||
override fun savePasswordGenerationOptions(options: PasswordGenerationOptions) {
|
||||
passwordGenerationOptions = options
|
||||
override fun getPasscodeGenerationOptions(): PasscodeGenerationOptions? {
|
||||
return passcodeGenerationOptions
|
||||
}
|
||||
|
||||
override fun savePasscodeGenerationOptions(options: PasscodeGenerationOptions) {
|
||||
passcodeGenerationOptions = options
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,9 +49,9 @@ class FakeGeneratorRepository : GeneratorRepository {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the mock password generation options.
|
||||
* Sets the mock result for the generatePassphrase function.
|
||||
*/
|
||||
fun setMockGeneratePasswordGenerationOptions(options: PasswordGenerationOptions?) {
|
||||
passwordGenerationOptions = options
|
||||
fun setMockGeneratePassphraseResult(result: GeneratedPassphraseResult) {
|
||||
generatePassphraseResult = result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import androidx.lifecycle.SavedStateHandle
|
|||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.GeneratedPasswordResult
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasswordGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.model.PasscodeGenerationOptions
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.util.FakeGeneratorRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
|
@ -48,7 +48,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
val viewModel = createViewModel()
|
||||
val initialState = viewModel.stateFlow.value
|
||||
|
||||
val updatedPasswordOptions = PasswordGenerationOptions(
|
||||
val updatedPasswordOptions = PasscodeGenerationOptions(
|
||||
length = 14,
|
||||
allowAmbiguousChar = false,
|
||||
hasNumbers = true,
|
||||
|
@ -59,6 +59,10 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
minLowercase = null,
|
||||
allowSpecial = false,
|
||||
minSpecial = 1,
|
||||
allowCapitalize = false,
|
||||
allowIncludeNumber = false,
|
||||
wordSeparator = "-",
|
||||
numWords = 3,
|
||||
)
|
||||
|
||||
fakeGeneratorRepository.setMockGeneratePasswordResult(
|
||||
|
@ -72,7 +76,7 @@ class GeneratorViewModelTest : BaseViewModelTest() {
|
|||
|
||||
assertEquals(
|
||||
updatedPasswordOptions,
|
||||
fakeGeneratorRepository.getPasswordGenerationOptions(),
|
||||
fakeGeneratorRepository.getPasscodeGenerationOptions(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue