From c4f0ac448abfc4ab0ed83c6311ef971b61346f9c Mon Sep 17 00:00:00 2001 From: Luca Tassinari Date: Wed, 28 Feb 2024 00:12:59 +0100 Subject: [PATCH] test(analyzer): use kotlin mock --- .../analyzer/client/AnalyzerAppController.kt | 3 +- .../github/tassiLuca/analyzer/lib/Analyzer.kt | 6 +- .../analyzer/lib/GitHubRepositoryProvider.kt | 8 ++- .../tassiLuca/analyzer/lib/GitHubService.kt | 2 +- .../tassiLuca/analyzer/lib/AnalyzerTest.kt | 70 +++++++++++++++++++ .../lib/GitHubRepositoryProviderTest.kt | 2 +- .../analyzer/core/AnalyzerTest.scala | 15 ++-- 7 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/AnalyzerTest.kt diff --git a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/client/AnalyzerAppController.kt b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/client/AnalyzerAppController.kt index 9bb9a808..0ab63bc8 100644 --- a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/client/AnalyzerAppController.kt +++ b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/client/AnalyzerAppController.kt @@ -2,6 +2,7 @@ package io.github.tassiLuca.analyzer.client import io.github.tassiLuca.analyzer.commons.client.AppController import io.github.tassiLuca.analyzer.lib.Analyzer +import io.github.tassiLuca.analyzer.lib.GitHubRepositoryProvider import io.github.tassiLuca.analyzer.lib.RepositoryReport import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -13,7 +14,7 @@ import kotlin.coroutines.CoroutineContext /** The analyzer application controller. */ class AnalyzerAppController : AppController, CoroutineScope { private val view = AnalyzerGUI(this) - private val analyzer = Analyzer.ofGitHub() + private val analyzer = Analyzer.ofGitHub(GitHubRepositoryProvider()) private var currentComputation: Job? = null init { diff --git a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/Analyzer.kt b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/Analyzer.kt index 25dcfb46..f29fdd8f 100644 --- a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/Analyzer.kt +++ b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/Analyzer.kt @@ -20,13 +20,11 @@ interface Analyzer { companion object { /** Creates a new GitHub organization [Analyzer]. */ - fun ofGitHub(): Analyzer = GitHubAnalyzer() + fun ofGitHub(gitHubProvider: GitHubRepositoryProvider): Analyzer = GitHubAnalyzer(gitHubProvider) } } -private class GitHubAnalyzer : Analyzer { - - private val gitHubProvider = GitHubRepositoryProvider(GitHubService.create()) +private class GitHubAnalyzer(private val gitHubProvider: GitHubRepositoryProvider) : Analyzer { override suspend fun analyze( organizationName: String, diff --git a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProvider.kt b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProvider.kt index 5f00fc61..5915ca08 100644 --- a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProvider.kt +++ b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProvider.kt @@ -8,15 +8,17 @@ import retrofit2.Response import retrofit2.awaitResponse /** A facade for the [GitHubService]. */ -class GitHubRepositoryProvider(private val gitHubService: GitHubService) { +class GitHubRepositoryProvider { + + private val gitHubService: GitHubService = GitHubService.create() /** Returns the repositories of the given [organizationName]. */ suspend fun repositoriesOf(organizationName: String): Result> = - paginatedRequest { gitHubService.organizationRepositories(organizationName, it).awaitResponse() } + paginatedRequest { gitHubService.repositoriesOf(organizationName, it).awaitResponse() } /** Returns the repositories of the given [organizationName] as [Flow]. */ fun flowingRepositoriesOf(organizationName: String): Flow> = - paginatedFlowRequest { gitHubService.organizationRepositories(organizationName, it).awaitResponse() } + paginatedFlowRequest { gitHubService.repositoriesOf(organizationName, it).awaitResponse() } /** Returns the contributors of the given [organizationName] and [repositoryName]. */ suspend fun contributorsOf(organizationName: String, repositoryName: String): Result> = diff --git a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubService.kt b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubService.kt index 91fa64b2..9a819054 100644 --- a/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubService.kt +++ b/analyzer-direct-kt/src/main/kotlin/io/github/tassiLuca/analyzer/lib/GitHubService.kt @@ -15,7 +15,7 @@ interface GitHubService { /** @return the repositories of the organization with the given name. */ @GET("orgs/{organizationName}/repos") - fun organizationRepositories( + fun repositoriesOf( @Path("organizationName") organizationName: String, @Query("page") page: Int = 1, ): Call> diff --git a/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/AnalyzerTest.kt b/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/AnalyzerTest.kt new file mode 100644 index 00000000..6300ffc3 --- /dev/null +++ b/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/AnalyzerTest.kt @@ -0,0 +1,70 @@ +package io.github.tassiLuca.analyzer.lib + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.runBlocking +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` + +class AnalyzerTest : FunSpec() { + + private val dummiesData: Map, Release?>> = mapOf( + Repository(0, "dse/test-1", 100, 10) to + Pair(setOf(Contribution("mrossi", 56)), Release("v0.1", "2024-02-21")), + Repository(1, "dse/test-2", 123, 198) to + Pair(setOf(Contribution("mrossi", 11), Contribution("averdi", 98)), null), + ) + + init { + test("Analyzer should return the correct results if given in input an existing organization") { + var incrementalResults = emptySet() + runBlocking { + val service = successfulService() + val allResults = service.analyze("dse") { + incrementalResults = incrementalResults + it + } + allResults.isSuccess shouldBe true + allResults.getOrThrow() shouldContainAll expectedResults() + incrementalResults shouldContainAll expectedResults() + } + } + + test("Analyzer should return a failure if given in input a non-existing organization") { + var incrementalResults = emptySet() + runBlocking { + val service = failingService() + val allResults = service.analyze("dse") { + incrementalResults = incrementalResults + it + } + allResults.isFailure shouldBe true + incrementalResults.shouldBeEmpty() + } + } + } + + private fun expectedResults() = dummiesData.map { + RepositoryReport(it.key.name, it.key.issues, it.key.stars, it.value.first.toList(), it.value.second) + }.toSet() + + private suspend fun successfulService(): Analyzer { + val gitHubProvider = mock() + `when`(gitHubProvider.repositoriesOf("dse")) + .thenReturn(Result.success(dummiesData.keys.toList())) + dummiesData.forEach { (repo, data) -> + `when`(gitHubProvider.contributorsOf(repo.organization, repo.name)) + .thenReturn(Result.success(data.first.toList())) + `when`(gitHubProvider.lastReleaseOf(repo.organization, repo.name)) + .thenReturn(Result.success(data.second)) + } + return Analyzer.ofGitHub(gitHubProvider) + } + + private suspend fun failingService(): Analyzer { + val gitHubProvider = mock() + `when`(gitHubProvider.repositoriesOf("dse")) + .thenReturn(Result.failure(RuntimeException("404, not found"))) + return Analyzer.ofGitHub(gitHubProvider) + } +} diff --git a/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProviderTest.kt b/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProviderTest.kt index 657b4dd1..e0845924 100644 --- a/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProviderTest.kt +++ b/analyzer-direct-kt/src/test/kotlin/io/github/tassiLuca/analyzer/lib/GitHubRepositoryProviderTest.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.runBlocking class GitHubRepositoryProviderTest : FreeSpec() { - private val gitHubRepositoryProvider = GitHubRepositoryProvider(GitHubService.create()) + private val gitHubRepositoryProvider = GitHubRepositoryProvider() init { "The github repository provider" - { diff --git a/analyzer-direct/src/test/scala/io/github/tassiLuca/analyzer/core/AnalyzerTest.scala b/analyzer-direct/src/test/scala/io/github/tassiLuca/analyzer/core/AnalyzerTest.scala index cfccbc32..4bc48f58 100644 --- a/analyzer-direct/src/test/scala/io/github/tassiLuca/analyzer/core/AnalyzerTest.scala +++ b/analyzer-direct/src/test/scala/io/github/tassiLuca/analyzer/core/AnalyzerTest.scala @@ -16,13 +16,10 @@ class AnalyzerTest extends AnyFlatSpec with Matchers with MockFactory { Repository(1, "dse/test-2", 123, 198) -> (Seq(Contribution("mrossi", 11), Contribution("averdi", 98)), None), ) - val gitHubService: GitHubService = mock[GitHubService] - val analyzer: Analyzer = Analyzer.of(gitHubService) - "Analyzer" should "return the correct results if given in input an existing organization" in { var incrementalResults = Set[RepositoryReport]() Async.blocking: - configureSuccessfulService() + val analyzer = successfulService() val allResults = analyzer.analyze("dse") { report => incrementalResults += report } @@ -34,7 +31,7 @@ class AnalyzerTest extends AnyFlatSpec with Matchers with MockFactory { "Analyzer" should "return a failure in case the given organization doesn't exists" in { var incrementalResults = Set[RepositoryReport]() Async.blocking: - configureFailureService() + val analyzer = failingService() val allResults = analyzer.analyze("non-existing") { report => incrementalResults += report } @@ -46,7 +43,8 @@ class AnalyzerTest extends AnyFlatSpec with Matchers with MockFactory { RepositoryReport(repo.name, repo.issues, repo.stars, data._1, data._2) }.toSet - private def configureSuccessfulService(): Unit = + private def successfulService(): Analyzer = + val gitHubService: GitHubService = mock[GitHubService] when(gitHubService.repositoriesOf(_: String)(using _: Async)).expects("dse", *) .returning(Right(dummiesData.keys.toSeq)) dummiesData.foreach { (repo, data) => @@ -55,8 +53,11 @@ class AnalyzerTest extends AnyFlatSpec with Matchers with MockFactory { when(gitHubService.lastReleaseOf(_: String, _: String)(using _: Async)).expects(repo.organization, repo.name, *) .returning(data._2.toRight("404, not found")) } + Analyzer.of(gitHubService) - private def configureFailureService(): Unit = + private def failingService(): Analyzer = + val gitHubService: GitHubService = mock[GitHubService] when(gitHubService.repositoriesOf(_: String)(using _: Async)).expects("non-existing", *) .returning(Left("404, not found")) + Analyzer.of(gitHubService) }