Add comprehensive tests for Import Parsers and UuidManager (#6423)

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Honkonen
2026-01-28 16:55:06 -05:00
committed by GitHub
parent 3d974d710c
commit 254b2cd25b
14 changed files with 4598 additions and 20 deletions

View File

@@ -21,6 +21,8 @@ import com.bitwarden.authenticator.data.platform.manager.imports.ImportManager
import com.bitwarden.authenticator.data.platform.manager.imports.ImportManagerImpl
import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepository
import com.bitwarden.authenticator.data.platform.repository.SettingsRepository
import com.bitwarden.core.data.manager.UuidManager
import com.bitwarden.core.data.manager.UuidManagerImpl
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
import com.bitwarden.core.data.manager.dispatcher.DispatcherManagerImpl
import com.bitwarden.core.data.manager.realtime.RealtimeManager
@@ -93,12 +95,20 @@ object PlatformManagerModule {
@Singleton
fun provideImportManager(
authenticatorDiskSource: AuthenticatorDiskSource,
): ImportManager = ImportManagerImpl(authenticatorDiskSource)
uuidManager: UuidManager,
): ImportManager = ImportManagerImpl(
authenticatorDiskSource = authenticatorDiskSource,
uuidManager = uuidManager,
)
@Provides
@Singleton
fun provideEncodingManager(): BitwardenEncodingManager = BitwardenEncodingManagerImpl()
@Provides
@Singleton
fun provideUuidManager(): UuidManager = UuidManagerImpl()
@Provides
@Singleton
fun providesFeatureFlagManager(

View File

@@ -9,6 +9,7 @@ import com.bitwarden.authenticator.data.platform.manager.imports.parsers.Bitward
import com.bitwarden.authenticator.data.platform.manager.imports.parsers.ExportParser
import com.bitwarden.authenticator.data.platform.manager.imports.parsers.LastPassExportParser
import com.bitwarden.authenticator.data.platform.manager.imports.parsers.TwoFasExportParser
import com.bitwarden.core.data.manager.UuidManager
/**
* Default implementation of [ImportManager] for managing importing files exported by various
@@ -16,6 +17,7 @@ import com.bitwarden.authenticator.data.platform.manager.imports.parsers.TwoFasE
*/
class ImportManagerImpl(
private val authenticatorDiskSource: AuthenticatorDiskSource,
private val uuidManager: UuidManager,
) : ImportManager {
override suspend fun import(
importFileFormat: ImportFileFormat,
@@ -29,9 +31,9 @@ class ImportManagerImpl(
importFileFormat: ImportFileFormat,
): ExportParser = when (importFileFormat) {
ImportFileFormat.BITWARDEN_JSON -> BitwardenExportParser(importFileFormat)
ImportFileFormat.TWO_FAS_JSON -> TwoFasExportParser()
ImportFileFormat.LAST_PASS_JSON -> LastPassExportParser()
ImportFileFormat.AEGIS -> AegisExportParser()
ImportFileFormat.TWO_FAS_JSON -> TwoFasExportParser(uuidManager)
ImportFileFormat.LAST_PASS_JSON -> LastPassExportParser(uuidManager)
ImportFileFormat.AEGIS -> AegisExportParser(uuidManager)
}
private suspend fun processParseResult(

View File

@@ -5,16 +5,18 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType
import com.bitwarden.authenticator.data.platform.manager.imports.model.AegisJsonExport
import com.bitwarden.authenticator.data.platform.manager.imports.model.ExportParseResult
import com.bitwarden.core.data.manager.UuidManager
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.ByteArrayInputStream
import java.util.UUID
/**
* Implementation of [ExportParser] responsible for parsing exports from the Aegis application.
*/
class AegisExportParser : ExportParser() {
class AegisExportParser(
private val uuidManager: UuidManager,
) : ExportParser() {
@OptIn(ExperimentalSerializationApi::class)
override fun parse(byteArray: ByteArray): ExportParseResult {
val importJson = Json {
@@ -49,7 +51,7 @@ class AegisExportParser : ExportParser() {
val issuer = issuer
.takeUnless { it.isEmpty() }
// If issuer is not provided we fallback to the account name.
// If issuer is not provided we fall back to the account name.
?: name
.split(":")
.first()
@@ -60,7 +62,7 @@ class AegisExportParser : ExportParser() {
.takeUnless { it == issuer }
return AuthenticatorItemEntity(
id = UUID.randomUUID().toString(),
id = uuidManager.generateUuid(),
key = info.secret,
type = type,
algorithm = algorithmEnum,

View File

@@ -5,17 +5,19 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType
import com.bitwarden.authenticator.data.platform.manager.imports.model.ExportParseResult
import com.bitwarden.authenticator.data.platform.manager.imports.model.LastPassJsonExport
import com.bitwarden.core.data.manager.UuidManager
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.ByteArrayInputStream
import java.util.UUID
/**
* An [ExportParser] responsible for transforming LastPass export files into Bitwarden Authenticator
* items.
*/
class LastPassExportParser : ExportParser() {
class LastPassExportParser(
private val uuidManager: UuidManager,
) : ExportParser() {
@OptIn(ExperimentalSerializationApi::class)
override fun parse(byteArray: ByteArray): ExportParseResult {
@@ -49,7 +51,7 @@ class LastPassExportParser : ExportParser() {
?: throw IllegalArgumentException("Unsupported algorithm.")
return AuthenticatorItemEntity(
id = UUID.randomUUID().toString(),
id = uuidManager.generateUuid(),
key = secret,
type = type,
algorithm = algorithmEnum,

View File

@@ -5,13 +5,13 @@ import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.Aut
import com.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType
import com.bitwarden.authenticator.data.platform.manager.imports.model.ExportParseResult
import com.bitwarden.authenticator.data.platform.manager.imports.model.TwoFasJsonExport
import com.bitwarden.core.data.manager.UuidManager
import com.bitwarden.ui.platform.resource.BitwardenString
import com.bitwarden.ui.util.asText
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.ByteArrayInputStream
import java.util.UUID
private const val TOKEN_TYPE_HOTP = "HOTP"
@@ -19,7 +19,9 @@ private const val TOKEN_TYPE_HOTP = "HOTP"
* An [ExportParser] responsible for transforming 2FAS export files into Bitwarden Authenticator
* items.
*/
class TwoFasExportParser : ExportParser() {
class TwoFasExportParser(
private val uuidManager: UuidManager,
) : ExportParser() {
override fun parse(byteArray: ByteArray): ExportParseResult {
return import2fasJson(byteArray)
}
@@ -74,11 +76,11 @@ class TwoFasExportParser : ExportParser() {
entry.name.equals(other = algorithm, ignoreCase = true)
}
}
// Default to SHA1 if not specified
// Default to SHA1 if not specified
?: AuthenticatorItemAlgorithm.SHA1
return AuthenticatorItemEntity(
id = UUID.randomUUID().toString(),
id = uuidManager.generateUuid(),
key = secret,
type = type,
algorithm = algorithm,

View File

@@ -6,6 +6,7 @@ import com.bitwarden.authenticator.data.platform.manager.imports.model.ExportPar
import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportDataResult
import com.bitwarden.authenticator.data.platform.manager.imports.model.ImportFileFormat
import com.bitwarden.authenticator.data.platform.manager.imports.parsers.BitwardenExportParser
import com.bitwarden.core.data.manager.UuidManager
import com.bitwarden.ui.util.asText
import io.mockk.coEvery
import io.mockk.coVerify
@@ -23,14 +24,17 @@ import org.junit.jupiter.api.Test
class ImportManagerTest {
private val mockAuthenticatorDiskSource = mockk<AuthenticatorDiskSource>()
private val mockUuidManager = mockk<UuidManager>()
private val manager = ImportManagerImpl(
authenticatorDiskSource = mockAuthenticatorDiskSource,
uuidManager = mockUuidManager,
)
@BeforeEach
fun setup() {
mockkConstructor(BitwardenExportParser::class)
every { mockUuidManager.generateUuid() } returns "test-uuid-1"
}
@AfterEach

View File

@@ -94,9 +94,12 @@ class DebugLaunchManagerTest {
val eventTimeMillis = 100L
assertFalse(actionHasBeenCalled)
debugLaunchManager.actionOnInputEvent(event = mockMotionEvent, action = action)
debugLaunchManager.actionOnInputEvent(event = mockMotionEvent.apply {
every { eventTime } returns eventTimeMillis
}, action = action)
debugLaunchManager.actionOnInputEvent(
event = mockMotionEvent.apply {
every { eventTime } returns eventTimeMillis
},
action = action,
)
val result = debugLaunchManager.actionOnInputEvent(
event = mockMotionEvent.apply {
every { eventTime } returns eventTimeMillis + 501

View File

@@ -13,13 +13,13 @@ import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.update
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
class RootNavScreenTest : AuthenticatorComposeTest() {

View File

@@ -0,0 +1,13 @@
package com.bitwarden.core.data.manager
/**
* Manager for generating unique identifiers.
*/
interface UuidManager {
/**
* Generates a random UUID string.
*
* @return A string representation of a randomly generated UUID.
*/
fun generateUuid(): String
}

View File

@@ -0,0 +1,12 @@
package com.bitwarden.core.data.manager
import com.bitwarden.annotation.OmitFromCoverage
import java.util.UUID
/**
* Default implementation of [UuidManager] that generates random UUIDs.
*/
@OmitFromCoverage
class UuidManagerImpl : UuidManager {
override fun generateUuid(): String = UUID.randomUUID().toString()
}