NTP-30344 | Girish Suragani | Test cases for RefreshAndSyncDataUseCase (#14634)

Co-authored-by: abhinav-g_navi <abhinav.g@navi.com>
Co-authored-by: venkat-praneeth_navi <venkat.praneeth@navi.com>
This commit is contained in:
Girish Suragani
2025-01-28 12:31:25 +05:30
committed by GitHub
parent d5604ddf97
commit b50f676f2d
2 changed files with 306 additions and 0 deletions

View File

@@ -0,0 +1,229 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.moneymanager.postonboard.dashboard.usecase
import com.navi.common.network.models.RepoResult
import com.navi.moneymanager.common.analytics.DataSyncEventTrackerImpl
import com.navi.moneymanager.common.dataprovider.data.datastore.DataStoreInfoProvider
import com.navi.moneymanager.common.dataprovider.domain.RemoteDataProvider
import com.navi.moneymanager.common.datasync.DBSyncExecutor
import com.navi.moneymanager.common.datasync.SyncStatus
import com.navi.moneymanager.common.datasync.model.DataSyncState
import com.navi.moneymanager.common.network.di.RoomDataStoreInfoProvider
import com.navi.moneymanager.common.network.model.MMConfigResponse
import com.navi.moneymanager.common.utils.Constants.LAST_REFRESH_SUCCESSFUL_TIMESTAMP
import com.navi.moneymanager.testsetup.DataSyncHelperTest
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.RelaxedMockK
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkAll
import junit.framework.TestCase.assertFalse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
class RefreshAndSyncDataUseCaseTest {
@RelaxedMockK
@RoomDataStoreInfoProvider
private lateinit var dbDataStoreProvider: DataStoreInfoProvider
@RelaxedMockK private lateinit var remoteDataProvider: RemoteDataProvider
@RelaxedMockK private lateinit var dBSyncExecutor: DBSyncExecutor
private val dataSyncHelperTest = DataSyncHelperTest()
private val testDispatcher = StandardTestDispatcher()
private lateinit var refreshAndSyncDataUseCase: RefreshAndSyncDataUseCase
@Before
fun setUp() {
MockKAnnotations.init(this, relaxed = true)
Dispatchers.setMain(testDispatcher)
mockkObject(DataSyncEventTrackerImpl)
refreshAndSyncDataUseCase =
RefreshAndSyncDataUseCase(
remoteDataProvider = remoteDataProvider,
dbDataStoreProvider = dbDataStoreProvider,
dBSyncExecutor = dBSyncExecutor,
)
}
@After
fun tearDown() {
Dispatchers.resetMain()
unmockkAll()
}
@Test
fun `execute should refresh data and start polling`() = runBlocking {
val mockResponse = mockk<MMConfigResponse>(relaxed = true)
val refreshDataResponse = dataSyncHelperTest.createRefreshDataResponse()
val mockSyncStatusFlow =
dataSyncHelperTest.createMockSyncStatusFlow(
DataSyncState.Completed,
DataSyncState.Completed,
)
val isRefreshingFlow = refreshAndSyncDataUseCase.isRefreshing
val refreshingStates = mutableListOf<Boolean>()
coEvery { remoteDataProvider.refreshData() } returns refreshDataResponse
every { dBSyncExecutor.dataSyncStatus } returns mockSyncStatusFlow
every { dBSyncExecutor.initDBSyncExecutor(any()) } just Runs
dataSyncHelperTest.mockEventTrackers()
val job = launch { refreshAndSyncDataUseCase.execute(this, mockResponse) }
val job2 = launch { isRefreshingFlow.collect { refreshingStates.add(it) } }
coVerify { remoteDataProvider.refreshData() }
coVerify { dbDataStoreProvider.saveLongData(LAST_REFRESH_SUCCESSFUL_TIMESTAMP, 1000L) }
assertFalse(refreshingStates.last())
job.cancelAndJoin()
job2.cancelAndJoin()
}
@Test
fun `execute should not start polling if response data is null`() = runBlocking {
val mockResponse = mockk<MMConfigResponse>(relaxed = true)
coEvery { remoteDataProvider.refreshData() } returns RepoResult(null)
refreshAndSyncDataUseCase.execute(this, mockResponse)
coVerify { remoteDataProvider.refreshData() }
coVerify(exactly = 0) { dbDataStoreProvider.saveLongData(any(), any()) }
}
@Test
fun `execute should stop polling if account sync fails`() = runBlocking {
val mockResponse = dataSyncHelperTest.createMockConfigResponse()
val refreshDataResponse = dataSyncHelperTest.createRefreshDataResponse()
val mockSyncStatusFlow =
dataSyncHelperTest.createMockSyncStatusFlow(
DataSyncState.InProgress,
DataSyncState.InProgress,
)
val pollingStatusResponse =
dataSyncHelperTest.createPollingStatusResponse(
accountDetailsStatus = SyncStatus.FAILED.name
)
coEvery { remoteDataProvider.refreshData() } returns refreshDataResponse
coEvery { remoteDataProvider.pollSyncStatus(any()) } returns pollingStatusResponse
every { dBSyncExecutor.dataSyncStatus } returns mockSyncStatusFlow
every { dBSyncExecutor.initDBSyncExecutor(any()) } just Runs
dataSyncHelperTest.mockEventTrackers()
val job = launch { refreshAndSyncDataUseCase.execute(this, mockResponse) }
coVerify { DataSyncEventTrackerImpl.pollingStopped() }
job.cancelAndJoin()
}
@Test
fun `execute should stop polling if all transactions are synced`() = runBlocking {
val mockResponse = dataSyncHelperTest.createMockConfigResponse()
val refreshDataResponse = dataSyncHelperTest.createRefreshDataResponse()
val mockSyncStatusFlow =
dataSyncHelperTest.createMockSyncStatusFlow(
DataSyncState.InProgress,
DataSyncState.InProgress,
)
val pollingStatusResponse =
dataSyncHelperTest.createPollingStatusResponse(
oldMonthTxnsStatus = SyncStatus.COMPLETED.name,
currMonthTxnsStatus = SyncStatus.COMPLETED.name,
accountDetailsStatus = SyncStatus.COMPLETED.name,
)
coEvery { remoteDataProvider.refreshData() } returns refreshDataResponse
coEvery { remoteDataProvider.pollSyncStatus(any()) } returns pollingStatusResponse
every { dBSyncExecutor.dataSyncStatus } returns mockSyncStatusFlow
every { dBSyncExecutor.initDBSyncExecutor(any()) } just Runs
dataSyncHelperTest.mockEventTrackers()
val job = launch { refreshAndSyncDataUseCase.execute(this, mockResponse) }
coVerify { DataSyncEventTrackerImpl.pollingStopped() }
job.cancelAndJoin()
}
@Test
fun `execute should stop polling when all transactions sync failed`() = runBlocking {
val mockResponse = dataSyncHelperTest.createMockConfigResponse()
val refreshDataResponse = dataSyncHelperTest.createRefreshDataResponse()
val mockSyncStatusFlow =
dataSyncHelperTest.createMockSyncStatusFlow(
DataSyncState.InProgress,
DataSyncState.InProgress,
)
val pollingStatusResponse =
dataSyncHelperTest.createPollingStatusResponse(
accountDetailsStatus = SyncStatus.COMPLETED.name,
oldMonthTxnsStatus = SyncStatus.FAILED.name,
currMonthTxnsStatus = SyncStatus.FAILED.name,
)
coEvery { remoteDataProvider.refreshData() } returns refreshDataResponse
coEvery { remoteDataProvider.pollSyncStatus(any()) } returns pollingStatusResponse
every { dBSyncExecutor.dataSyncStatus } returns mockSyncStatusFlow
every { dBSyncExecutor.initDBSyncExecutor(any()) } just Runs
dataSyncHelperTest.mockEventTrackers()
val job = launch { refreshAndSyncDataUseCase.execute(this, mockResponse) }
coVerify { DataSyncEventTrackerImpl.pollingStopped() }
job.cancelAndJoin()
}
@Test
fun `execute should stop polling when current month sync failed but all months sync completed`() =
runBlocking {
val mockResponse = dataSyncHelperTest.createMockConfigResponse()
val refreshDataResponse = dataSyncHelperTest.createRefreshDataResponse()
val mockSyncStatusFlow =
dataSyncHelperTest.createMockSyncStatusFlow(
DataSyncState.InProgress,
DataSyncState.InProgress,
)
val pollingStatusResponse =
dataSyncHelperTest.createPollingStatusResponse(
accountDetailsStatus = SyncStatus.COMPLETED.name,
oldMonthTxnsStatus = SyncStatus.COMPLETED.name,
currMonthTxnsStatus = SyncStatus.FAILED.name,
)
coEvery { remoteDataProvider.refreshData() } returns refreshDataResponse
coEvery { remoteDataProvider.pollSyncStatus(any()) } returns pollingStatusResponse
every { dBSyncExecutor.dataSyncStatus } returns mockSyncStatusFlow
every { dBSyncExecutor.initDBSyncExecutor(any()) } just Runs
dataSyncHelperTest.mockEventTrackers()
val job = launch { refreshAndSyncDataUseCase.execute(this, mockResponse) }
coVerify { DataSyncEventTrackerImpl.pollingStopped() }
job.cancelAndJoin()
}
}

View File

@@ -0,0 +1,77 @@
/*
*
* * Copyright © 2025 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.moneymanager.testsetup
import com.navi.common.network.models.RepoResult
import com.navi.moneymanager.common.analytics.DataSyncEventTrackerImpl
import com.navi.moneymanager.common.datasync.model.DataSyncState
import com.navi.moneymanager.common.datasync.model.DataSyncStatus
import com.navi.moneymanager.common.network.model.MMConfigResponse
import com.navi.moneymanager.common.network.model.PollingConfig
import com.navi.moneymanager.common.network.model.PollingStatusResponse
import com.navi.moneymanager.common.network.model.RefreshDataResponse
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
class DataSyncHelperTest @Inject constructor() {
fun createRefreshDataResponse(): RepoResult<RefreshDataResponse> {
return RepoResult(
data =
RefreshDataResponse(
requestId = "testRequestId",
lastRefreshSuccessTimestamp = 1000L,
isAARefreshApplicable = true,
)
)
}
fun createMockSyncStatusFlow(
allMonthsStatus: DataSyncState,
currentMonthStatus: DataSyncState,
): MutableStateFlow<DataSyncStatus> {
return MutableStateFlow(
DataSyncStatus(
allMonthsSyncStatus = allMonthsStatus,
currentMonthSyncStatus = currentMonthStatus,
)
)
}
fun createMockConfigResponse(): MMConfigResponse {
return MMConfigResponse(dataSyncPollingConfig = PollingConfig(10, 0, 5))
}
fun createPollingStatusResponse(
oldMonthTxnsStatus: String? = null,
currMonthTxnsStatus: String? = null,
accountDetailsStatus: String? = null,
): RepoResult<PollingStatusResponse> {
return RepoResult(
data =
PollingStatusResponse(
oldMonthTxnsStatus = oldMonthTxnsStatus,
currMonthTxnsStatus = currMonthTxnsStatus,
accountDetailsStatus = accountDetailsStatus,
)
)
}
fun mockEventTrackers() {
every {
DataSyncEventTrackerImpl.refreshStarted(any(), any())
DataSyncEventTrackerImpl.periodicTaskSchedulerCreated()
DataSyncEventTrackerImpl.pollingStarted()
DataSyncEventTrackerImpl.pollingTimeout()
DataSyncEventTrackerImpl.pollingStopped()
} just Runs
}
}