Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions face/capture/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation(libs.androidX.cameraX.view)
implementation(libs.androidX.ui.preference)
implementation(libs.workManager.work)
implementation(libs.kotlin.serialization)

implementation(libs.circleImageView)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import com.simprints.face.infra.basebiosdk.detection.FaceDetector
import com.simprints.face.infra.biosdkresolver.ResolveFaceBioSdkUseCase
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.config.store.models.FaceConfiguration
import com.simprints.infra.config.store.models.experimental
import com.simprints.testtools.common.coroutines.TestCoroutineRule
import com.simprints.testtools.common.livedata.testObserver
import io.mockk.*
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -74,8 +74,8 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
} returns QUALITY_THRESHOLD
every { isUsingAutoCapture.invoke(any()) } returns true
coEvery {
configRepository.getProjectConfiguration().experimental().singleQualityFallbackRequired
} returns false
configRepository.getProjectConfiguration().custom
} returns mapOf("singleQualityFallbackRequired" to JsonPrimitive(false))
every { timeHelper.now() } returnsMany (0..100L).map { Timestamp(it) }
justRun { previewFrame.recycle() }
val resolveFaceBioSdkUseCase = mockk<ResolveFaceBioSdkUseCase> {
Expand Down Expand Up @@ -294,8 +294,8 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
val badQuality: Face = getFace(quality = -2f)

coEvery {
configRepository.getProjectConfiguration().experimental().singleQualityFallbackRequired
} returns true
configRepository.getProjectConfiguration().custom
} returns mapOf("singleQualityFallbackRequired" to JsonPrimitive(true))

