Skip to content

Instantly share code, notes, and snippets.

@zamahaka
Created October 18, 2021 09:17
Show Gist options
  • Save zamahaka/87585a2e64c05d0cc7d2496e86ad9238 to your computer and use it in GitHub Desktop.
Save zamahaka/87585a2e64c05d0cc7d2496e86ad9238 to your computer and use it in GitHub Desktop.
test example
package com.advant.flodplain.approval.data
import androidx.lifecycle.LiveData
import arrow.core.Either
import arrow.core.flatMap
import arrow.core.left
import com.advant.flodplain.approval.data.local.ApprovalLocalDataSource
import com.advant.flodplain.approval.data.local.replaceWith
import com.advant.flodplain.approval.data.model.ApprovalRequest
import com.advant.flodplain.approval.data.model.ApprovalRequestId
import com.advant.flodplain.approval.data.model.ApprovalSubjectId
import com.advant.flodplain.approval.data.remote.ApprovalRemoteDataSource
import com.advant.flodplain.base.data.model.MaybeNoneRepositoryFailure
import com.advant.flodplain.base.data.model.RepositoryFailure
import javax.inject.Inject
internal interface ApprovalRepository {
fun observe(): LiveData<List<ApprovalRequest>>
fun observe(id: ApprovalRequestId): LiveData<ApprovalRequest>
suspend fun getRequest(
id: ApprovalRequestId, forceRemote: Boolean = false,
): Either<MaybeNoneRepositoryFailure, ApprovalRequest>
suspend fun getPendingRequests(
forceRemote: Boolean = false,
): Either<RepositoryFailure, List<ApprovalRequest>>
suspend fun respond(
rId: ApprovalRequestId, sId: ApprovalSubjectId, isApproved: Boolean,
): Either<RepositoryFailure, Unit>
suspend fun delete(
rIds: List<ApprovalRequestId>,
): Either<RepositoryFailure, Unit>
}
internal suspend fun ApprovalRepository.delete(rId: ApprovalRequestId) = delete(listOf(rId))
internal class ApprovalRepositoryImpl @Inject constructor(
private val local: ApprovalLocalDataSource,
private val remote: ApprovalRemoteDataSource,
) : ApprovalRepository {
override fun observe() = local.observe()
override fun observe(id: ApprovalRequestId) = local.observe(id)
override suspend fun getRequest(
id: ApprovalRequestId, forceRemote: Boolean,
): Either<MaybeNoneRepositoryFailure, ApprovalRequest> {
if (!forceRemote && local.hasData(id)) return local.get(id)
return getPendingRequests(forceRemote = true).fold(
ifLeft = RepositoryFailure::left,
ifRight = { local.get(id) },
)
}
override suspend fun getPendingRequests(
forceRemote: Boolean,
): Either<RepositoryFailure, List<ApprovalRequest>> {
if (!forceRemote && local.hasData()) return local.getPending()
return remote.getPendingRequests().flatMap { local.replaceWith(it) }
}
override suspend fun respond(
rId: ApprovalRequestId,
sId: ApprovalSubjectId,
isApproved: Boolean
) = remote.respond(rId, sId, isApproved).flatMap { delete(rId) }
override suspend fun delete(rIds: List<ApprovalRequestId>) = local.delete(rIds)
}
package com.advant.flodplain.approval.data
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.MutableLiveData
import arrow.core.Either
import com.advant.flodplain.approval.data.local.ApprovalLocalDataSource
import com.advant.flodplain.approval.data.local.delete
import com.advant.flodplain.approval.data.model.ApprovalRequest
import com.advant.flodplain.approval.data.model.ApprovalRequestType
import com.advant.flodplain.approval.data.remote.ApprovalRemoteDataSource
import com.advant.flodplain.base.data.model.NotInCache
import io.mockk.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.util.*
@ExperimentalCoroutinesApi
class ApprovalRepositoryImplTest {
private val local = mockk<ApprovalLocalDataSource>()
private val remote = mockk<ApprovalRemoteDataSource>()
private val repository = ApprovalRepositoryImpl(local, remote)
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setUp() {
}
@After
fun tearDown() {
clearAllMocks()
}
/* observe */
@Test
fun `observe delegates to local`() {
val expected = listOf(mockApprovalRequest())
every { local.observe() } returns
MutableLiveData<List<ApprovalRequest>>().apply { value = expected }
val result = repository.observe()
coVerify { local.observe() }
confirmVerified(local)
assertEquals(result.value, expected)
}
@Test
fun `observe id delegates to local`() {
val expected = mockApprovalRequest()
every { local.observe(expected.requestId) } returns
MutableLiveData<ApprovalRequest>().apply { value = expected }
val result = repository.observe(expected.requestId)
coVerify { local.observe(expected.requestId) }
confirmVerified(local)
assertEquals(result.value, expected)
}
/* getRequest */
@Test
fun `returns local request if local has it`() = runBlockingTest {
val request = mockApprovalRequest()
val expected = Either.Right(request)
coEvery { local.hasData(request.requestId) } returns true
coEvery { local.get(request.requestId) } returns expected
val result = repository.getRequest(request.requestId, forceRemote = false)
coVerifyOrder {
local.hasData(request.requestId)
local.get(request.requestId)
}
coVerify {
remote.getPendingRequests() wasNot called
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
@Test
fun `resolves to remote if local doesn't contain id and updates local`() = runBlockingTest {
val request = mockApprovalRequest()
val expected = Either.Right(request)
val expectedList = Either.Right(listOf(request))
coEvery { local.hasData(request.requestId) } returns false
coEvery { local.deleteAll() } returns Either.Right(Unit)
coEvery { local.add(expectedList.value) } returns expectedList
coEvery { local.get(request.requestId) } returns expected
coEvery { remote.getPendingRequests() } returns expectedList
val result = repository.getRequest(request.requestId, forceRemote = false)
coVerifyOrder {
local.hasData(request.requestId)
local.getPending() wasNot called
remote.getPendingRequests()
local.deleteAll()
local.add(expectedList.value)
local.get(request.requestId)
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
@Test
fun `resolves to remote if forced to and saves request`() = runBlockingTest {
val request = mockApprovalRequest()
val expected = Either.Right(request)
val expectedList = Either.Right(listOf(request))
coEvery { local.deleteAll() } returns Either.Right(Unit)
coEvery { local.add(expectedList.value) } returns expectedList
coEvery { local.get(request.requestId) } returns expected
coEvery { remote.getPendingRequests() } returns expectedList
val result = repository.getRequest(request.requestId, forceRemote = true)
coVerify(exactly = 0) {
local.hasData()
local.getPending()
}
coVerifyOrder {
remote.getPendingRequests()
local.deleteAll()
local.add(expectedList.value)
local.get(request.requestId)
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
@Test
fun `returns NotInCache if both stores doesn't contain id`() = runBlockingTest {
val request = mockApprovalRequest()
val expected = Either.Left(NotInCache)
val expectedList = Either.Right(listOf(request))
coEvery { local.hasData(request.requestId) } returns false
coEvery { local.deleteAll() } returns Either.Right(Unit)
coEvery { local.add(expectedList.value) } returns expectedList
coEvery { local.get(request.requestId) } returns expected
coEvery { remote.getPendingRequests() } returns expectedList
val result = repository.getRequest(request.requestId, forceRemote = false)
coVerifyOrder {
local.hasData(request.requestId)
local.getPending() wasNot called
remote.getPendingRequests()
local.deleteAll()
local.add(expectedList.value)
local.get(request.requestId)
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
/* getPendingRequests */
@Test
fun `returns local data if local has it`() = runBlockingTest {
val expected = Either.Right(listOf(mockApprovalRequest()))
coEvery { local.hasData() } returns true
coEvery { local.getPending() } returns expected
val result = repository.getPendingRequests(forceRemote = false)
coVerifyOrder {
local.hasData()
local.getPending()
}
coVerify {
remote.getPendingRequests() wasNot called
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
@Test
fun `resolves to remote if local has no data and saves it`() = runBlockingTest {
val expected = Either.Right(listOf(mockApprovalRequest()))
coEvery { local.hasData() } returns false
coEvery { local.deleteAll() } returns Either.Right(Unit)
coEvery { local.add(expected.value) } returns expected
coEvery { remote.getPendingRequests() } returns expected
val result = repository.getPendingRequests(forceRemote = false)
coVerifyOrder {
local.hasData()
local.getPending() wasNot called
remote.getPendingRequests()
local.deleteAll()
local.add(expected.value)
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
@Test
fun `resolves to remote if forced to and saves data`() = runBlockingTest {
val expected = Either.Right(listOf(mockApprovalRequest()))
coEvery { local.deleteAll() } returns Either.Right(Unit)
coEvery { local.add(expected.value) } returns expected
coEvery { remote.getPendingRequests() } returns expected
val result = repository.getPendingRequests(forceRemote = true)
coVerify(exactly = 0) {
local.hasData()
local.getPending()
}
coVerifyOrder {
remote.getPendingRequests()
local.deleteAll()
local.add(expected.value)
}
confirmVerified(local, remote)
assertEquals(result, expected)
}
/* respond */
@Test
fun `responds to requests and deletes it from local`() = runBlockingTest {
val request = mockApprovalRequest()
val respondWith = true
mockkStatic(ApprovalLocalDataSource::delete)
coEvery { local.delete(request.requestId) } returns Either.Right(Unit)
coEvery {
remote.respond(request.requestId, request.subjectId, respondWith)
} returns Either.Right(Unit)
val result = repository.respond(
request.requestId, request.subjectId, respondWith
)
coVerifyOrder {
remote.respond(request.requestId, request.subjectId, respondWith)
local.delete(request.requestId)
}
confirmVerified(local, remote)
assertEquals(result, Either.Right(Unit))
}
/* delete */
@Test
fun `just deletes from local`() = runBlockingTest {
val request = mockApprovalRequest()
mockkStatic(ApprovalLocalDataSource::delete)
coEvery { local.delete(request.requestId) } returns Either.Right(Unit)
val result = repository.delete(request.requestId)
coVerifyOrder {
local.delete(request.requestId)
}
confirmVerified(local)
assertEquals(result, Either.Right(Unit))
}
private fun mockApprovalRequest() = ApprovalRequest(
subjectId = "test-subject-id",
requestId = "test-request-id",
validForSeconds = 120,
expiry = Date(),
type = ApprovalRequestType.OPEN_ACCOUNT,
info = emptyList(),
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment