feat: harden offline sync and add diagnostics
This commit is contained in:
@@ -261,6 +261,32 @@ class DriverUiRulesTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun healthyOnlineStateDoesNotShowOfflineBanner() {
|
||||
assertEquals(
|
||||
null,
|
||||
connectionStatusBannerMessage(
|
||||
isOnline = true,
|
||||
isStale = false,
|
||||
serverConnectionState = ServerConnectionState.Reachable,
|
||||
showRealtimeReconnecting = false,
|
||||
syncLabel = "12:30",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun serverFailureAndRealtimeReconnectAreNotCalledOffline() {
|
||||
assertEquals(
|
||||
"Internet działa, ale serwer jest chwilowo niedostępny. Pokazujemy dane z 12:30.",
|
||||
connectionStatusBannerMessage(true, true, ServerConnectionState.Degraded, false, "12:30"),
|
||||
)
|
||||
assertEquals(
|
||||
"Ponowne łączenie. Dane są uzgadniane z serwerem.",
|
||||
connectionStatusBannerMessage(true, false, ServerConnectionState.Reachable, true, "12:30"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun explainsStaleCachedDataWhenOnline() {
|
||||
assertEquals(
|
||||
@@ -321,7 +347,7 @@ class DriverUiRulesTest {
|
||||
val steps = routeFlowSteps(route, emptyList())
|
||||
val action = routeLifecyclePrimaryAction(route, "2026-06-30", today)
|
||||
|
||||
assertEquals(listOf("Zdjęcia załadunku", "Waga załadunku", "W trasie", "Rozładunek"), steps.map { it.title })
|
||||
assertEquals(listOf("Zdjęcia załadunku", "Tonaż załadunku", "W trasie", "Rozładunek"), steps.map { it.title })
|
||||
assertEquals(RouteFlowStepState.Todo, steps[0].state)
|
||||
assertEquals(RouteFlowStepState.Todo, steps[1].state)
|
||||
assertEquals(RouteFlowStepState.Todo, steps[2].state)
|
||||
@@ -393,12 +419,22 @@ class DriverUiRulesTest {
|
||||
assertEquals(RouteFlowStepState.NeedsAttention, steps[3].state)
|
||||
assertEquals("Kurs wymaga obsługi. Dane wpisane w telefonie zostały zachowane.", callout?.text)
|
||||
assertTrue(callout?.isConflict ?: false)
|
||||
assertFalse(callout?.canCorrect ?: true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun permanentlyRejectedRouteActionOffersDataCorrection() {
|
||||
val callout = routeSyncCallout(
|
||||
listOf(action(RouteActionType.Start, RouteActionStatus.FailedPermanent, "Brakuje wymaganych danych.")),
|
||||
)
|
||||
|
||||
assertTrue(callout?.canCorrect ?: false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun routeStageSubmitBlockerExplainsMissingRequirements() {
|
||||
assertEquals("Podaj wagę.", routeStageSubmitBlocker("", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Waga musi być większa od zera.", routeStageSubmitBlocker("0", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Podaj tonaż.", routeStageSubmitBlocker("", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Tonaż musi być większy od zera.", routeStageSubmitBlocker("0", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Dodaj co najmniej jedno zdjęcie etapu.", routeStageSubmitBlocker("12,5", serverPhotoCount = 0, localUploadCount = 0))
|
||||
assertEquals(null, routeStageSubmitBlocker("12,5", serverPhotoCount = 0, localUploadCount = 1))
|
||||
}
|
||||
@@ -415,9 +451,9 @@ class DriverUiRulesTest {
|
||||
|
||||
@Test
|
||||
fun loadingWeightStepRequiresPositiveWeightAndPreviousLoadingPhoto() {
|
||||
assertEquals("Podaj wagę.", loadingWeightSubmitBlocker("", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Podaj poprawną wagę.", loadingWeightSubmitBlocker("abc", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Waga musi być większa od zera.", loadingWeightSubmitBlocker("0", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Podaj tonaż.", loadingWeightSubmitBlocker("", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Podaj poprawny tonaż.", loadingWeightSubmitBlocker("abc", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals("Tonaż musi być większy od zera.", loadingWeightSubmitBlocker("0", serverPhotoCount = 1, localUploadCount = 0))
|
||||
assertEquals(
|
||||
"Dodaj co najmniej jedno zdjęcie załadunku.",
|
||||
loadingWeightSubmitBlocker("12,5", serverPhotoCount = 0, localUploadCount = 0),
|
||||
@@ -425,6 +461,61 @@ class DriverUiRulesTest {
|
||||
assertEquals(null, loadingWeightSubmitBlocker("12,5", serverPhotoCount = 0, localUploadCount = 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun optionalStageInputsAreVisibleButDoNotBlockConfirmation() {
|
||||
assertEquals(
|
||||
null,
|
||||
routeStageSubmitBlocker(
|
||||
weightText = "",
|
||||
serverPhotoCount = 0,
|
||||
localUploadCount = 0,
|
||||
weightRequirement = RouteStageRequirement.Optional,
|
||||
photoRequirement = RouteStageRequirement.Optional,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
"Podaj poprawny tonaż.",
|
||||
routeStageSubmitBlocker(
|
||||
weightText = "abc",
|
||||
serverPhotoCount = 0,
|
||||
localUploadCount = 0,
|
||||
weightRequirement = RouteStageRequirement.Optional,
|
||||
photoRequirement = RouteStageRequirement.Optional,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun disabledStageInputsAreHiddenAndDoNotBlockConfirmation() {
|
||||
assertEquals(RouteStageRequirement.Required, normalizeRouteStageRequirement(null))
|
||||
assertEquals(RouteStageRequirement.Required, normalizeRouteStageRequirement("unknown"))
|
||||
assertFalse(routeStageRequirementIsVisible(RouteStageRequirement.Disabled))
|
||||
assertTrue(routeStageRequirementIsVisible(RouteStageRequirement.Optional))
|
||||
assertTrue(routeStageRequirementIsRequired(RouteStageRequirement.Required))
|
||||
assertEquals(
|
||||
null,
|
||||
routeStageSubmitBlocker(
|
||||
weightText = "invalid value is ignored",
|
||||
serverPhotoCount = 0,
|
||||
localUploadCount = 0,
|
||||
weightRequirement = RouteStageRequirement.Disabled,
|
||||
photoRequirement = RouteStageRequirement.Disabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun disabledLoadingStepsAreRemovedFromRouteFlow() {
|
||||
val steps = routeFlowSteps(
|
||||
route(status = "ZAPLANOWANA"),
|
||||
emptyList(),
|
||||
loadingPhotoRequirement = RouteStageRequirement.Disabled,
|
||||
loadingWeightRequirement = RouteStageRequirement.Disabled,
|
||||
)
|
||||
|
||||
assertEquals(listOf("W trasie", "Rozładunek"), steps.map { it.title })
|
||||
}
|
||||
|
||||
private fun route(
|
||||
status: String = "ZAPLANOWANA",
|
||||
loadingWeight: Double? = null,
|
||||
|
||||
@@ -3,6 +3,7 @@ package pl.firmatpp.kierowca.ui
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.io.IOException
|
||||
import pl.firmatpp.kierowca.data.model.DriverLeaveRequestDto
|
||||
import pl.firmatpp.kierowca.data.model.DriverRouteDto
|
||||
import pl.firmatpp.kierowca.data.upload.RouteActionEntity
|
||||
@@ -26,6 +27,21 @@ class DriverUiStateTest {
|
||||
assertEquals(AppThemeMode.Material3, state.themeMode)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkTimeoutDoesNotChangeValidatedNetworkState() {
|
||||
val state = DriverUiState(
|
||||
isOnline = true,
|
||||
serverConnectionState = ServerConnectionState.Reachable,
|
||||
lastServerSyncAtEpochMillis = 123L,
|
||||
)
|
||||
|
||||
val failed = state.withApiError(IOException("timeout"))
|
||||
|
||||
assertTrue(failed.isOnline)
|
||||
assertTrue(failed.isStale)
|
||||
assertEquals(ServerConnectionState.Degraded, failed.serverConnectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun refreshingLeaveRequestsKeepsDetailScreenOpenAndUpdatesSelectedRequest() {
|
||||
val state = DriverUiState(
|
||||
|
||||
Reference in New Issue
Block a user