adding test around top level push registrar switching

This commit is contained in:
Adam Brown 2022-11-03 15:40:03 +00:00
parent 1991468a01
commit b417b83fdf
6 changed files with 255 additions and 32 deletions

View File

@ -9,7 +9,7 @@ import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
import app.dapk.st.firebase.messaging.Messaging
import app.dapk.st.push.messaging.MessagingPushTokenRegistrar
import app.dapk.st.push.unifiedpush.UnifiedPush
import app.dapk.st.push.unifiedpush.UnifiedPushImpl
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
class PushModule(
@ -22,9 +22,8 @@ class PushModule(
) : ProvidableModule {
private val registrars by unsafeLazy {
val unifiedPush = object : UnifiedPush {}
val unifiedPush = UnifiedPushImpl(context)
PushTokenRegistrars(
context,
MessagingPushTokenRegistrar(
errorTracker,
pushHandler,
@ -32,7 +31,6 @@ class PushModule(
),
UnifiedPushRegistrar(context, unifiedPush),
PushTokenRegistrarPreferences(preferences),
unifiedPush,
)
}

View File

@ -1,33 +1,30 @@
package app.dapk.st.push
import android.content.Context
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
import app.dapk.st.push.messaging.MessagingPushTokenRegistrar
import app.dapk.st.push.unifiedpush.UnifiedPush
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
private val FIREBASE_OPTION = Registrar("Google - Firebase (FCM)")
private val NONE = Registrar("None")
class PushTokenRegistrars(
private val context: Context,
private val messagingPushTokenRegistrar: MessagingPushTokenRegistrar,
private val unifiedPushRegistrar: UnifiedPushRegistrar,
private val pushTokenStore: PushTokenRegistrarPreferences,
private val unifiedPush: UnifiedPush,
private val state: SelectionState = SelectionState(selection = null),
) : PushTokenRegistrar {
private var selection: Registrar? = null
fun options(): List<Registrar> {
val messagingOption = when (messagingPushTokenRegistrar.isAvailable()) {
true -> FIREBASE_OPTION
else -> null
}
return listOfNotNull(NONE, messagingOption) + unifiedPush.getDistributors(context).map { Registrar(it) }
return listOfNotNull(NONE, messagingOption) + unifiedPushRegistrar.getDistributors()
}
suspend fun currentSelection() = selection ?: (pushTokenStore.currentSelection()?.let { Registrar(it) } ?: defaultSelection()).also { selection = it }
suspend fun currentSelection() = state.selection ?: (readStoredSelection() ?: defaultSelection()).also { state.selection = it }
private suspend fun readStoredSelection() = pushTokenStore.currentSelection()?.let { Registrar(it) }?.takeIf { options().contains(it) }
private fun defaultSelection() = when (messagingPushTokenRegistrar.isAvailable()) {
true -> FIREBASE_OPTION
@ -35,7 +32,7 @@ class PushTokenRegistrars(
}
suspend fun makeSelection(option: Registrar) {
selection = option
state.selection = option
pushTokenStore.store(option.id)
when (option) {
NONE -> {
@ -67,7 +64,7 @@ class PushTokenRegistrars(
}
override fun unregister() {
when (selection) {
when (state.selection) {
FIREBASE_OPTION -> messagingPushTokenRegistrar.unregister()
NONE -> {
runCatching {
@ -87,4 +84,6 @@ class PushTokenRegistrars(
}
@JvmInline
value class Registrar(val id: String)
value class Registrar(val id: String)
data class SelectionState(var selection: Registrar?)

View File

@ -4,9 +4,17 @@ import android.content.Context
import org.unifiedpush.android.connector.UnifiedPush
interface UnifiedPush {
fun saveDistributor(context: Context, distributor: String) = UnifiedPush.saveDistributor(context, distributor)
fun getDistributor(context: Context): String = UnifiedPush.getDistributor(context)
fun getDistributors(context: Context): List<String> = UnifiedPush.getDistributors(context)
fun registerApp(context: Context) = UnifiedPush.registerApp(context)
fun unregisterApp(context: Context) = UnifiedPush.unregisterApp(context)
fun saveDistributor(distributor: String)
fun getDistributor(): String
fun getDistributors(): List<String>
fun registerApp()
fun unregisterApp()
}
internal class UnifiedPushImpl(private val context: Context) : app.dapk.st.push.unifiedpush.UnifiedPush {
override fun saveDistributor(distributor: String) = UnifiedPush.saveDistributor(context, distributor)
override fun getDistributor(): String = UnifiedPush.getDistributor(context)
override fun getDistributors(): List<String> = UnifiedPush.getDistributors(context)
override fun registerApp() = UnifiedPush.registerApp(context)
override fun unregisterApp() = UnifiedPush.unregisterApp(context)
}

View File

@ -14,15 +14,17 @@ class UnifiedPushRegistrar(
private val componentFactory: (Context) -> ComponentName = { ComponentName(it, UnifiedPushMessageReceiver::class.java) }
) : PushTokenRegistrar {
fun getDistributors() = unifiedPush.getDistributors().map { Registrar(it) }
fun registerSelection(registrar: Registrar) {
log(AppLogTag.PUSH, "UnifiedPush - register: $registrar")
unifiedPush.saveDistributor(context, registrar.id)
unifiedPush.saveDistributor(registrar.id)
registerApp()
}
override suspend fun registerCurrentToken() {
log(AppLogTag.PUSH, "UnifiedPush - register current token")
if (unifiedPush.getDistributor(context).isNotEmpty()) {
if (unifiedPush.getDistributor().isNotEmpty()) {
registerApp()
}
}
@ -33,11 +35,11 @@ class UnifiedPushRegistrar(
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP,
)
unifiedPush.registerApp(context)
unifiedPush.registerApp()
}
override fun unregister() {
unifiedPush.unregisterApp(context)
unifiedPush.unregisterApp()
context.packageManager.setComponentEnabledSetting(
componentFactory(context),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,

View File

@ -0,0 +1,205 @@
package app.dapk.st.push
import app.dapk.st.domain.push.PushTokenRegistrarPreferences
import app.dapk.st.push.messaging.MessagingPushTokenRegistrar
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
import io.mockk.*
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import test.delegateReturn
import test.runExpectTest
private val UNIFIED_PUSH = Registrar("unified-push option")
private val NONE = Registrar("None")
private val FIREBASE = Registrar("Google - Firebase (FCM)")
private val UNIFIED_PUSH_DISTRIBUTORS = listOf(UNIFIED_PUSH)
class PushTokenRegistrarsTest {
private val fakeMessagingPushRegistrar = FakeMessagingPushRegistrar()
private val fakeUnifiedPushRegistrar = FakeUnifiedPushRegistrar()
private val fakePushTokenRegistrarPreferences = FakePushTokenRegistrarPreferences()
private val selectionState = SelectionState(selection = null)
private val registrars = PushTokenRegistrars(
fakeMessagingPushRegistrar.instance,
fakeUnifiedPushRegistrar.instance,
fakePushTokenRegistrarPreferences.instance,
selectionState,
)
@Test
fun `given messaging is available, when reading options, then returns firebase and unified push`() {
fakeMessagingPushRegistrar.givenIsAvailable().returns(true)
fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS)
val result = registrars.options()
result shouldBeEqualTo listOf(Registrar("None"), FIREBASE) + UNIFIED_PUSH_DISTRIBUTORS
}
@Test
fun `given messaging is not available, when reading options, then returns unified push`() {
fakeMessagingPushRegistrar.givenIsAvailable().returns(false)
fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS)
val result = registrars.options()
result shouldBeEqualTo listOf(Registrar("None")) + UNIFIED_PUSH_DISTRIBUTORS
}
@Test
fun `given no saved selection and messaging is not available, when reading default selection, then returns none`() = runTest {
fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(null)
fakeMessagingPushRegistrar.givenIsAvailable().returns(false)
val result = registrars.currentSelection()
result shouldBeEqualTo NONE
}
@Test
fun `given no saved selection and messaging is available, when reading default selection, then returns firebase`() = runTest {
fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(null)
fakeMessagingPushRegistrar.givenIsAvailable().returns(true)
val result = registrars.currentSelection()
result shouldBeEqualTo FIREBASE
}
@Test
fun `given saved selection and is a option, when reading default selection, then returns selection`() = runTest {
fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS)
fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(FIREBASE.id)
fakeMessagingPushRegistrar.givenIsAvailable().returns(true)
val result = registrars.currentSelection()
result shouldBeEqualTo FIREBASE
}
@Test
fun `given saved selection and is not an option, when reading default selection, then returns next default`() = runTest {
fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS)
fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(FIREBASE.id)
fakeMessagingPushRegistrar.givenIsAvailable().returns(false)
val result = registrars.currentSelection()
result shouldBeEqualTo NONE
}
@Test
fun `when selecting none, then stores and unregisters`() = runExpectTest {
fakePushTokenRegistrarPreferences.instance.expect { it.store(NONE.id) }
fakeMessagingPushRegistrar.instance.expect { it.unregister() }
fakeUnifiedPushRegistrar.instance.expect { it.unregister() }
registrars.makeSelection(NONE)
verifyExpects()
}
@Test
fun `when selecting firebase, then stores and unregisters unifiedpush`() = runExpectTest {
fakePushTokenRegistrarPreferences.instance.expect { it.store(FIREBASE.id) }
fakeMessagingPushRegistrar.instance.expect { it.registerCurrentToken() }
fakeUnifiedPushRegistrar.instance.expect { it.unregister() }
registrars.makeSelection(FIREBASE)
verifyExpects()
}
@Test
fun `when selecting unified push, then stores and unregisters firebase`() = runExpectTest {
fakePushTokenRegistrarPreferences.instance.expect { it.store(UNIFIED_PUSH.id) }
fakeMessagingPushRegistrar.instance.expect { it.unregister() }
fakeUnifiedPushRegistrar.instance.expect { it.registerSelection(UNIFIED_PUSH) }
registrars.makeSelection(UNIFIED_PUSH)
verifyExpects()
}
@Test
fun `given unified push selected, when registering current token, then delegates`() = runExpectTest {
selectionState.selection = UNIFIED_PUSH
fakeUnifiedPushRegistrar.instance.expect { it.registerCurrentToken() }
registrars.registerCurrentToken()
verifyExpects()
}
@Test
fun `given firebase selected, when registering current token, then delegates`() = runExpectTest {
selectionState.selection = FIREBASE
fakeMessagingPushRegistrar.instance.expect { it.registerCurrentToken() }
registrars.registerCurrentToken()
verifyExpects()
}
@Test
fun `given none selected, when registering current token, then does nothing`() = runExpectTest {
selectionState.selection = NONE
registrars.registerCurrentToken()
verify { fakeMessagingPushRegistrar.instance wasNot Called }
verify { fakeUnifiedPushRegistrar.instance wasNot Called }
}
@Test
fun `given unified push selected, when unregistering, then delegates`() = runExpectTest {
selectionState.selection = UNIFIED_PUSH
fakeUnifiedPushRegistrar.instance.expect { it.unregister() }
registrars.unregister()
verifyExpects()
}
@Test
fun `given firebase selected, when unregistering, then delegates`() = runExpectTest {
selectionState.selection = FIREBASE
fakeMessagingPushRegistrar.instance.expect { it.unregister() }
registrars.unregister()
verifyExpects()
}
@Test
fun `given none selected, when unregistering, then unregisters all`() = runExpectTest {
selectionState.selection = NONE
fakeUnifiedPushRegistrar.instance.expect { it.unregister() }
fakeMessagingPushRegistrar.instance.expect { it.unregister() }
registrars.unregister()
verifyExpects()
}
}
class FakeMessagingPushRegistrar {
val instance = mockk<MessagingPushTokenRegistrar>()
fun givenIsAvailable() = every { instance.isAvailable() }.delegateReturn()
}
class FakeUnifiedPushRegistrar {
val instance = mockk<UnifiedPushRegistrar>()
fun givenDistributors() = every { instance.getDistributors() }.delegateReturn()
}
class FakePushTokenRegistrarPreferences {
val instance = mockk<PushTokenRegistrarPreferences>()
fun givenCurrentSelection() = coEvery { instance.currentSelection() }.delegateReturn()
}

View File

@ -10,6 +10,7 @@ import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import test.delegateReturn
import test.runExpectTest
@ -31,7 +32,7 @@ class UnifiedPushRegistrarTest {
@Test
fun `when unregistering, then updates unified push and disables component`() = runExpectTest {
fakeUnifiedPush.expect { it.unregisterApp(fakeContext.instance) }
fakeUnifiedPush.expect { it.unregisterApp() }
fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
}
@ -43,8 +44,8 @@ class UnifiedPushRegistrarTest {
@Test
fun `when registering selection, then updates unified push and enables component`() = runExpectTest {
fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) }
fakeUnifiedPush.expect { it.saveDistributor(fakeContext.instance, A_REGISTRAR_SELECTION.id) }
fakeUnifiedPush.expect { it.registerApp() }
fakeUnifiedPush.expect { it.saveDistributor(A_REGISTRAR_SELECTION.id) }
fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
}
@ -56,8 +57,8 @@ class UnifiedPushRegistrarTest {
@Test
fun `given saved distributor, when registering current token, then updates unified push and enables component`() = runExpectTest {
fakeUnifiedPush.givenDistributor(fakeContext.instance).returns(A_SAVED_DISTRIBUTOR)
fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) }
fakeUnifiedPush.givenDistributor().returns(A_SAVED_DISTRIBUTOR)
fakeUnifiedPush.expect { it.registerApp() }
fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
}
@ -69,18 +70,28 @@ class UnifiedPushRegistrarTest {
@Test
fun `given no distributor, when registering current token, then does nothing`() = runExpectTest {
fakeUnifiedPush.givenDistributor(fakeContext.instance).returns("")
fakeUnifiedPush.givenDistributor().returns("")
registrar.registerCurrentToken()
verify(exactly = 0) { fakeUnifiedPush.registerApp(any()) }
verify(exactly = 0) { fakeUnifiedPush.registerApp() }
verify { fakePackageManager.instance wasNot Called }
}
@Test
fun `given distributors, then returns them as Registrars`() {
fakeUnifiedPush.givenDistributors().returns(listOf("a", "b"))
val result = registrar.getDistributors()
result shouldBeEqualTo listOf(Registrar("a"), Registrar("b"))
}
}
class FakeUnifiedPush : UnifiedPush by mockk() {
fun givenDistributor(context: Context) = every { getDistributor(context) }.delegateReturn()
fun givenDistributor() = every { getDistributor() }.delegateReturn()
fun givenDistributors() = every { getDistributors() }.delegateReturn()
}
class FakeComponentName {