every { faceDetector.analyze(frame) } returnsMany listOf(
badQuality, // not a fallback image due to bad quality
Expand Down Expand Up @@ -325,11 +325,9 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
fun `Use default imaging duration when not configured`() = runTest {
coEvery { faceDetector.analyze(frame) } returns getFace()
coEvery {
configRepository
.getProjectConfiguration()
.experimental()
.faceAutoCaptureImagingDurationMillis
} returns AUTO_CAPTURE_IMAGING_DURATION_MS
configRepository.getProjectConfiguration().custom
} returns mapOf("faceAutoCaptureImagingDurationMillis" to JsonPrimitive(AUTO_CAPTURE_IMAGING_DURATION_MS))

val capturingState = viewModel.capturingState.testObserver()

viewModel.initAutoCapture()
Expand All @@ -351,12 +349,12 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
fun `Use custom imaging duration when provided in config`() = runTest {
val configDuration = 5000L
coEvery { faceDetector.analyze(frame) } returns getFace()
coEvery {
configRepository
.getProjectConfiguration()
.experimental()
.faceAutoCaptureImagingDurationMillis
} returns configDuration
coEvery { configRepository.getProjectConfiguration().custom } returns
mapOf(
"faceAutoCaptureImagingDurationMillis" to JsonPrimitive(configDuration.toInt()),
"singleQualityFallbackRequired" to JsonPrimitive(false),
)

val capturingState = viewModel.capturingState.testObserver()

viewModel.initAutoCapture()
Expand All @@ -367,8 +365,8 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
Truth
.assertThat(capturingState.observedValues.last())
.isEqualTo(LiveFeedbackFragmentViewModel.CapturingState.CAPTURING)

advanceTimeBy(configDuration / 2)
// Add 1ms to account for json deserialization delay
Comment thread
meladRaouf marked this conversation as resolved.
advanceTimeBy((configDuration / 2) + 1)
Truth
.assertThat(capturingState.observedValues.last())
.isEqualTo(LiveFeedbackFragmentViewModel.CapturingState.FINISHED)
Expand All @@ -386,7 +384,7 @@ internal class LiveFeedbackAutoCaptureFragmentViewModelTest {
viewModel.initCapture(FaceConfiguration.BioSdk.SIM_FACE, samplesToKeep, 0)
viewModel.process(frame) // won't be observed during the preparation phase
viewModel.startCapture()
(1..100).forEach {
repeat(100) {
viewModel.process(frame)
}
advanceTimeBy(AUTO_CAPTURE_IMAGING_DURATION_MS + 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import com.simprints.face.infra.basebiosdk.detection.FaceDetector
import com.simprints.face.infra.biosdkresolver.ResolveFaceBioSdkUseCase
import com.simprints.infra.config.store.ConfigRepository
import com.simprints.infra.config.store.models.FaceConfiguration
import com.simprints.infra.config.store.models.experimental
import com.simprints.testtools.common.coroutines.TestCoroutineRule
import com.simprints.testtools.common.livedata.testObserver
import io.mockk.*
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -69,7 +69,8 @@ internal class LiveFeedbackFragmentViewModelTest {
?.qualityThreshold
} returns QUALITY_THRESHOLD
every { isUsingAutoCapture.invoke(any()) } returns false
coEvery { configRepository.getProjectConfiguration().experimental().singleQualityFallbackRequired } returns false
coEvery { configRepository.getProjectConfiguration().custom } returns
mapOf("singleQualityFallbackRequired" to JsonPrimitive(false))
every { timeHelper.now() } returnsMany (0..100L).map { Timestamp(it) }
justRun { previewFrame.recycle() }
val resolveFaceBioSdkUseCase = mockk<ResolveFaceBioSdkUseCase> {
Expand Down Expand Up @@ -163,7 +164,8 @@ internal class LiveFeedbackFragmentViewModelTest {
val validFace: Face = getFace()
val badQuality: Face = getFace(quality = -2f)

coEvery { configRepository.getProjectConfiguration().experimental().singleQualityFallbackRequired } returns true
coEvery { configRepository.getProjectConfiguration().custom } returns
mapOf("singleQualityFallbackRequired" to JsonPrimitive(true))

every { faceDetector.analyze(frame) } returnsMany listOf(
badQuality,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import com.simprints.infra.config.store.models.ProjectConfiguration
import com.simprints.infra.config.store.models.experimental
import io.mockk.*
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
Expand Down Expand Up @@ -38,7 +38,8 @@ class IsUsingAutoCaptureUseCaseTest {
featureEnabled: Boolean,
preferenceEnabled: Boolean,
) {
coEvery { projectConfiguration.experimental().faceAutoCaptureEnabled } returns featureEnabled
coEvery { projectConfiguration.custom } returns
mapOf("faceAutoCaptureEnabled" to JsonPrimitive(featureEnabled))
every { sharedPreferences.getBoolean("preference_enable_face_auto_capture", true) } returns preferenceEnabled
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ClientApiViewModel @Inject internal constructor(
action: String,
extras: Bundle,
): ActionRequest? {
val extrasMap = extras.toMap()
val extrasMap: Map<String, String> = extras.toMap().mapValues { it.value.toString() }
return try {
// Session must be created to be able to report invalid intents if mapping fails
if (createSessionIfRequiredUseCase(action)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal class SimpleEventReporter @Inject constructor(
) {
fun addInvalidIntentEvent(
action: String,
extras: Map<String, Any>,
extras: Map<String, String>,
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still verifying this part, as I believe using String alone should be sufficient for the extras.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and it is working as expected. below an example event

{
          "id": "ebc63e69-3360-4071-a1d5-da10da55f719",
          "type": "InvalidIntent",
          "version": 2,
          "payload": {
            "startTime": {
              "unixMs": 1768329314909,
              "isUnixMsTrustworthy": true,
              "elapsedSinceBoot": 85249056
            },
            "action": "com.simprints.id.REGISTER",
            "extras": {
              "test": "newExtra",
              "projectId": "NDAdcsfcentNWOGsIkqC",
              "userId": "1",
              "oduleId": "1",
              "callerPackageName": "com.simprints.intentlauncher"
            }
          },
          "tokenizedFields": []
        }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this might only be used in the dashboard reporting and values are only looked at in case of deep investigation, so just strings would be fine.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if other types are passed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you are explicitly converting it. Then I guess it's fine.

) {
sessionCoroutineScope.launch {
coreEventRepository.addOrUpdateEvent(InvalidIntentEvent(timeHelper.now(), action, extras))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Before
import org.junit.Test

Expand Down Expand Up @@ -199,7 +200,7 @@ internal class ConfirmIdentityValidatorTest : ActionRequestValidatorTest(Confirm
coEvery { mockEventRepository.getEventsFromScope(any()) } returns listOf(mockCallback)

// Mock ConfigManager with feature flag enabled
every { mockProjectConfig.custom } returns mapOf("allowConfirmingGuidsNotInCallback" to true)
every { mockProjectConfig.custom } returns mapOf("allowConfirmingGuidsNotInCallback" to JsonPrimitive(true))
coEvery { mockConfigRepository.getProjectConfiguration() } returns mockProjectConfig

val validator = ConfirmIdentityValidator(
Expand All @@ -224,7 +225,7 @@ internal class ConfirmIdentityValidatorTest : ActionRequestValidatorTest(Confirm
coEvery { mockEventRepository.getEventsFromScope(any()) } returns listOf(mockCallback)

// Mock ConfigManager with feature flag disabled
every { mockProjectConfig.custom } returns mapOf("allowConfirmingGuidsNotInCallback" to false)
every { mockProjectConfig.custom } returns mapOf("allowConfirmingGuidsNotInCallback" to JsonPrimitive(false))
coEvery { mockConfigRepository.getProjectConfiguration() } returns mockProjectConfig

val validator = ConfirmIdentityValidator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ class GetEnrolmentCreationEventForRecordUseCaseTest {
fun setUp() {
MockKAnnotations.init(this, relaxed = true)

every { jsonHelper.toJson(any()) } returns "json"

useCase = GetEnrolmentCreationEventForRecordUseCase(
configRepository,
enrolmentRecordRepository,
Expand Down
1 change: 1 addition & 0 deletions feature/dashboard/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
debugImplementation(project(":testing:data-generator"))

implementation(libs.fuzzywuzzy.core)
implementation(libs.kotlin.serialization)

// UI
implementation(libs.androidX.ui.preference)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.mockk.*
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -66,8 +67,8 @@ class SettingsViewModelTest {

@Test
fun `experimentalConfiguration live data should follow the project experimental configuration`() = runTest {
val experimentalConfig1 = mapOf("key1" to "value1")
val experimentalConfig2 = mapOf("key2" to "value2")
val experimentalConfig1 = mapOf("key1" to JsonPrimitive("value1"))
val experimentalConfig2 = mapOf("key2" to JsonPrimitive("value2"))

coEvery { configRepository.observeProjectConfiguration() } returns flowOf(
mockk<ProjectConfiguration>(relaxed = true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ internal class ReportActionRequestEventsUseCase @Inject constructor(
private fun reportUnknownExtras(actionRequest: ActionRequest) {
if (actionRequest.unknownExtras.isNotEmpty()) {
sessionCoroutineScope.launch {
sessionEventRepository.addOrUpdateEvent(SuspiciousIntentEvent(timeHelper.now(), actionRequest.unknownExtras))
sessionEventRepository.addOrUpdateEvent(
SuspiciousIntentEvent(
timeHelper.now(),
actionRequest.unknownExtras.mapValues { it.value.toString() },
),
)
}
}
}
Expand All @@ -61,6 +66,7 @@ internal class ReportActionRequestEventsUseCase @Inject constructor(
metadata,
BiometricDataSource.fromString(biometricDataSource),
)

is ActionRequest.IdentifyActionRequest -> IdentificationCalloutEventV3(
startTime,
projectId,
Expand All @@ -69,6 +75,7 @@ internal class ReportActionRequestEventsUseCase @Inject constructor(
metadata,
BiometricDataSource.fromString(biometricDataSource),
)

is ActionRequest.VerifyActionRequest -> VerificationCalloutEventV3(
startTime,
projectId,
Expand All @@ -78,13 +85,15 @@ internal class ReportActionRequestEventsUseCase @Inject constructor(
metadata,
BiometricDataSource.fromString(biometricDataSource),
)

is ActionRequest.ConfirmIdentityActionRequest -> ConfirmationCalloutEventV3(
startTime,
projectId,
selectedGuid,
sessionId,
metadata,
)

is ActionRequest.EnrolLastBiometricActionRequest -> EnrolmentLastBiometricsCalloutEventV3(
startTime,
projectId,
Expand Down
1 change: 1 addition & 0 deletions feature/orchestrator/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("simprints.feature")
id("kotlin-parcelize")
id("simprints.library.kotlinSerialization")
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.simprints.feature.orchestrator.usecases.steps
import com.google.common.truth.Truth.*
import com.simprints.core.domain.common.AgeGroup
import com.simprints.core.domain.common.Modality
import com.simprints.core.domain.externalcredential.ExternalCredentialType
import com.simprints.core.domain.common.TemplateIdentifier
import com.simprints.core.domain.externalcredential.ExternalCredentialType
Comment thread
meladRaouf marked this conversation as resolved.
import com.simprints.feature.externalcredential.screens.search.model.ScannedCredential
import com.simprints.feature.orchestrator.cache.OrchestratorCache
import com.simprints.feature.orchestrator.exceptions.SubjectAgeNotSupportedException
Expand All @@ -17,12 +17,12 @@ import com.simprints.infra.config.store.models.FingerprintConfiguration
import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.NEC
import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER
import com.simprints.infra.config.store.models.ProjectConfiguration
import com.simprints.infra.config.store.models.experimental
import com.simprints.infra.orchestration.data.ActionRequest
import io.mockk.*
import io.mockk.impl.annotations.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
Expand Down Expand Up @@ -240,7 +240,8 @@ class BuildStepsUseCaseTest {

val action = mockk<ActionRequest.IdentifyActionRequest>(relaxed = true)
every { action.getSubjectAgeIfAvailable() } returns null
every { projectConfiguration.experimental().idPoolValidationEnabled } returns true
every { projectConfiguration.custom } returns
mapOf("validateIdentificationPool" to JsonPrimitive(true))

val steps = useCase.build(action, projectConfiguration, enrolmentSubjectId, cachedScannedCredential)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.mockk.every
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonPrimitive
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -44,7 +45,8 @@ class FallbackToCommCareDataSourceIfNeededUseCaseTest {

return mockk<ProjectConfiguration>(relaxed = true) {
every { synchronization } returns syncConfig
every { custom } returns mapOf("fallbackToCommCareThresholdDays" to thresholdDays)
every { custom } returns
mapOf("fallbackToCommCareThresholdDays" to JsonPrimitive(thresholdDays))
}
}

Expand Down
3 changes: 0 additions & 3 deletions infra/config-store/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ android {
dependencies {
implementation(project(":infra:auth-store"))
implementation(project(":infra:enrolment-records:realm-store"))

implementation(libs.datastore)

implementation(libs.retrofit.core)
implementation(libs.jackson.core)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.simprints.infra.config.store.local.migrations
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.datastore.core.DataMigration
import com.fasterxml.jackson.core.JacksonException
import com.simprints.core.tools.json.JsonHelper
import com.simprints.infra.authstore.AuthStore
import com.simprints.infra.config.store.local.migrations.models.OldProjectConfig
Expand All @@ -12,6 +11,7 @@ import com.simprints.infra.config.store.local.models.toProto
import com.simprints.infra.logging.LoggingConstants.CrashReportTag.MIGRATION
import com.simprints.infra.logging.Simber
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.serialization.SerializationException
import javax.inject.Inject

/**
Expand All @@ -37,12 +37,12 @@ internal class ProjectConfigSharedPrefsMigration @Inject constructor(
if (projectSettingsJson.isNullOrEmpty()) return currentData

return try {
JsonHelper
.fromJson<OldProjectConfig>(projectSettingsJson)
JsonHelper.json
Comment thread
meladRaouf marked this conversation as resolved.
.decodeFromString<OldProjectConfig>(projectSettingsJson)
.toDomain(authStore.signedInProjectId)
.toProto()
} catch (e: Exception) {
if (e is JacksonException) {
if (e is SerializationException) {
// Return default value
Simber.i("Invalid old configuration for project ${authStore.signedInProjectId}", e, tag = MIGRATION)
ProtoProjectConfiguration.getDefaultInstance()
Expand Down
Loading