From 932fa90397c2bac649e14e6a6f90d3675d8cbe9f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 24 Jul 2026 15:11:58 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../usecase/GetAppUpdateStateUseCase.kt | 22 ++++++++----- .../appupdate/GetAppUpdateStateUseCaseTest.kt | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/domain/app-update/src/main/kotlin/com/tangem/domain/appupdate/usecase/GetAppUpdateStateUseCase.kt b/domain/app-update/src/main/kotlin/com/tangem/domain/appupdate/usecase/GetAppUpdateStateUseCase.kt index ad85e96d3d..70b8e8de43 100644 --- a/domain/app-update/src/main/kotlin/com/tangem/domain/appupdate/usecase/GetAppUpdateStateUseCase.kt +++ b/domain/app-update/src/main/kotlin/com/tangem/domain/appupdate/usecase/GetAppUpdateStateUseCase.kt @@ -71,14 +71,17 @@ class GetAppUpdateStateUseCase( val latestVersion = info.latestVersion?.let(AppVersion::parseOrNull) val criticalVersion = info.criticalVersion?.let(AppVersion::parseOrNull) - if (criticalVersion != null && appVersion <= criticalVersion && isEscapable(latestVersion, criticalVersion)) { + if (criticalVersion != null && + appVersion <= criticalVersion && + isEscapable(latestVersion, criticalVersion, appVersion) + ) { return blockingStateFor(info.criticalOSVersion, deviceOsVersion, AppUpdateState.Brick) } val minSupportedVersion = info.minSupportedVersion?.let(AppVersion::parseOrNull) if (minSupportedVersion != null && appVersion < minSupportedVersion && - isEscapable(latestVersion, minSupportedVersion) + isEscapable(latestVersion, minSupportedVersion, appVersion) ) { return blockingStateFor(info.minSupportedOSVersion, deviceOsVersion, AppUpdateState.OsTooOld) } @@ -91,12 +94,17 @@ class GetAppUpdateStateUseCase( } /** - * A blocking threshold is honored only if the advertised latest version is strictly above it — i.e. - * updating actually clears the block. A threshold no installable version can satisfy is a backend - * misconfiguration and is ignored. + * A blocking threshold is honored only if the user can actually escape it by updating. Normally that + * means the advertised latest version is strictly above the threshold. But a latest version reported + * below the installed one is stale/misconfigured and must not suppress the block — the store almost + * certainly has an installable build, so the threshold is honored. A missing latest version leaves no + * installable target, so the threshold is ignored. */ - private fun isEscapable(latestVersion: AppVersion?, threshold: AppVersion): Boolean = - latestVersion != null && latestVersion > threshold + private fun isEscapable(latestVersion: AppVersion?, threshold: AppVersion, appVersion: AppVersion): Boolean { + latestVersion ?: return false + if (latestVersion < appVersion) return true + return latestVersion > threshold + } private suspend fun resolveOptionalUpdate(latestVersion: String, recordOptionalShown: Boolean): AppUpdateState { if (!recordOptionalShown) return AppUpdateState.OptionalUpdate diff --git a/domain/app-update/src/test/kotlin/com/tangem/domain/appupdate/GetAppUpdateStateUseCaseTest.kt b/domain/app-update/src/test/kotlin/com/tangem/domain/appupdate/GetAppUpdateStateUseCaseTest.kt index 59477145b0..a12982ed1a 100644 --- a/domain/app-update/src/test/kotlin/com/tangem/domain/appupdate/GetAppUpdateStateUseCaseTest.kt +++ b/domain/app-update/src/test/kotlin/com/tangem/domain/appupdate/GetAppUpdateStateUseCaseTest.kt @@ -111,6 +111,37 @@ internal class GetAppUpdateStateUseCaseTest { assertThat(useCase.getCached()).isEqualTo(AppUpdateState.OsTooOld) } + @Test + fun `GIVEN latest below installed and min supported not met WHEN getCached THEN ForceUpdate`() = runTest { + // [REDACTED_TASK_KEY]: a latestVersion below the installed one must not suppress the min-supported force update. + givenCached( + appVersion = "6.1", + osVersion = "16", + info = info(minSupportedVersion = "6.2", minSupportedOSVersion = "15.0", latestVersion = "5.0"), + ) + + assertThat(useCase.getCached()).isEqualTo(AppUpdateState.ForceUpdate) + } + + @Test + fun `GIVEN latest below installed and critical met WHEN getCached THEN ForceUpdate`() = runTest { + // [REDACTED_TASK_KEY]: a latestVersion below the installed one must not suppress the critical force update either. + givenCached( + appVersion = "6.1", + osVersion = "16", + info = info(criticalVersion = "6.1", criticalOSVersion = "15.0", latestVersion = "5.0"), + ) + + assertThat(useCase.getCached()).isEqualTo(AppUpdateState.ForceUpdate) + } + + @Test + fun `GIVEN latest below installed and no thresholds WHEN getCached THEN NoUpdate`() = runTest { + givenCached(appVersion = "6.1", info = info(latestVersion = "5.0")) + + assertThat(useCase.getCached()).isEqualTo(AppUpdateState.NoUpdate) + } + @Test fun `GIVEN critical above latest WHEN getCached THEN not blocking and degraded to optional`() = runTest { givenCached(appVersion = "5.20", info = info(criticalVersion = "9.99", latestVersion = "5.41"))