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

View File

@ -1,33 +1,30 @@
package app.dapk.st.push package app.dapk.st.push
import android.content.Context
import app.dapk.st.domain.push.PushTokenRegistrarPreferences import app.dapk.st.domain.push.PushTokenRegistrarPreferences
import app.dapk.st.push.messaging.MessagingPushTokenRegistrar import app.dapk.st.push.messaging.MessagingPushTokenRegistrar
import app.dapk.st.push.unifiedpush.UnifiedPush
import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar
private val FIREBASE_OPTION = Registrar("Google - Firebase (FCM)") private val FIREBASE_OPTION = Registrar("Google - Firebase (FCM)")
private val NONE = Registrar("None") private val NONE = Registrar("None")
class PushTokenRegistrars( class PushTokenRegistrars(
private val context: Context,
private val messagingPushTokenRegistrar: MessagingPushTokenRegistrar, private val messagingPushTokenRegistrar: MessagingPushTokenRegistrar,
private val unifiedPushRegistrar: UnifiedPushRegistrar, private val unifiedPushRegistrar: UnifiedPushRegistrar,
private val pushTokenStore: PushTokenRegistrarPreferences, private val pushTokenStore: PushTokenRegistrarPreferences,
private val unifiedPush: UnifiedPush, private val state: SelectionState = SelectionState(selection = null),
) : PushTokenRegistrar { ) : PushTokenRegistrar {
private var selection: Registrar? = null
fun options(): List<Registrar> { fun options(): List<Registrar> {
val messagingOption = when (messagingPushTokenRegistrar.isAvailable()) { val messagingOption = when (messagingPushTokenRegistrar.isAvailable()) {
true -> FIREBASE_OPTION true -> FIREBASE_OPTION
else -> null 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()) { private fun defaultSelection() = when (messagingPushTokenRegistrar.isAvailable()) {
true -> FIREBASE_OPTION true -> FIREBASE_OPTION
@ -35,7 +32,7 @@ class PushTokenRegistrars(
} }
suspend fun makeSelection(option: Registrar) { suspend fun makeSelection(option: Registrar) {
selection = option state.selection = option
pushTokenStore.store(option.id) pushTokenStore.store(option.id)
when (option) { when (option) {
NONE -> { NONE -> {
@ -67,7 +64,7 @@ class PushTokenRegistrars(
} }
override fun unregister() { override fun unregister() {
when (selection) { when (state.selection) {
FIREBASE_OPTION -> messagingPushTokenRegistrar.unregister() FIREBASE_OPTION -> messagingPushTokenRegistrar.unregister()
NONE -> { NONE -> {
runCatching { runCatching {
@ -87,4 +84,6 @@ class PushTokenRegistrars(
} }
@JvmInline @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 import org.unifiedpush.android.connector.UnifiedPush
interface UnifiedPush { interface UnifiedPush {
fun saveDistributor(context: Context, distributor: String) = UnifiedPush.saveDistributor(context, distributor) fun saveDistributor(distributor: String)
fun getDistributor(context: Context): String = UnifiedPush.getDistributor(context) fun getDistributor(): String
fun getDistributors(context: Context): List<String> = UnifiedPush.getDistributors(context) fun getDistributors(): List<String>
fun registerApp(context: Context) = UnifiedPush.registerApp(context) fun registerApp()
fun unregisterApp(context: Context) = UnifiedPush.unregisterApp(context) 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) } private val componentFactory: (Context) -> ComponentName = { ComponentName(it, UnifiedPushMessageReceiver::class.java) }
) : PushTokenRegistrar { ) : PushTokenRegistrar {
fun getDistributors() = unifiedPush.getDistributors().map { Registrar(it) }
fun registerSelection(registrar: Registrar) { fun registerSelection(registrar: Registrar) {
log(AppLogTag.PUSH, "UnifiedPush - register: $registrar") log(AppLogTag.PUSH, "UnifiedPush - register: $registrar")
unifiedPush.saveDistributor(context, registrar.id) unifiedPush.saveDistributor(registrar.id)
registerApp() registerApp()
} }
override suspend fun registerCurrentToken() { override suspend fun registerCurrentToken() {
log(AppLogTag.PUSH, "UnifiedPush - register current token") log(AppLogTag.PUSH, "UnifiedPush - register current token")
if (unifiedPush.getDistributor(context).isNotEmpty()) { if (unifiedPush.getDistributor().isNotEmpty()) {
registerApp() registerApp()
} }
} }
@ -33,11 +35,11 @@ class UnifiedPushRegistrar(
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP, PackageManager.DONT_KILL_APP,
) )
unifiedPush.registerApp(context) unifiedPush.registerApp()
} }
override fun unregister() { override fun unregister() {
unifiedPush.unregisterApp(context) unifiedPush.unregisterApp()
context.packageManager.setComponentEnabledSetting( context.packageManager.setComponentEnabledSetting(
componentFactory(context), componentFactory(context),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 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.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test
import test.delegateReturn import test.delegateReturn
import test.runExpectTest import test.runExpectTest
@ -31,7 +32,7 @@ class UnifiedPushRegistrarTest {
@Test @Test
fun `when unregistering, then updates unified push and disables component`() = runExpectTest { fun `when unregistering, then updates unified push and disables component`() = runExpectTest {
fakeUnifiedPush.expect { it.unregisterApp(fakeContext.instance) } fakeUnifiedPush.expect { it.unregisterApp() }
fakePackageManager.instance.expect { fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP) it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
} }
@ -43,8 +44,8 @@ class UnifiedPushRegistrarTest {
@Test @Test
fun `when registering selection, then updates unified push and enables component`() = runExpectTest { fun `when registering selection, then updates unified push and enables component`() = runExpectTest {
fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) } fakeUnifiedPush.expect { it.registerApp() }
fakeUnifiedPush.expect { it.saveDistributor(fakeContext.instance, A_REGISTRAR_SELECTION.id) } fakeUnifiedPush.expect { it.saveDistributor(A_REGISTRAR_SELECTION.id) }
fakePackageManager.instance.expect { fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP) it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
} }
@ -56,8 +57,8 @@ class UnifiedPushRegistrarTest {
@Test @Test
fun `given saved distributor, when registering current token, then updates unified push and enables component`() = runExpectTest { 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.givenDistributor().returns(A_SAVED_DISTRIBUTOR)
fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) } fakeUnifiedPush.expect { it.registerApp() }
fakePackageManager.instance.expect { fakePackageManager.instance.expect {
it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP) it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
} }
@ -69,18 +70,28 @@ class UnifiedPushRegistrarTest {
@Test @Test
fun `given no distributor, when registering current token, then does nothing`() = runExpectTest { fun `given no distributor, when registering current token, then does nothing`() = runExpectTest {
fakeUnifiedPush.givenDistributor(fakeContext.instance).returns("") fakeUnifiedPush.givenDistributor().returns("")
registrar.registerCurrentToken() registrar.registerCurrentToken()
verify(exactly = 0) { fakeUnifiedPush.registerApp(any()) } verify(exactly = 0) { fakeUnifiedPush.registerApp() }
verify { fakePackageManager.instance wasNot Called } 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() { 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 { class FakeComponentName {