Skip to content

Commit

Permalink
feature: make ReviewInfoRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
kosenda committed Nov 8, 2023
1 parent 7e6bbed commit a43fab2
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ksnd.hiraganaconverter.view

import ksnd.hiraganaconverter.core.data.inappupdate.InAppUpdateState
import ksnd.hiraganaconverter.core.model.ReviewInfo
import ksnd.hiraganaconverter.core.model.ui.FontType
import ksnd.hiraganaconverter.core.model.ui.Theme

data class MainActivityUiState(
val theme: Theme = Theme.AUTO,
val fontType: FontType = FontType.YUSEI_MAGIC,
val inAppUpdateState: InAppUpdateState = InAppUpdateState.Requesting,
val reviewInfo: ReviewInfo = ReviewInfo(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,6 @@ class MainActivityViewModelTest {

override suspend fun updateUseInAppUpdate(isUsed: Boolean) {}

override suspend fun countUpTotalConvertCount(): Int = 1

suspend fun emit(theme: Theme) {
this.theme.emit(theme)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ object PreferenceKeys {
val FONT_TYPE = stringPreferencesKey("font_type")
val LAST_CONVERT_DATE = stringPreferencesKey("last_convert_date")
val TODAY_CONVERT_COUNT = intPreferencesKey("today_convert_count")
val TOTAL_CONVERT_COUNT = intPreferencesKey("total_convert_count")
val ENABLE_IN_APP_UPDATE = booleanPreferencesKey("enable_in_app_update")
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package ksnd.hiraganaconverter.core.data.di

import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.Serializer
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
Expand All @@ -10,6 +13,11 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import ksnd.hiraganaconverter.core.model.ReviewInfo
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Singleton

@Module
Expand All @@ -24,4 +32,29 @@ object DataStoreModule {
},
)
}

@Provides
@Singleton
fun provideCalcInputRequiredInterestDataStore(
@ApplicationContext context: Context,
): DataStore<ReviewInfo> {
return DataStoreFactory.create(
serializer = ReviewInfoSerializer,
produceFile = { context.preferencesDataStoreFile("ReviewInfoDataStore") },
)
}
}

object ReviewInfoSerializer : Serializer<ReviewInfo> {
override val defaultValue = ReviewInfo()
override suspend fun readFrom(input: InputStream): ReviewInfo {
try {
return Json.decodeFromString(ReviewInfo.serializer(), input.readBytes().decodeToString())
} catch (serialization: SerializationException) {
throw CorruptionException("Unable to read data", serialization)
}
}
override suspend fun writeTo(t: ReviewInfo, output: OutputStream) {
output.write(Json.encodeToString(ReviewInfo.serializer(), t).encodeToByteArray())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ksnd.hiraganaconverter.core.data.di

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ksnd.hiraganaconverter.core.domain.repository.ConvertHistoryRepository
import ksnd.hiraganaconverter.core.data.repository.ConvertHistoryRepositoryImpl
import ksnd.hiraganaconverter.core.data.repository.ReviewInfoRepositoryImpl
import ksnd.hiraganaconverter.core.domain.repository.ReviewInfoRepository
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class ReviewInfoRepositoryModule {
@Binds
@Singleton
abstract fun bindConvertHistoryRepository(impl: ReviewInfoRepositoryImpl): ReviewInfoRepository
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,6 @@ class DataStoreRepositoryImpl @Inject constructor(
}
}

private suspend fun getTotalConvertCount(): Int? {
return dataStore.data
.catch { exception ->
Timber.e("DataStore: %s".format(exception))
if (exception is IOException) emit(emptyPreferences())
}
.map { preferences ->
preferences[PreferenceKeys.TOTAL_CONVERT_COUNT]
}.firstOrNull()
}

override fun enableInAppUpdate(): Flow<Boolean> {
return dataStore.data
.catch { exception ->
Expand Down Expand Up @@ -120,10 +109,4 @@ class DataStoreRepositoryImpl @Inject constructor(
private suspend fun updateTodayConvertCount(convertCount: Int) {
dataStore.edit { it[PreferenceKeys.TODAY_CONVERT_COUNT] = convertCount }
}

override suspend fun countUpTotalConvertCount(): Int {
val totalConvertCount = (getTotalConvertCount() ?: 0) + 1
dataStore.edit { it[PreferenceKeys.TOTAL_CONVERT_COUNT] = totalConvertCount }
return totalConvertCount
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ksnd.hiraganaconverter.core.data.repository

import androidx.datastore.core.DataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import ksnd.hiraganaconverter.core.domain.repository.ReviewInfoRepository
import ksnd.hiraganaconverter.core.model.ReviewInfo
import javax.inject.Inject

class ReviewInfoRepositoryImpl @Inject constructor(
private val dataStore: DataStore<ReviewInfo>,
) : ReviewInfoRepository {
override val reviewInfo: Flow<ReviewInfo> = dataStore.data

override suspend fun countUpTotalConvertCount(): Int {
val newCount = (reviewInfo.firstOrNull()?.totalConvertCount ?: 0) + 1
dataStore.updateData { it.copy(totalConvertCount = newCount) }
return newCount
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,4 @@ class DataStoreRepositoryImplTest {
dataStoreRepository.updateUseInAppUpdate(false)
assertThat(dataStoreRepository.enableInAppUpdate().first()).isFalse()
}

@Test
fun countUpTotalConvertCount_first_is1() = runTest {
assertThat(dataStoreRepository.countUpTotalConvertCount()).isEqualTo(1)
}

@Test
fun countUpTotalConvertCount_countUp_isCountUp() = runTest {
repeat(5) { assertThat(dataStoreRepository.countUpTotalConvertCount()).isEqualTo(it + 1) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ksnd.hiraganaconverter.core.data.repository

import android.content.Context
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import ksnd.hiraganaconverter.core.data.di.ReviewInfoSerializer
import ksnd.hiraganaconverter.core.testing.MainDispatcherRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ReviewInfoRepositoryImplTest {
@get: Rule
val mainDispatcherRule = MainDispatcherRule()

private val context = ApplicationProvider.getApplicationContext<Context>()
private val dataStore = DataStoreFactory.create(
serializer = ReviewInfoSerializer,
produceFile = { context.preferencesDataStoreFile("TestReviewInfoDataStore") },
)
private val repository = ReviewInfoRepositoryImpl(dataStore = dataStore)

@Test

fun countUpTotalConvertCount_first_is1() = runTest {
assertThat(repository.countUpTotalConvertCount()).isEqualTo(1)
}

@Test
fun countUpTotalConvertCount_countUp_isCountUp() = runTest {
repeat(5) { assertThat(repository.countUpTotalConvertCount()).isEqualTo(it + 1) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ interface DataStoreRepository {
suspend fun updateFontType(fontType: FontType)
suspend fun checkIsExceedingMaxLimit(): Boolean
suspend fun updateUseInAppUpdate(isUsed: Boolean)
suspend fun countUpTotalConvertCount(): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ksnd.hiraganaconverter.core.domain.repository

import kotlinx.coroutines.flow.Flow
import ksnd.hiraganaconverter.core.model.ReviewInfo

interface ReviewInfoRepository {
val reviewInfo: Flow<ReviewInfo>
suspend fun countUpTotalConvertCount(): Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ksnd.hiraganaconverter.core.analytics.Analytics
import ksnd.hiraganaconverter.core.domain.repository.ConvertHistoryRepository
import ksnd.hiraganaconverter.core.domain.repository.ConvertRepository
import ksnd.hiraganaconverter.core.domain.repository.DataStoreRepository
import ksnd.hiraganaconverter.core.domain.repository.ReviewInfoRepository
import ksnd.hiraganaconverter.core.model.ui.HiraKanaType
import ksnd.hiraganaconverter.core.resource.AppConfig
import ksnd.hiraganaconverter.core.resource.di.IODispatcher
Expand All @@ -16,12 +17,13 @@ class ConvertTextUseCase @Inject constructor(
private val convertRepository: ConvertRepository,
private val dataStoreRepository: DataStoreRepository,
private val convertHistoryRepository: ConvertHistoryRepository,
private val reviewInfoRepository: ReviewInfoRepository,
private val appConfig: AppConfig,
private val analytics: Analytics,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) {
suspend operator fun invoke(inputText: String, selectedTextType: HiraKanaType): String = withContext(ioDispatcher) {
val totalConvertCount = dataStoreRepository.countUpTotalConvertCount()
val totalConvertCount = reviewInfoRepository.countUpTotalConvertCount()
analytics.logTotalConvertCount(count = totalConvertCount)

val isReachedConvertMaxLimit = dataStoreRepository.checkIsExceedingMaxLimit()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ksnd.hiraganaconverter.core.analytics.Analytics
import ksnd.hiraganaconverter.core.domain.repository.ConvertHistoryRepository
import ksnd.hiraganaconverter.core.domain.repository.ConvertRepository
import ksnd.hiraganaconverter.core.domain.repository.DataStoreRepository
import ksnd.hiraganaconverter.core.domain.repository.ReviewInfoRepository
import ksnd.hiraganaconverter.core.model.ResponseData
import ksnd.hiraganaconverter.core.model.ui.HiraKanaType
import ksnd.hiraganaconverter.core.testing.MainDispatcherRule
Expand All @@ -29,29 +30,31 @@ class ConvertTextUseCaseTest {
private val convertRepository = mockk<ConvertRepository>(relaxed = true)
private val dataStoreRepository = mockk<DataStoreRepository>(relaxUnitFun = true)
private val convertHistoryRepository = mockk<ConvertHistoryRepository>(relaxUnitFun = true)
private val reviewInfoRepository = mockk<ReviewInfoRepository>(relaxUnitFun = true)
private val analytics = mockk<Analytics>(relaxUnitFun = true)
private val useCase = ConvertTextUseCase(
convertRepository = convertRepository,
dataStoreRepository = dataStoreRepository,
convertHistoryRepository = convertHistoryRepository,
reviewInfoRepository = reviewInfoRepository,
appConfig = mockk(relaxed = true),
analytics = analytics,
ioDispatcher = mainDispatcherRule.testDispatcher,
)

@Test
fun invoke_first_callCountUpTotalConvertCount() = runTest {
coEvery { dataStoreRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { reviewInfoRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { dataStoreRepository.checkIsExceedingMaxLimit() } returns false
coEvery { convertRepository.requestConvert(any(), any(), any()) } returns successResponse
useCase(inputText = INPUT_TXT, selectedTextType = SELECTED_TYPE)
coVerify { dataStoreRepository.countUpTotalConvertCount() }
coVerify { reviewInfoRepository.countUpTotalConvertCount() }
coVerify(exactly = 1) { analytics.logTotalConvertCount(TOTAL_CONVERT_COUNT) }
}

@Test
fun invoke_overConvertForReachedConvert_isReachedConvertMaxLimitException() = runTest {
coEvery { dataStoreRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { reviewInfoRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { dataStoreRepository.checkIsExceedingMaxLimit() } returns true
assertFailsWith<IsReachedConvertMaxLimitException> {
useCase(inputText = INPUT_TXT, selectedTextType = SELECTED_TYPE)
Expand All @@ -61,7 +64,7 @@ class ConvertTextUseCaseTest {

@Test
fun invoke_responseIsNothing_conversionFailedException() = runTest {
coEvery { dataStoreRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { reviewInfoRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { dataStoreRepository.checkIsExceedingMaxLimit() } returns false
coEvery { convertRepository.requestConvert(any(), any(), any()) } returns null
assertFailsWith<ConversionFailedException> {
Expand All @@ -72,7 +75,7 @@ class ConvertTextUseCaseTest {

@Test
fun invoke_error413_conversionFailedException() = runTest {
coEvery { dataStoreRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { reviewInfoRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { dataStoreRepository.checkIsExceedingMaxLimit() } returns false
coEvery { convertRepository.requestConvert(any(), any(), any()) } returns errorResponse
assertFailsWith<InterceptorError> {
Expand All @@ -83,7 +86,7 @@ class ConvertTextUseCaseTest {

@Test
fun invoke_relaxed_outputConverted() = runTest {
coEvery { dataStoreRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { reviewInfoRepository.countUpTotalConvertCount() } returns TOTAL_CONVERT_COUNT
coEvery { dataStoreRepository.checkIsExceedingMaxLimit() } returns false
coEvery { convertRepository.requestConvert(any(), any(), any()) } returns successResponse
assertThat(useCase(inputText = INPUT_TXT, selectedTextType = SELECTED_TYPE)).isNotEmpty()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ksnd.hiraganaconverter.core.model

import kotlinx.serialization.Serializable

@Serializable
data class ReviewInfo(
val isAlreadyReviewed: Boolean = false,
val totalConvertCount: Int = 0,
val lastRequestReviewLocalDateStr: String? = null,
)

0 comments on commit a43fab2

Please sign in to comment.