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
2 changes: 1 addition & 1 deletion feature/login/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 All @@ -19,6 +20,5 @@ dependencies {
implementation(libs.androidX.cameraX.core)
implementation(libs.androidX.cameraX.lifecycle)
implementation(libs.androidX.cameraX.view)
implementation(libs.jackson.core)
implementation(libs.playServices.barcode)
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,21 @@ internal class LoginFormViewModel @Inject constructor(

private fun mapAuthDataResult(result: AuthenticateDataResult): SignInState = when (result) {
AuthenticateDataResult.Authenticated -> SignInState.Success

AuthenticateDataResult.BadCredentials -> SignInState.BadCredentials

AuthenticateDataResult.IntegrityException -> SignInState.IntegrityException

AuthenticateDataResult.IntegrityServiceTemporaryDown -> SignInState.IntegrityServiceTemporaryDown

AuthenticateDataResult.MissingOrOutdatedGooglePlayStoreApp -> SignInState.MissingOrOutdatedGooglePlayStoreApp

AuthenticateDataResult.Offline -> SignInState.Offline

AuthenticateDataResult.TechnicalFailure -> SignInState.TechnicalFailure

AuthenticateDataResult.Unknown -> SignInState.Unknown

Comment on lines +65 to +79
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The blank lines between each branch of the when expression are unnecessary and inconsistent with typical Kotlin style conventions. Consider removing these extra blank lines to make the code more compact and readable.

Suggested change
AuthenticateDataResult.BadCredentials -> SignInState.BadCredentials
AuthenticateDataResult.IntegrityException -> SignInState.IntegrityException
AuthenticateDataResult.IntegrityServiceTemporaryDown -> SignInState.IntegrityServiceTemporaryDown
AuthenticateDataResult.MissingOrOutdatedGooglePlayStoreApp -> SignInState.MissingOrOutdatedGooglePlayStoreApp
AuthenticateDataResult.Offline -> SignInState.Offline
AuthenticateDataResult.TechnicalFailure -> SignInState.TechnicalFailure
AuthenticateDataResult.Unknown -> SignInState.Unknown
AuthenticateDataResult.BadCredentials -> SignInState.BadCredentials
AuthenticateDataResult.IntegrityException -> SignInState.IntegrityException
AuthenticateDataResult.IntegrityServiceTemporaryDown -> SignInState.IntegrityServiceTemporaryDown
AuthenticateDataResult.MissingOrOutdatedGooglePlayStoreApp -> SignInState.MissingOrOutdatedGooglePlayStoreApp
AuthenticateDataResult.Offline -> SignInState.Offline
AuthenticateDataResult.TechnicalFailure -> SignInState.TechnicalFailure
AuthenticateDataResult.Unknown -> SignInState.Unknown

Copilot uses AI. Check for mistakes.
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 will look into disabling those rules.

is AuthenticateDataResult.BackendMaintenanceError -> SignInState.BackendMaintenanceError(
result.estimatedOutage?.let { TimeUtils.getFormattedEstimatedOutage(it) },
)
Expand All @@ -88,7 +96,7 @@ internal class LoginFormViewModel @Inject constructor(
_signInState.send(mapQrError(result.error))
} else if (!result.content.isNullOrEmpty()) {
try {
val qrContent = jsonHelper.fromJson<QrCodeContent>(result.content)
val qrContent = jsonHelper.json.decodeFromString<QrCodeContent>(result.content)
Simber.i("QR scanning successful", tag = LOGIN)

if (projectId != qrContent.projectId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.simprints.feature.login.screens.qrscanner

import androidx.annotation.Keep
import com.fasterxml.jackson.annotation.JsonProperty
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Keep
@Serializable
internal data class QrCodeContent(
val projectId: String,
val projectSecret: String,
@JsonProperty("backend") val apiBaseUrl: String? = null,
@SerialName("backend") val apiBaseUrl: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@ package com.simprints.feature.login.screens.form

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.*
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Wildcard imports should be avoided. Instead of using import com.google.common.truth.Truth.* and import io.mockk.*, explicitly import only the functions and classes that are used in this file. This makes dependencies clearer and avoids potential naming conflicts.

Copilot uses AI. Check for mistakes.
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.

Explicitly allowed in tests.

import com.simprints.core.domain.tokenization.asTokenizableRaw
import com.simprints.core.tools.json.JsonHelper
import com.simprints.feature.login.LoginParams
import com.simprints.feature.login.screens.qrscanner.QrCodeContent
import com.simprints.feature.login.screens.qrscanner.QrScannerResult
import com.simprints.feature.login.screens.qrscanner.QrScannerResult.QrScannerError
import com.simprints.infra.authlogic.AuthManager
import com.simprints.infra.authlogic.model.AuthenticateDataResult
import com.simprints.infra.network.SimNetwork
import com.simprints.testtools.common.coroutines.TestCoroutineRule
import com.simprints.testtools.common.livedata.getOrAwaitValue
import io.mockk.MockKAnnotations
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.*
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Wildcard imports should be avoided. Instead of using import io.mockk.*, explicitly import only the functions and classes that are used in this file (e.g., MockKAnnotations, clearMocks, coEvery, every, mockk, slot, verify). This makes dependencies clearer and avoids potential naming conflicts.

Suggested change
import io.mockk.*
import io.mockk.MockKAnnotations
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify

Copilot uses AI. Check for mistakes.
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand All @@ -40,9 +33,6 @@ internal class LoginFormViewModelTest {
@MockK
private lateinit var authManager: AuthManager

@MockK
private lateinit var jsonHelper: JsonHelper

private lateinit var viewModel: LoginFormViewModel

@Before
Expand All @@ -53,7 +43,7 @@ internal class LoginFormViewModelTest {
DEVICE_ID,
simNetwork,
authManager,
jsonHelper,
JsonHelper,
)
}

Expand Down Expand Up @@ -170,29 +160,39 @@ internal class LoginFormViewModelTest {

@Test
fun `returns correct SignInState when QR code parsing fails`() {
every { jsonHelper.fromJson<QrCodeContent>(any()) } throws RuntimeException("parsing fail")

viewModel.handleQrResult(PROJECT_ID, QrScannerResult(QR_CONTENT, null))
viewModel.handleQrResult(PROJECT_ID, QrScannerResult("Invalid json", null))
val result = viewModel.signInState.getOrAwaitValue()

assertThat(result.getContentIfNotHandled()).isInstanceOf(SignInState.QrInvalidCode::class.java)
}

@Test
fun `returns correct SignInState when QR contains wrong project ID`() {
every { jsonHelper.fromJson<QrCodeContent>(eq(QR_CONTENT)) } returns QrCodeContent("differentProjectId", PROJECT_SECRET)

viewModel.handleQrResult(PROJECT_ID, QrScannerResult(QR_CONTENT, null))
val qrContent =
"""
{
"projectId": "differentProjectId",
"projectSecret": "$PROJECT_SECRET",
"backend": "$URL"
}
""".trimIndent()
viewModel.handleQrResult(PROJECT_ID, QrScannerResult(qrContent, null))
val result = viewModel.signInState.getOrAwaitValue()

assertThat(result.getContentIfNotHandled()).isInstanceOf(SignInState.ProjectIdMismatch::class.java)
}

@Test
fun `returns correct SignInState when QR code parsing success`() {
every { jsonHelper.fromJson<QrCodeContent>(eq(QR_CONTENT)) } returns QrCodeContent(PROJECT_ID, PROJECT_SECRET)

viewModel.handleQrResult(PROJECT_ID, QrScannerResult(QR_CONTENT, null))
val qrContent =
"""
{
"projectId": "$PROJECT_ID",
"projectSecret": "$PROJECT_SECRET",
"backend": "$URL"
}
""".trimIndent()
viewModel.handleQrResult(PROJECT_ID, QrScannerResult(qrContent, null))
val result = viewModel.signInState.getOrAwaitValue()

assertThat(result.getContentIfNotHandled()).isInstanceOf(SignInState.QrCodeValid::class.java)
Expand All @@ -202,9 +202,15 @@ internal class LoginFormViewModelTest {

@Test
fun `updates base API url when QR code parsing success`() {
every { jsonHelper.fromJson<QrCodeContent>(eq(QR_CONTENT)) } returns QrCodeContent(PROJECT_ID, PROJECT_SECRET, URL)

viewModel.handleQrResult(PROJECT_ID, QrScannerResult(QR_CONTENT, null))
val qrContent =
"""
{
"projectId": "$PROJECT_ID",
"projectSecret": "$PROJECT_SECRET",
"backend": "$URL"
}
""".trimIndent()
viewModel.handleQrResult(PROJECT_ID, QrScannerResult(qrContent, null))

verify { simNetwork.setApiBaseUrl(eq(URL)) }
}
Expand Down Expand Up @@ -236,7 +242,6 @@ internal class LoginFormViewModelTest {
private const val PROJECT_ID = "projectId"
private val USER_ID = "userId".asTokenizableRaw()

private const val QR_CONTENT = "qrCodeContents"
private const val PROJECT_SECRET = "projectSecret"
private const val URL = "projectUrl"
}
Expand Down
1 change: 0 additions & 1 deletion infra/ui-base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ dependencies {
api(libs.androidX.cameraX.core)
api(libs.androidX.cameraX.lifecycle)
api(libs.androidX.cameraX.view)
api(libs.jackson.core)
api(libs.playServices.barcode)

testImplementation(project(":infra:test-tools"))
Expand Down