diff --git a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt new file mode 100644 index 0000000000..eaf9730f21 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt @@ -0,0 +1,11 @@ +package com.tangem.tap.data + +import com.google.firebase.messaging.FirebaseMessaging +import com.tangem.utils.notifications.PushNotificationsTokenProvider +import kotlinx.coroutines.tasks.await + +class FirebasePushNotificationsTokenProvider : PushNotificationsTokenProvider { + override suspend fun getToken(): String { + return FirebaseMessaging.getInstance().token.await() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt b/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt new file mode 100644 index 0000000000..063efaf3dc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.di.data + +import com.tangem.tap.data.FirebasePushNotificationsTokenProvider +import com.tangem.utils.notifications.PushNotificationsTokenProvider +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface PushNotificationsModule { + + @Binds + @Singleton + fun bindPushNotificationsTokenProvider(impl: FirebasePushNotificationsTokenProvider): PushNotificationsTokenProvider +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt index caad0bce3a..fe5ea6d52f 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt @@ -1,7 +1,9 @@ package com.tangem.tap.di.domain import com.tangem.domain.notifications.GetApplicationIdUseCase +import com.tangem.domain.notifications.SendPushTokenUseCase import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.utils.notifications.PushNotificationsTokenProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -18,4 +20,16 @@ internal object NotificationsDomainModule { GetApplicationIdUseCase( notificationsRepository = notificationsRepository, ) + + @Provides + @Singleton + fun providesSendPushTokenUseCase( + notificationsRepository: NotificationsRepository, + getApplicationIdUseCase: GetApplicationIdUseCase, + pushNotificationsTokenProvider: PushNotificationsTokenProvider, + ): SendPushTokenUseCase = SendPushTokenUseCase( + notificationsRepository = notificationsRepository, + getApplicationIdUseCase = getApplicationIdUseCase, + pushNotificationsTokenProvider = pushNotificationsTokenProvider, + ) } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/notifications/PushNotificationsTokenProvider.kt b/core/utils/src/main/java/com/tangem/utils/notifications/PushNotificationsTokenProvider.kt new file mode 100644 index 0000000000..2d14d8c9ad --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/notifications/PushNotificationsTokenProvider.kt @@ -0,0 +1,6 @@ +package com.tangem.utils.notifications + +interface PushNotificationsTokenProvider { + + suspend fun getToken(): String +} \ No newline at end of file diff --git a/domain/notifications/build.gradle.kts b/domain/notifications/build.gradle.kts index ae59c08bd3..eba62986ce 100644 --- a/domain/notifications/build.gradle.kts +++ b/domain/notifications/build.gradle.kts @@ -5,6 +5,7 @@ plugins { } dependencies { + implementation(projects.core.utils) implementation(projects.domain.core) implementation(projects.domain.notifications.models) diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt new file mode 100644 index 0000000000..45617cf6e4 --- /dev/null +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/SendPushTokenUseCase.kt @@ -0,0 +1,19 @@ +package com.tangem.domain.notifications + +import arrow.core.Either +import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.utils.notifications.PushNotificationsTokenProvider + +class SendPushTokenUseCase( + private val notificationsRepository: NotificationsRepository, + private val getApplicationIdUseCase: GetApplicationIdUseCase, + private val pushNotificationsTokenProvider: PushNotificationsTokenProvider, +) { + + suspend operator fun invoke(): Either = Either.catch { + val applicationId = getApplicationIdUseCase().getOrNull() + ?: error("Application ID not found") + val token = pushNotificationsTokenProvider.getToken() + notificationsRepository.sendPushToken(applicationId, token) + } +} \ No newline at end of file diff --git a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt index 8f562e98b7..350f5bad73 100644 --- a/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt +++ b/domain/notifications/src/test/java/com/tangem/domain/notifications/GetApplicationIdUseCaseTest.kt @@ -97,7 +97,6 @@ class GetApplicationIdUseCaseTest { val results = coroutineScope { List(PARALLEL_COUNT) { async { - delay(100) useCase() } }.awaitAll() diff --git a/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt b/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt new file mode 100644 index 0000000000..aeb18310a1 --- /dev/null +++ b/domain/notifications/src/test/java/com/tangem/domain/notifications/SendPushTokenUseCaseTest.kt @@ -0,0 +1,79 @@ +package com.tangem.domain.notifications + +import arrow.core.Either +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.utils.notifications.PushNotificationsTokenProvider +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +class SendPushTokenUseCaseTest { + + private lateinit var notificationsRepository: NotificationsRepository + private lateinit var getApplicationIdUseCase: GetApplicationIdUseCase + private lateinit var pushNotificationsTokenProvider: PushNotificationsTokenProvider + private lateinit var sendPushTokenUseCase: SendPushTokenUseCase + + @Before + fun setup() { + notificationsRepository = mockk() + getApplicationIdUseCase = mockk() + pushNotificationsTokenProvider = mockk() + sendPushTokenUseCase = SendPushTokenUseCase( + notificationsRepository = notificationsRepository, + getApplicationIdUseCase = getApplicationIdUseCase, + pushNotificationsTokenProvider = pushNotificationsTokenProvider, + ) + } + + @Test + fun `GIVEN valid application ID and token WHEN invoke THEN token is sent successfully`() = runTest { + // GIVEN + val applicationId = "test-app-id" + val token = "test-token" + coEvery { getApplicationIdUseCase() } returns Either.Right(applicationId) + coEvery { pushNotificationsTokenProvider.getToken() } returns token + coEvery { notificationsRepository.sendPushToken(applicationId, token) } returns Unit + + // WHEN + val result = sendPushTokenUseCase() + + // THEN + assertThat(result).isEqualTo(Either.Right(Unit)) + coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) } + } + + @Test + fun `GIVEN application ID is not found WHEN invoke THEN throws error`() = runTest { + // GIVEN + coEvery { getApplicationIdUseCase() } returns Either.Left(Throwable("Application ID not found")) + + // WHEN & THEN + val result = sendPushTokenUseCase() + assertThat(result.isLeft()).isTrue() + assertThat(result.fold({ it.message }, { null })).isEqualTo("Application ID not found") + coVerify(exactly = 0) { notificationsRepository.sendPushToken(any(), any()) } + } + + @Test + fun `GIVEN repository throws error WHEN invoke THEN returns error`() = runTest { + // GIVEN + val applicationId = "test-app-id" + val token = "test-token" + val expectedError = RuntimeException("Network error") + coEvery { getApplicationIdUseCase() } returns Either.Right(applicationId) + coEvery { pushNotificationsTokenProvider.getToken() } returns token + coEvery { notificationsRepository.sendPushToken(applicationId, token) } throws expectedError + + // WHEN + val result = sendPushTokenUseCase() + + // THEN + assertThat(result).isEqualTo(Either.Left(expectedError)) + coVerify(exactly = 1) { notificationsRepository.sendPushToken(applicationId, token) } + } +} \ No newline at end of file