From ba52b9ad3a350544b16bf4cc3aa087d504684b92 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Oct 2025 12:27:23 +0200 Subject: [PATCH] Add test on api getAvailableFeatures. --- libraries/featureflag/impl/build.gradle.kts | 1 + .../impl/DefaultFeatureFlagService.kt | 4 +- .../featureflag/impl/FeaturesProvider.kt | 24 +++++ .../impl/DefaultFeatureFlagServiceTest.kt | 92 ++++++++++++++++++- 4 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt diff --git a/libraries/featureflag/impl/build.gradle.kts b/libraries/featureflag/impl/build.gradle.kts index c54f95a293..f44032661c 100644 --- a/libraries/featureflag/impl/build.gradle.kts +++ b/libraries/featureflag/impl/build.gradle.kts @@ -31,4 +31,5 @@ dependencies { testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.featureflag.test) } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt index dca114f5d8..0e7a389250 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagService.kt @@ -14,7 +14,6 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -24,6 +23,7 @@ import kotlinx.coroutines.flow.flowOf class DefaultFeatureFlagService( private val providers: Set<@JvmSuppressWildcards FeatureFlagProvider>, private val buildMeta: BuildMeta, + private val featuresProvider: FeaturesProvider, ) : FeatureFlagService { override fun isFeatureEnabledFlow(feature: Feature): Flow { return providers.filter { it.hasFeature(feature) } @@ -44,7 +44,7 @@ class DefaultFeatureFlagService( includeFinishFeatures: Boolean, isInLabs: Boolean, ): List { - return FeatureFlags.entries.filter { flag -> + return featuresProvider.provide().filter { flag -> (includeFinishFeatures || !flag.isFinished) && flag.isInLabs == isInLabs } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt new file mode 100644 index 0000000000..8d580006e7 --- /dev/null +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/FeaturesProvider.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.featureflag.impl + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.libraries.featureflag.api.Feature +import io.element.android.libraries.featureflag.api.FeatureFlags + +fun interface FeaturesProvider { + fun provide(): List +} + +@ContributesBinding(AppScope::class) +@Inject +class DefaultFeaturesProvider : FeaturesProvider { + override fun provide(): List = FeatureFlags.entries +} diff --git a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt index 097e318609..becb82da49 100644 --- a/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt +++ b/libraries/featureflag/impl/src/test/kotlin/io/element/android/libraries/featureflag/impl/DefaultFeatureFlagServiceTest.kt @@ -9,7 +9,10 @@ package io.element.android.libraries.featureflag.impl import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.matrix.test.core.aBuildMeta import kotlinx.coroutines.test.runTest import org.junit.Test @@ -18,7 +21,7 @@ class DefaultFeatureFlagServiceTest { @Test fun `given service without provider when feature is checked then it returns the default value`() = runTest { val buildMeta = aBuildMeta() - val featureFlagService = DefaultFeatureFlagService(emptySet(), buildMeta) + val featureFlagService = createDefaultFeatureFlagService(buildMeta = buildMeta) featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test { assertThat(awaitItem()).isEqualTo(FeatureFlags.Space.defaultValue(buildMeta)) cancelAndIgnoreRemainingEvents() @@ -27,7 +30,7 @@ class DefaultFeatureFlagServiceTest { @Test fun `given service without provider when set enabled feature is called then it returns false`() = runTest { - val featureFlagService = DefaultFeatureFlagService(emptySet(), aBuildMeta()) + val featureFlagService = createDefaultFeatureFlagService() val result = featureFlagService.setFeatureEnabled(FeatureFlags.Space, true) assertThat(result).isFalse() } @@ -36,7 +39,10 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider when set enabled feature is called then it returns true`() = runTest { val buildMeta = aBuildMeta() val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta) - val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta) + val featureFlagService = createDefaultFeatureFlagService( + providers = setOf(featureFlagProvider), + buildMeta = buildMeta, + ) val result = featureFlagService.setFeatureEnabled(FeatureFlags.Space, true) assertThat(result).isTrue() } @@ -45,7 +51,10 @@ class DefaultFeatureFlagServiceTest { fun `given service with a runtime provider and feature enabled when feature is checked then it returns the correct value`() = runTest { val buildMeta = aBuildMeta() val featureFlagProvider = FakeMutableFeatureFlagProvider(0, buildMeta) - val featureFlagService = DefaultFeatureFlagService(setOf(featureFlagProvider), buildMeta) + val featureFlagService = createDefaultFeatureFlagService( + providers = setOf(featureFlagProvider), + buildMeta = buildMeta + ) featureFlagService.setFeatureEnabled(FeatureFlags.Space, true) featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test { assertThat(awaitItem()).isTrue() @@ -59,11 +68,84 @@ class DefaultFeatureFlagServiceTest { val buildMeta = aBuildMeta() val lowPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(LOW_PRIORITY, buildMeta) val highPriorityFeatureFlagProvider = FakeMutableFeatureFlagProvider(HIGH_PRIORITY, buildMeta) - val featureFlagService = DefaultFeatureFlagService(setOf(lowPriorityFeatureFlagProvider, highPriorityFeatureFlagProvider), buildMeta) + val featureFlagService = createDefaultFeatureFlagService( + providers = setOf(lowPriorityFeatureFlagProvider, highPriorityFeatureFlagProvider), + buildMeta = buildMeta + ) lowPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.Space, false) highPriorityFeatureFlagProvider.setFeatureEnabled(FeatureFlags.Space, true) featureFlagService.isFeatureEnabledFlow(FeatureFlags.Space).test { assertThat(awaitItem()).isTrue() } } + + @Test + fun `getAvailableFeatures should return expected features`() { + val aFinishedLabFeature = FakeFeature( + key = "finished_lab_feature", + title = "Finished Lab Feature", + isFinished = true, + isInLabs = true, + ) + val aFinishedDevFeature = FakeFeature( + key = "finished_dev_feature", + title = "Finished Dev Feature", + isFinished = true, + isInLabs = false, + ) + val anUnfinishedLabFeature = FakeFeature( + key = "unfinished_lab_feature", + title = "Unfinished Lab Feature", + isFinished = false, + isInLabs = true, + ) + val anUnfinishedDevFeature = FakeFeature( + key = "unfinished_dev_feature", + title = "Unfinished Dev Feature", + isFinished = false, + isInLabs = false, + ) + val featureFlagService = createDefaultFeatureFlagService( + features = listOf( + aFinishedLabFeature, + aFinishedDevFeature, + anUnfinishedLabFeature, + anUnfinishedDevFeature, + ), + ) + assertThat( + featureFlagService.getAvailableFeatures( + includeFinishFeatures = false, + isInLabs = true, + ) + ).containsExactly(anUnfinishedLabFeature) + assertThat( + featureFlagService.getAvailableFeatures( + includeFinishFeatures = true, + isInLabs = true, + ) + ).containsExactly(aFinishedLabFeature, anUnfinishedLabFeature) + assertThat( + featureFlagService.getAvailableFeatures( + includeFinishFeatures = false, + isInLabs = false, + ) + ).containsExactly(anUnfinishedDevFeature) + assertThat( + featureFlagService.getAvailableFeatures( + includeFinishFeatures = true, + isInLabs = false, + ) + ).containsExactly(aFinishedDevFeature, anUnfinishedDevFeature) + } } + +private fun createDefaultFeatureFlagService( + providers: Set = emptySet(), + buildMeta: BuildMeta = aBuildMeta(), + features: List = FeatureFlags.entries, +) = DefaultFeatureFlagService( + providers = providers, + buildMeta = buildMeta, + featuresProvider = { features } +)