diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt index a31ca79324..11055b0477 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSource.kt @@ -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?) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt index 0078f86db8..40c0921435 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceImpl.kt @@ -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( diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSource.kt index b2dd2f6355..7e66ebabfc 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSource.kt @@ -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 + + /** + * Generates a passphrase returning a [String] wrapped in a [Result]. + */ + suspend fun generatePassphrase(request: PassphraseGeneratorRequest): Result } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceImpl.kt index a962d72d15..13b00ba646 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceImpl.kt @@ -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 = runCatching { clientGenerator.password(request) } + + override suspend fun generatePassphrase( + request: PassphraseGeneratorRequest, + ): Result = runCatching { + clientGenerator.passphrase(request) + } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepository.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepository.kt index c75c338106..14f1483521 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepository.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepository.kt @@ -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) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryImpl.kt index d11fc7ea96..2928f25a8e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryImpl.kt @@ -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) } } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/GeneratedPassphraseResult.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/GeneratedPassphraseResult.kt new file mode 100644 index 0000000000..7bf3d63cbe --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/GeneratedPassphraseResult.kt @@ -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() +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasswordGenerationOptions.kt b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasscodeGenerationOptions.kt similarity index 65% rename from app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasswordGenerationOptions.kt rename to app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasscodeGenerationOptions.kt index 8715f04134..f83e3488fe 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasswordGenerationOptions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/tools/generator/repository/model/PasscodeGenerationOptions.kt @@ -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, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt index 830258a165..f571c4237a 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModel.kt @@ -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) { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt index d3439d0a29..539721a3f3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/disk/GeneratorDiskSourceTest.kt @@ -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) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceTest.kt index 102365261f..479172744d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/datasource/sdk/GeneratorSdkSourceTest.kt @@ -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) + } + } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt index 00215618e2..9c638eebe2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/GeneratorRepositoryTest.kt @@ -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( diff --git a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/util/FakeGeneratorRepository.kt b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/util/FakeGeneratorRepository.kt index 4e3b7c6ab6..0966d1dc62 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/util/FakeGeneratorRepository.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/tools/generator/repository/util/FakeGeneratorRepository.kt @@ -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 } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt index bbbc723c25..a992baa9b0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt @@ -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(), ) }