Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/flipcash/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ android {
dependencies {
testImplementation(kotlin("test"))
testImplementation(libs.bundles.unit.testing)
testImplementation(libs.robolectric)

implementation(libs.androidx.browser)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package com.flipcash.app.core.cache

import kotlinx.coroutines.test.runTest
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class CachePolicyHandlerTest {

private val handler = CachePolicyHandler<String, String>()

// region CacheOnly

@Test
fun `CacheOnly with cache hit returns success with Cache origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.CacheOnly,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("cached", entry.data)
assertEquals(DataOrigin.Cache, entry.origin)
}

@Test
fun `CacheOnly with cache miss returns failure`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.CacheOnly,
cacheLookup = { null },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isFailure)
}

// endregion

// region CacheFirst

@Test
fun `CacheFirst with cache hit returns success with Cache origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.CacheFirst,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("cached", entry.data)
assertEquals(DataOrigin.Cache, entry.origin)
}

@Test
fun `CacheFirst with cache miss and network success returns success with Network origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.CacheFirst,
cacheLookup = { null },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("network", entry.data)
assertEquals(DataOrigin.Network, entry.origin)
}

@Test
fun `CacheFirst with cache miss and network failure returns failure`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.CacheFirst,
cacheLookup = { null },
persistNetworkData = { },
networkRequest = { Result.failure(RuntimeException("network error")) },
domainMapper = { it },
)

assertTrue(result.isFailure)
}

// endregion

// region NetworkFirst

@Test
fun `NetworkFirst with network success returns success with Network origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.NetworkFirst,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("network", entry.data)
assertEquals(DataOrigin.Network, entry.origin)
}

@Test
fun `NetworkFirst with network failure and cache hit returns success with Cache origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.NetworkFirst,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.failure(RuntimeException("network error")) },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("cached", entry.data)
assertEquals(DataOrigin.Cache, entry.origin)
}

@Test
fun `NetworkFirst with network failure and cache miss returns failure`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.NetworkFirst,
cacheLookup = { null },
persistNetworkData = { },
networkRequest = { Result.failure(RuntimeException("network error")) },
domainMapper = { it },
)

assertTrue(result.isFailure)
}

// endregion

// region NetworkOnly

@Test
fun `NetworkOnly with network success returns success with Network origin`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.NetworkOnly,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.success("network") },
domainMapper = { it },
)

assertTrue(result.isSuccess)
val entry = result.getOrThrow()
assertEquals("network", entry.data)
assertEquals(DataOrigin.Network, entry.origin)
}

@Test
fun `NetworkOnly with network failure returns failure`() = runTest {
val result = handler.execute(
cachePolicy = CachePolicy.NetworkOnly,
cacheLookup = { "cached" },
persistNetworkData = { },
networkRequest = { Result.failure(RuntimeException("network error")) },
domainMapper = { it },
)

assertTrue(result.isFailure)
}

// endregion

// region persistNetworkData

@Test
fun `persistNetworkData is called when network succeeds for NetworkOnly`() = runTest {
var persisted: String? = null

handler.execute(
cachePolicy = CachePolicy.NetworkOnly,
cacheLookup = { null },
persistNetworkData = { persisted = it },
networkRequest = { Result.success("network-data") },
domainMapper = { it },
)

assertEquals("network-data", persisted)
}

@Test
fun `persistNetworkData is called when network succeeds for NetworkFirst`() = runTest {
var persisted: String? = null

handler.execute(
cachePolicy = CachePolicy.NetworkFirst,
cacheLookup = { null },
persistNetworkData = { persisted = it },
networkRequest = { Result.success("network-data") },
domainMapper = { it },
)

assertEquals("network-data", persisted)
}

@Test
fun `persistNetworkData is called when CacheFirst falls back to network`() = runTest {
var persisted: String? = null

handler.execute(
cachePolicy = CachePolicy.CacheFirst,
cacheLookup = { null },
persistNetworkData = { persisted = it },
networkRequest = { Result.success("network-data") },
domainMapper = { it },
)

assertEquals("network-data", persisted)
}

// endregion
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.flipcash.app.core.data

import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue

class LoadableTest {

@Test
fun `Loading state reports isLoading true`() {
val state: Loadable<String> = Loadable.Loading()

assertTrue(state.isLoading())
assertFalse(state.isLoaded())
assertFalse(state.isError())
}

@Test
fun `Loaded state reports isLoaded true`() {
val state: Loadable<String> = Loadable.Loaded("data")

assertTrue(state.isLoaded())
assertFalse(state.isLoading())
assertFalse(state.isError())
}

@Test
fun `Error state reports isError true`() {
val state: Loadable<String> = Loadable.Error("something went wrong")

assertTrue(state.isError())
assertFalse(state.isLoading())
assertFalse(state.isLoaded())
}

@Test
fun `dataOrNull returns data for Loading with data`() {
val state: Loadable<String> = Loadable.Loading(data = "partial")

assertEquals("partial", state.dataOrNull)
}

@Test
fun `dataOrNull returns null for Loading without data`() {
val state: Loadable<String> = Loadable.Loading()

assertNull(state.dataOrNull)
}

@Test
fun `dataOrNull returns data for Loaded`() {
val state: Loadable<String> = Loadable.Loaded("complete")

assertEquals("complete", state.dataOrNull)
}

@Test
fun `dataOrNull returns null for Error`() {
val state: Loadable<String> = Loadable.Error("fail")

assertNull(state.dataOrNull)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.flipcash.app.core.extensions

import org.junit.Test
import kotlin.test.assertEquals

class ListExtensionsTest {

@Test
fun `filterIsNotInstance removes instances of specified type`() {
val list: List<Any> = listOf("hello", 1, "world", 2, 3)

val result = list.filterIsNotInstance<String, Any>()

assertEquals(listOf(1, 2, 3), result)
}

@Test
fun `filterIsNotInstance keeps non-matching types`() {
val list: List<Any> = listOf(1, 2, 3)

val result = list.filterIsNotInstance<String, Any>()

assertEquals(listOf(1, 2, 3), result)
}

@Test
fun `filterIsNotInstance on empty list returns empty`() {
val list = emptyList<Any>()

val result = list.filterIsNotInstance<String, Any>()

assertEquals(emptyList(), result)
}
}
Loading
Loading