Napraw obsługę kodów OTP podczas logowania

This commit is contained in:
admin
2026-07-18 17:33:40 +02:00
parent a86dca50b8
commit b9ae128842
8 changed files with 319 additions and 47 deletions
@@ -3,12 +3,16 @@ package pl.firmatpp.kierowca.data
import java.io.IOException
import java.net.UnknownHostException
import kotlin.coroutines.cancellation.CancellationException
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.ResponseBody.Companion.toResponseBody
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Test
import retrofit2.HttpException
import retrofit2.Response
class ApiErrorMapperTest {
@Test
@@ -84,4 +88,30 @@ class ApiErrorMapperTest {
assertEquals("Storage timeout", error.message)
assertEquals(body, error.responseBody)
}
@Test
fun mapsInvalidOtpReasonToActionableMessage() {
val error = ApiErrorMapper.mapHttpStatus(422, """{"ok":false,"reason":"INVALID_OTP"}""")
assertEquals("INVALID_OTP", error.code)
assertEquals("Kod jest nieprawidłowy lub wygasł. Użyj kodu z najnowszego SMS-a.", error.message)
}
@Test
fun translatesRateLimitResponses() {
val error = ApiErrorMapper.mapHttpStatus(429, """{"message":"Too Many Attempts."}""")
assertEquals(ApiErrorKind.RateLimited, error.kind)
assertEquals("Za dużo prób. Odczekaj chwilę i spróbuj ponownie.", error.message)
}
@Test
fun preservesApiProblemWhenSameExceptionIsMappedMoreThanOnce() {
val body = """{"ok":false,"reason":"INVALID_OTP"}"""
.toResponseBody("application/json".toMediaType())
val exception = HttpException(Response.error<Any>(422, body))
assertEquals("INVALID_OTP", ApiErrorMapper.map(exception).code)
assertEquals("INVALID_OTP", ApiErrorMapper.map(exception).code)
}
}
@@ -0,0 +1,48 @@
package pl.firmatpp.kierowca.ui
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class OtpAttemptCoordinatorTest {
@Test
fun buffersSmsReceivedBeforeOtpRequestCompletes() {
val coordinator = OtpAttemptCoordinator()
val attemptId = coordinator.beginAttempt()
assertTrue(coordinator.bufferRetrievedCode(attemptId, "123456"))
assertEquals("123456", coordinator.takeBufferedCode(attemptId))
assertNull(coordinator.takeBufferedCode(attemptId))
}
@Test
fun ignoresConsentResultFromPreviousRequest() {
val coordinator = OtpAttemptCoordinator()
val previousAttempt = coordinator.beginAttempt()
val currentAttempt = coordinator.beginAttempt()
assertFalse(coordinator.bufferRetrievedCode(previousAttempt, "111111"))
assertTrue(coordinator.bufferRetrievedCode(currentAttempt, "222222"))
assertEquals("222222", coordinator.takeBufferedCode(currentAttempt))
}
@Test
fun acceptsOnlyOtpLengthsSupportedByApi() {
val coordinator = OtpAttemptCoordinator()
val attemptId = coordinator.beginAttempt()
assertFalse(coordinator.bufferRetrievedCode(attemptId, "123"))
assertTrue(coordinator.bufferRetrievedCode(attemptId, "1234"))
assertTrue(coordinator.bufferRetrievedCode(attemptId, "1234567890"))
assertFalse(coordinator.bufferRetrievedCode(attemptId, "12345678901"))
}
@Test
fun roundsResendCooldownUpToFullSeconds() {
assertEquals(30, otpResendDelaySeconds(40_000L, 10_000L))
assertEquals(1, otpResendDelaySeconds(10_001L, 10_000L))
assertEquals(0, otpResendDelaySeconds(10_000L, 10_001L))
}
}
@@ -38,4 +38,13 @@ class OtpAutoSubmitPolicyTest {
assertFalse(policy.shouldSubmit("123456", loading = true))
assertFalse(policy.shouldSubmit("1234567", loading = false))
}
@Test
fun submitsCompleteRetrievedCodesAcceptedByApi() {
val policy = OtpAutoSubmitPolicy()
assertTrue(policy.shouldSubmitRetrieved("1234", loading = false))
assertTrue(policy.shouldSubmitRetrieved("1234567890", loading = false))
assertFalse(policy.shouldSubmitRetrieved("123", loading = false))
}
}