Skip to content

Commit

Permalink
WTA #39: Added simple test case and code for it.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob3075 committed Oct 18, 2022
1 parent 7df0f50 commit bd1ecf3
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ dependencies {
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
testImplementation("io.mockk:mockk:1.13.2")
testImplementation("io.kotest:kotest-assertions-core:5.5.0")
testImplementation("io.kotest.extensions:kotest-assertions-arrow:1.2.5")

androidTestImplementation("androidx.test.ext:junit:1.1.3")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package com.jacob.wakatimeapp.core.common // ktlint-disable filename

import java.time.format.TextStyle.SHORT
import java.util.Locale
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

val LocalDate.Companion.today
get() = Instant.DISTANT_PAST.toLocalDateTime(TimeZone.currentSystemDefault()).date

fun LocalDate.getDisplayNameForDay(): String =
dayOfWeek.getDisplayName(SHORT, Locale.getDefault())
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.jacob.wakatimeapp.home.domain.models.Last7DaysStats
import com.jacob.wakatimeapp.home.domain.models.StreakRange
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -44,7 +43,7 @@ class HomePageCache @Inject constructor(
}
}

fun getLast7DaysStats(): Flow<Either<Error, Last7DaysStats>> = dataStore.data.map {
fun getLast7DaysStats() = dataStore.data.map {
val stringUiData = it[KEY_LAST_7_DAYS_STATS] ?: return@map emptyCacheError
json.decodeFromString<Last7DaysStats>(stringUiData).right()
}.catch {
Expand All @@ -58,9 +57,10 @@ class HomePageCache @Inject constructor(
}
}

fun getCurrentStreak() = dataStore.data.map {
val stringCurrentStreak = it[KEY_CURRENT_STREAK] ?: return@map emptyCacheError
json.decodeFromString<StreakRange>(stringCurrentStreak).right()
fun getCurrentStreak() = dataStore.data.map<Preferences, Either<Error, StreakRange>> {
val streakRange = it[KEY_CURRENT_STREAK]?.let<String, StreakRange>(json::decodeFromString)
?: StreakRange.ZERO
streakRange.right()
}.catch {
Timber.e(it)
emit(DatabaseError.UnknownError(it.message!!, it).left())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
package com.jacob.wakatimeapp.home.domain.usecases

import arrow.core.Either
import arrow.core.computations.either
import com.jacob.wakatimeapp.core.common.today
import com.jacob.wakatimeapp.core.models.Error
import com.jacob.wakatimeapp.core.models.Time
import com.jacob.wakatimeapp.home.data.local.HomePageCache
import com.jacob.wakatimeapp.home.domain.models.StreakRange
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.datetime.LocalDate

@Singleton
class CalculateCurrentStreakUC @Inject constructor(
dispatcher: CoroutineContext = Dispatchers.IO,
private val homePageCache: HomePageCache,
) {

operator fun invoke(): Flow<Either<Error, StreakRange>> {
TODO()
operator fun invoke(): Flow<Either<Error, StreakRange>> = channelFlow {
val currentStreakFlow = homePageCache.getCurrentStreak()
val last7DaysStatsFlow = homePageCache.getLast7DaysStats()

launch { currentStreakFlow.collect { send(it) } }

last7DaysStatsFlow.collect { last7DaysStatsEither ->
either {
val last7DaysStats = last7DaysStatsEither.bind()
val currentStreak = currentStreakFlow.first().bind()

val todaysStats = last7DaysStats.weeklyTimeSpent[LocalDate.today] ?: Time.ZERO

if (todaysStats == Time.ZERO) return@either

val updatedStreakRange = if (currentStreak == StreakRange.ZERO) {
StreakRange(
start = LocalDate.today,
end = LocalDate.today,
)
} else {
StreakRange(
start = currentStreak.start,
end = LocalDate.today,
)
}

homePageCache.updateCurrentStreak(updatedStreakRange)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.jacob.wakatimeapp.home.domain.usecases

import arrow.core.Either
import com.jacob.wakatimeapp.core.common.today
import com.jacob.wakatimeapp.core.models.Error
import com.jacob.wakatimeapp.core.models.Time
import com.jacob.wakatimeapp.home.data.local.HomePageCache
import com.jacob.wakatimeapp.home.domain.models.Last7DaysStats
import com.jacob.wakatimeapp.home.domain.models.StreakRange
import io.kotest.assertions.arrow.core.shouldBeLeft
import io.kotest.assertions.arrow.core.shouldBeRight
import io.kotest.matchers.collections.shouldContain
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import io.mockk.clearMocks
import io.mockk.coEvery
Expand All @@ -16,6 +19,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.datetime.LocalDate

@OptIn(ExperimentalCoroutinesApi::class)
internal class CalculateCurrentStreakUCRobot {
Expand All @@ -27,6 +31,7 @@ internal class CalculateCurrentStreakUCRobot {

fun buildUseCase() = apply {
clearMocks(mockCache)
results.clear()

useCase = CalculateCurrentStreakUC(
dispatcher = UnconfinedTestDispatcher(),
Expand All @@ -46,8 +51,20 @@ internal class CalculateCurrentStreakUCRobot {
results shouldContain expected
}

fun resultsShouldContain(expected: List<Either<Error, StreakRange>>) = apply {
results shouldContainExactly expected
fun resultsShouldContain(expected: StreakRange) = apply {
results.first() shouldBeRight expected
}

fun resultShouldBeRight() = apply {
results.first().shouldBeRight()
}

fun resultsShouldContain(expected: Error) = apply {
results.first() shouldBeLeft expected
}

fun resultShouldBeLeft() = apply {
results.first().shouldBeLeft()
}

fun mockGetCurrentStreak(data: Either<Error, StreakRange>) = apply {
Expand All @@ -58,7 +75,21 @@ internal class CalculateCurrentStreakUCRobot {
coEvery { mockCache.getLast7DaysStats() } returns flowOf(data)
}

fun verifyUpdateCurrentStreakCalled(data: StreakRange) = apply {
coVerify { mockCache.updateCurrentStreak(data) }
fun verifyUpdateCurrentStreakCalled(data: StreakRange, count: Int = 1) = apply {
coVerify(exactly = count) { mockCache.updateCurrentStreak(data) }
}

companion object {
val streakRange = StreakRange.ZERO
val today = LocalDate.today

val last7DaysStats = Last7DaysStats(
timeSpentToday = Time.ZERO,
projectsWorkedOn = listOf(),
weeklyTimeSpent = mutableMapOf(),
mostUsedLanguage = "",
mostUsedEditor = "",
mostUsedOs = ""
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.jacob.wakatimeapp.home.domain.usecases

import arrow.core.right
import com.jacob.wakatimeapp.core.models.Time
import com.jacob.wakatimeapp.home.domain.models.StreakRange
import com.jacob.wakatimeapp.home.domain.usecases.CalculateCurrentStreakUCRobot.Companion.last7DaysStats
import com.jacob.wakatimeapp.home.domain.usecases.CalculateCurrentStreakUCRobot.Companion.streakRange
import com.jacob.wakatimeapp.home.domain.usecases.CalculateCurrentStreakUCRobot.Companion.today
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate
import kotlinx.datetime.minus
import org.junit.jupiter.api.Test

@OptIn(ExperimentalCoroutinesApi::class)
internal class CalculateCurrentStreakUCTest {
private val robot = CalculateCurrentStreakUCRobot()

@Test
internal fun `when there is no value in the cache, then set current streak value to 0`() =
runTest {
val map = mutableMapOf<LocalDate, Time>()
for (i in 0 until 7) {
map[today.minus(DatePeriod(years = 0, months = 0, days = i))] = Time.ZERO
}

val updatedLast7DaysStats = last7DaysStats.copy(weeklyTimeSpent = map)

robot.buildUseCase()
.mockGetCurrentStreak(StreakRange.ZERO.right())
.mockGetLast7DaysStats(updatedLast7DaysStats.right())
.callUseCase()
.resultSizeShouldBe(1)
.resultsShouldContain(streakRange.right())
}

@Test
internal fun `when there is a value in the cache, then increase the value by 1`() {
TODO("Not yet implemented")
}

@Test
internal fun `when last 7 days stats cache sends multiple values, then streak should only increase by 1`() {
TODO("Not yet implemented")
}

@Test
internal fun `when there is non 0 value in cache and todays stats arrives later, then streak should increase by 1`() {
TODO("Not yet implemented")
}
}

0 comments on commit bd1ecf3

Please sign in to comment.