diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/FallbackToCommCareDataSourceIfNeededUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/FallbackToCommCareDataSourceIfNeededUseCase.kt index f4531ddb96..ce0a5d88f5 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/FallbackToCommCareDataSourceIfNeededUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/FallbackToCommCareDataSourceIfNeededUseCase.kt @@ -45,7 +45,11 @@ internal class FallbackToCommCareDataSourceIfNeededUseCase @Inject constructor( return false } - Simber.w("Falling back to CommCare data source because threshold is $thresholdMs and last sync was at $lastSyncTime", tag = SYNC) + Simber.w( + message = "Falling back to CommCare data source because threshold is $thresholdMs and last sync was at $lastSyncTime", + t = Exception("Fallback to CommCare just-in-time reading"), + tag = SYNC, + ) return true } } diff --git a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSource.kt b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSource.kt index fc3cc68646..69a6d38293 100644 --- a/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSource.kt +++ b/infra/event-sync/src/main/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSource.kt @@ -253,7 +253,11 @@ internal class CommCareEventDataSource @Inject constructor( } } - Simber.w("All date parsing attempts failed for: $dateString", tag = COMMCARE_SYNC) + Simber.w( + message = "All date parsing attempts failed for: $dateString", + t = IllegalArgumentException("Could not parse date string"), + tag = COMMCARE_SYNC, + ) return 0L } diff --git a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSourceTest.kt b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSourceTest.kt index 1107c38d15..300e032d74 100644 --- a/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSourceTest.kt +++ b/infra/event-sync/src/test/java/com/simprints/infra/eventsync/event/commcare/CommCareEventDataSourceTest.kt @@ -570,7 +570,7 @@ class CommCareEventDataSourceTest { assertEquals(1, events.size) // Still processes because invalid date defaults to 0L // Should log error for each format attempt plus final failure warning verify(atLeast = 2) { Simber.e(any(), ofType(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } - verify { Simber.w(any(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } + verify { Simber.w(any(), ofType(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } } @Test @@ -976,7 +976,7 @@ class CommCareEventDataSourceTest { // Should log date parsing errors verify(atLeast = 2) { Simber.e(any(), ofType(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } - verify { Simber.w(any(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } + verify { Simber.w(any(), ofType(), tag = LoggingConstants.CrashReportTag.COMMCARE_SYNC) } } @Test diff --git a/infra/logging/src/main/java/com/simprints/infra/logging/Simber.kt b/infra/logging/src/main/java/com/simprints/infra/logging/Simber.kt index 54da5d2f5c..0c0b7aaaf6 100644 --- a/infra/logging/src/main/java/com/simprints/infra/logging/Simber.kt +++ b/infra/logging/src/main/java/com/simprints/infra/logging/Simber.kt @@ -72,19 +72,19 @@ object Simber { */ fun w( message: String, - t: Throwable? = null, + t: Throwable, tag: CrashReportTag, ) = w(message, t, tag.name) fun w( message: String, - t: Throwable? = null, + t: Throwable, tag: String = DEFAULT_TAG, ) { - when { - t == null -> Logger.w(message, null, ensureCharactersAreValid(tag)) - shouldSkipThrowableReporting(t) -> Logger.i(message, t, ensureCharactersAreValid(tag)) - else -> Logger.w(message, t, ensureCharactersAreValid(tag)) + if (shouldSkipThrowableReporting(t)) { + Logger.i(message, t, ensureCharactersAreValid(tag)) + } else { + Logger.w(message, t, ensureCharactersAreValid(tag)) } } @@ -171,7 +171,7 @@ object Simber { throw IllegalArgumentException("String must be less than $max characters.") } - return message.substring(0, max) + return message.take(max) } return message } diff --git a/infra/logging/src/test/java/com/simprints/infra/logging/writers/CrashlyticsLogWriterTest.kt b/infra/logging/src/test/java/com/simprints/infra/logging/writers/CrashlyticsLogWriterTest.kt index ac6a422023..b3635485a6 100644 --- a/infra/logging/src/test/java/com/simprints/infra/logging/writers/CrashlyticsLogWriterTest.kt +++ b/infra/logging/src/test/java/com/simprints/infra/logging/writers/CrashlyticsLogWriterTest.kt @@ -56,21 +56,6 @@ class CrashlyticsLogWriterTest { verify { crashMock.log("[TAG] Test Message") } } - @Test - fun `should log and record error on WARN priority`() { - val crashMock = mockk(relaxed = true) - val spyCrashReportingTree = spyk(CrashlyticsLogWriter(crashMock)) - - Logger.setLogWriters(spyCrashReportingTree) - Simber.w("Test Message", null) - - verify { - crashMock.recordException( - match { it.message?.contains("Test Message") == true } - ) - } - } - @Test fun `with custom exception should log and record error on WARN priority`() { val crashMock = mockk(relaxed = true) diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt index 7e0cf05608..c0ee511cea 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FaceMatcherUseCase.kt @@ -40,7 +40,11 @@ class FaceMatcherUseCase @Inject constructor( ): Flow = channelFlow { Simber.i("Initialising matcher", tag = crashReportTag) if (matchParams.bioSdk !is FaceConfiguration.BioSdk) { - Simber.w("Face SDK was not provided", tag = crashReportTag) + Simber.w( + message = "Face SDK was not provided", + t = IllegalArgumentException("Face SDK was not provided"), + tag = crashReportTag, + ) send(MatcherState.Success(emptyList(), emptyList(), 0, "")) return@channelFlow } diff --git a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt index 975fc4c737..9fdd14e0f3 100644 --- a/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt +++ b/infra/matching/src/main/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCase.kt @@ -42,7 +42,11 @@ class FingerprintMatcherUseCase @Inject constructor( ): Flow = channelFlow { Simber.i("Initialising matcher", tag = crashReportTag) if (matchParams.bioSdk !is FingerprintConfiguration.BioSdk) { - Simber.w("Fingerprint SDK was not provided", tag = crashReportTag) + Simber.w( + message = "Fingerprint SDK was not provided", + t = IllegalArgumentException("Fingerprint SDK was not provided"), + tag = crashReportTag, + ) send(MatcherState.Success(emptyList(), emptyList(), 0, "")) return@channelFlow } diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt index 0c984ce674..e0ade2049a 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FaceMatcherUseCaseTest.kt @@ -12,10 +12,13 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.face.infra.basebiosdk.matching.FaceMatcher import com.simprints.face.infra.biosdkresolver.ResolveFaceBioSdkUseCase import com.simprints.infra.config.store.models.FaceConfiguration +import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.config.store.models.Project import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepository import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.logging.LoggingConstants +import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchParams import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.* @@ -130,6 +133,52 @@ internal class FaceMatcherUseCaseTest { ) } + @Test + fun `Logs warning and returns empty success when wrong SDK type is provided`() = runTest { + mockkObject(Simber) + justRun { Simber.w(message = any(), t = any(), tag = any()) } + + val results = useCase + .invoke( + MatchParams( + probeReferenceId = "referenceId", + probeSamples = listOf( + CaptureSample( + captureEventId = "faceId", + template = byteArrayOf(1, 2, 3), + modality = Modality.FACE, + format = "format", + ), + ), + bioSdk = FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER, // Wrong SDK type + flowType = FlowType.VERIFY, + queryForCandidates = SubjectQuery(), + biometricDataSource = BiometricDataSource.Simprints, + ), + project, + ).toList() + + verify { + Simber.w( + message = "Face SDK was not provided", + t = ofType(), + tag = LoggingConstants.CrashReportTag.FACE_MATCHING, + ) + } + + assertThat(results).containsExactly( + MatcherUseCase.MatcherState.Success( + comparisonResults = emptyList(), + totalCandidates = 0, + matcherName = "", + matchBatches = emptyList(), + ), + ) + + coVerify(exactly = 0) { faceMatcher.getHighestComparisonScoreForCandidate(any()) } + unmockkObject(Simber) + } + @Test fun `Correctly calls SDK matcher`() = runTest { val totalCandidates = 1 diff --git a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt index aeebdd2a7a..df9a0bd045 100644 --- a/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt +++ b/infra/matching/src/test/java/com/simprints/infra/matching/usecase/FingerprintMatcherUseCaseTest.kt @@ -12,6 +12,7 @@ import com.simprints.core.tools.time.TimeHelper import com.simprints.core.tools.time.Timestamp import com.simprints.fingerprint.infra.biosdk.BioSdkWrapper import com.simprints.fingerprint.infra.biosdk.ResolveBioSdkWrapperUseCase +import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration.BioSdk.SECUGEN_SIM_MATCHER import com.simprints.infra.config.store.models.Project import com.simprints.infra.config.sync.ConfigManager @@ -19,6 +20,8 @@ import com.simprints.infra.enrolment.records.repository.EnrolmentRecordRepositor import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource import com.simprints.infra.enrolment.records.repository.domain.models.IdentityBatch import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery +import com.simprints.infra.logging.LoggingConstants +import com.simprints.infra.logging.Simber import com.simprints.infra.matching.MatchParams import com.simprints.testtools.common.coroutines.TestCoroutineRule import io.mockk.* @@ -147,6 +150,53 @@ internal class FingerprintMatcherUseCaseTest { ) } + @Test + fun `Logs warning and returns empty success when wrong SDK type is provided`() = runTest { + mockkObject(Simber) + justRun { Simber.w(message = any(), t = any(), tag = any()) } + + val results = useCase + .invoke( + MatchParams( + probeReferenceId = "referenceId", + probeSamples = listOf( + CaptureSample( + captureEventId = "fingerprintId", + template = byteArrayOf(1, 2, 3), + modality = Modality.FINGERPRINT, + format = "format", + identifier = SampleIdentifier.LEFT_3RD_FINGER, + ), + ), + bioSdk = FaceConfiguration.BioSdk.RANK_ONE, // Wrong SDK type + flowType = FlowType.VERIFY, + queryForCandidates = SubjectQuery(), + biometricDataSource = BiometricDataSource.Simprints, + ), + project, + ).toList() + + verify { + Simber.w( + message = "Fingerprint SDK was not provided", + t = ofType(), + tag = LoggingConstants.CrashReportTag.FINGER_MATCHING, + ) + } + + assertThat(results).containsExactly( + MatcherUseCase.MatcherState.Success( + comparisonResults = emptyList(), + totalCandidates = 0, + matcherName = "", + matchBatches = emptyList(), + ), + ) + + coVerify(exactly = 0) { bioSdkWrapper.match(any(), any(), any()) } + unmockkObject(Simber) + } + @Test fun `Correctly calls SDK matcher`() = runTest { coEvery { enrolmentRecordRepository.count(any(), any()) } returns 100 diff --git a/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt index 82b96148d8..f0658571c7 100644 --- a/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt +++ b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt @@ -168,7 +168,7 @@ private fun NavController.navigateIfPossible( } else { val fragmentName = currentFragment.toString().takeWhile { it != ' ' } val target = (currentDestination as? FragmentNavigator.Destination)?.className - Simber.w("Cannot navigate from $fragmentName to $target") + Simber.w("Cannot navigate from $fragmentName to $target", t = IllegalStateException("Navigation not possible")) } }