Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bbe45f2a authored by Jonathan Klee's avatar Jonathan Klee
Browse files

tests: add FDroidRepository unit tests

parent 41994fa2
Loading
Loading
Loading
Loading
+168 −0
Original line number Diff line number Diff line
package foundation.e.apps.data.fdroid

import com.google.common.truth.Truth.assertThat
import foundation.e.apps.data.application.data.Application
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.fdroid.models.BuildInfo
import foundation.e.apps.data.fdroid.models.FdroidApiModel
import foundation.e.apps.data.fdroid.models.FdroidEntity
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import retrofit2.Response
import java.io.InterruptedIOException
import kotlin.test.assertFailsWith

@OptIn(ExperimentalCoroutinesApi::class)
class FDroidRepositoryTest {

    private val fdroidApi = mockk<FdroidApiInterface>()
    private val fdroidDao = mockk<FdroidDao>(relaxed = true)
    private val repository = FDroidRepository(fdroidApi, fdroidDao)

    @Test
    fun getFdroidInfo_returnsDaoEntityWithoutCallingApi() = runTest {
        val entity = FdroidEntity("org.example.app", "DAO Author")
        coEvery { fdroidDao.getFdroidEntityFromPackageName(entity.packageName) } returns entity

        val result = repository.getFdroidInfo(entity.packageName)

        assertThat(result).isSameInstanceAs(entity)
        coVerify(exactly = 1) { fdroidDao.getFdroidEntityFromPackageName(entity.packageName) }
        coVerify(exactly = 0) { fdroidApi.getFdroidInfoForPackage(any()) }
        coVerify(exactly = 0) { fdroidDao.saveFdroidEntity(any()) }
    }

    @Test
    fun getFdroidInfo_fetchesFromApiAndPersistsEntity() = runTest {
        val packageName = "org.example.app"
        coEvery { fdroidDao.getFdroidEntityFromPackageName(packageName) } returns null
        coEvery { fdroidApi.getFdroidInfoForPackage(packageName) } returns
            Response.success(FdroidApiModel("API Author", emptyList()))

        val result = repository.getFdroidInfo(packageName)

        assertThat(result?.packageName).isEqualTo(packageName)
        assertThat(result?.authorName).isEqualTo("API Author")
        coVerify(exactly = 1) { fdroidApi.getFdroidInfoForPackage(packageName) }
        coVerify(exactly = 1) {
            fdroidDao.saveFdroidEntity(match {
                it.packageName == packageName && it.authorName == "API Author"
            })
        }
    }

    @Test
    fun getAuthorName_returnsDefaultWhenFdroidRequestFails() = runTest {
        val application = Application(
            author = FDroidRepository.UNKNOWN,
            package_name = "org.example.app",
            source = Source.OPEN_SOURCE,
        )
        coEvery { fdroidDao.getFdroidEntityFromPackageName(application.package_name) } returns null
        coEvery { fdroidApi.getFdroidInfoForPackage(application.package_name) } throws
            InterruptedIOException("timeout")

        val authorName = repository.getAuthorName(application)

        assertThat(authorName).isEqualTo(FdroidEntity.DEFAULT_FDROID_AUTHOR_NAME)
    }

    @Test
    fun getAuthorName_returnsUnknownForPlayStoreAppsWithoutLookup() = runTest {
        val application = Application(
            author = "",
            package_name = "org.example.play",
            source = Source.PLAY_STORE,
        )

        val authorName = repository.getAuthorName(application)

        assertThat(authorName).isEqualTo(FDroidRepository.UNKNOWN)
        coVerify(exactly = 0) { fdroidDao.getFdroidEntityFromPackageName(any()) }
        coVerify(exactly = 0) { fdroidApi.getFdroidInfoForPackage(any()) }
    }

    @Test
    fun getAuthorName_usesInMemoryCacheAfterFirstLookup() = runTest {
        val application = Application(
            author = FDroidRepository.UNKNOWN,
            package_name = "org.example.cached",
            source = Source.OPEN_SOURCE,
        )
        coEvery { fdroidDao.getFdroidEntityFromPackageName(application.package_name) } returns null
        coEvery { fdroidApi.getFdroidInfoForPackage(application.package_name) } returns
            Response.success(FdroidApiModel("Cached Author", emptyList()))

        val firstAuthorName = repository.getAuthorName(application)
        val secondAuthorName = repository.getAuthorName(application)

        assertThat(firstAuthorName).isEqualTo("Cached Author")
        assertThat(secondAuthorName).isEqualTo("Cached Author")
        coVerify(exactly = 1) { fdroidDao.getFdroidEntityFromPackageName(application.package_name) }
        coVerify(exactly = 1) { fdroidApi.getFdroidInfoForPackage(application.package_name) }
        coVerify(exactly = 1) { fdroidDao.saveFdroidEntity(any()) }
    }

    @Test
    fun isFdroidApplication_returnsFalseWhenRequestFails() = runTest {
        coEvery { fdroidApi.getFdroidInfoForPackage("org.example.app") } throws
            InterruptedIOException("timeout")

        val isFdroidApplication = repository.isFdroidApplication("org.example.app")

        assertThat(isFdroidApplication).isFalse()
    }

    @Test
    fun isFdroidApplication_returnsTrueForSuccessfulResponse() = runTest {
        coEvery { fdroidApi.getFdroidInfoForPackage("org.example.app") } returns
            Response.success(FdroidApiModel("Author", emptyList()))

        val isFdroidApplication = repository.isFdroidApplication("org.example.app")

        assertThat(isFdroidApplication).isTrue()
    }

    @Test
    fun getBuildVersionInfo_returnsNullWhenRequestFails() = runTest {
        coEvery { fdroidApi.getFdroidInfoForPackage("org.example.app") } throws
            InterruptedIOException("timeout")

        val buildVersionInfo = repository.getBuildVersionInfo("org.example.app")

        assertThat(buildVersionInfo).isNull()
    }

    @Test
    fun getBuildVersionInfo_returnsBuildsFromApi() = runTest {
        val builds = listOf(
            BuildInfo("123", "1.2.3"),
            BuildInfo("124", "1.2.4"),
        )
        coEvery { fdroidApi.getFdroidInfoForPackage("org.example.app") } returns
            Response.success(FdroidApiModel("Author", builds))

        val buildVersionInfo = repository.getBuildVersionInfo("org.example.app")

        assertThat(buildVersionInfo).hasSize(2)
        assertThat(buildVersionInfo?.map { it.versionCode }).containsExactly("123", "124").inOrder()
        assertThat(buildVersionInfo?.map { it.versionName }).containsExactly("1.2.3", "1.2.4").inOrder()
    }

    @Test
    fun getBuildVersionInfo_rethrowsCancellationException() = runTest {
        coEvery { fdroidApi.getFdroidInfoForPackage("org.example.app") } throws
            CancellationException("cancelled")

        val exception = assertFailsWith<CancellationException> {
            repository.getBuildVersionInfo("org.example.app")
        }

        assertThat(exception).hasMessageThat().contains("cancelled")
    }
}