From f5e8dec9660e78db6733da63a38a29e119004ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Salcedo=20Garc=C3=ADa?= Date: Sun, 21 Apr 2024 23:55:54 +0200 Subject: [PATCH] chore: tests --- .../data/datastore/SessionDataStoreTest.kt | 7 +- .../repositories/SessionRepositoryTest.kt | 3 +- .../data/sources/SessionLocalSourceTest.kt | 3 +- .../ObserveActiveSessionUseCaseTest.kt | 5 +- .../data/managers/ApolloUserIdManagerTest.kt | 12 +- .../mappers/responses/UserIdMapperTest.kt | 16 --- .../data/repositories/UserRepositoryTest.kt | 3 +- .../data/sources/id/UserIdRemoteSourceTest.kt | 14 ++- .../sources/info/UserInfoRemoteSourceTest.kt | 50 ++------ .../usecases/ObserveUserInfoUseCaseTest.kt | 5 +- .../mappers/requests/MediaListMapperTest.kt | 52 ++++---- .../mappers/responses/AnimeEntryMapperTest.kt | 29 +---- .../mappers/responses/MangaEntryMapperTest.kt | 29 +---- .../mappers/responses/MediaEntryMapperTest.kt | 12 +- .../mappers/responses/MediaListMapperTest.kt | 116 ++++++++---------- .../alvr/katana/features/lists/data/mocks.kt | 27 ++-- .../data/repositories/ListsRepositoryTest.kt | 18 +-- .../sources/ApolloListsRemoteSourceTest.kt | 77 +++++------- .../sources/CommonListsRemoteSourceTest.kt | 55 ++++----- .../data/sources/ListsRemoteSourceTest.kt | 5 +- .../alvr/katana/features/lists/data/utils.kt | 12 -- .../usecases/ObserveAnimeListUseCaseTest.kt | 5 +- .../usecases/ObserveMangaListUseCaseTest.kt | 5 +- 23 files changed, 211 insertions(+), 349 deletions(-) delete mode 100644 features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/utils.kt diff --git a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/datastore/SessionDataStoreTest.kt b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/datastore/SessionDataStoreTest.kt index e5cbf62c3..bbd5e511e 100644 --- a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/datastore/SessionDataStoreTest.kt +++ b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/datastore/SessionDataStoreTest.kt @@ -12,7 +12,6 @@ import dev.alvr.katana.core.tests.koinExtension import io.kotest.core.spec.style.FreeSpec import io.kotest.core.test.TestCase import io.kotest.matchers.equals.shouldBeEqual -import kotlin.time.Duration.Companion.milliseconds import org.koin.test.KoinTest import org.koin.test.inject @@ -22,7 +21,7 @@ internal class SessionDataStoreTest : FreeSpec(), KoinTest { init { "initial session should equal to the Session class" { - dataStore.data.test(100.milliseconds) { + dataStore.data.test { awaitItem() shouldBeEqual Session() cancelAndConsumeRemainingEvents() } @@ -37,7 +36,7 @@ internal class SessionDataStoreTest : FreeSpec(), KoinTest { ) } - data.test(100.milliseconds) { + data.test { awaitItem() shouldBeEqual Session( anilistToken = AnilistToken("token"), isSessionActive = true, @@ -48,7 +47,7 @@ internal class SessionDataStoreTest : FreeSpec(), KoinTest { } "corrupted dataStore should recreate again the file with initial values" { - corruptedDataStore.data.test(100.milliseconds) { + corruptedDataStore.data.test { awaitItem() shouldBeEqual Session(anilistToken = AnilistToken("recreated")) cancelAndConsumeRemainingEvents() } diff --git a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/repositories/SessionRepositoryTest.kt b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/repositories/SessionRepositoryTest.kt index d56bf4b78..4a7d4fea6 100644 --- a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/repositories/SessionRepositoryTest.kt +++ b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/repositories/SessionRepositoryTest.kt @@ -20,7 +20,6 @@ import dev.mokkery.mock import dev.mokkery.verify import dev.mokkery.verifySuspend import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf internal class SessionRepositoryTest : FreeSpec() { @@ -37,7 +36,7 @@ internal class SessionRepositoryTest : FreeSpec() { false.right(), ) - repo.sessionActive.test(100.milliseconds) { + repo.sessionActive.test { awaitItem().shouldBeRight(true) awaitItem().shouldBeRight(true) awaitItem().shouldBeRight(false) diff --git a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/sources/SessionLocalSourceTest.kt b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/sources/SessionLocalSourceTest.kt index 58d0440b3..8190c0602 100644 --- a/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/sources/SessionLocalSourceTest.kt +++ b/common/session/data/src/commonTest/kotlin/dev/alvr/katana/common/session/data/sources/SessionLocalSourceTest.kt @@ -20,7 +20,6 @@ import dev.mokkery.mock import dev.mokkery.verify import dev.mokkery.verifySuspend import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf internal class SessionLocalSourceTest : FreeSpec() { @@ -80,7 +79,7 @@ internal class SessionLocalSourceTest : FreeSpec() { "checking session active for ${session.anilistToken} and ${session.isSessionActive}" { every { store.data } returns flowOf(session) - source.sessionActive.test(100.milliseconds) { + source.sessionActive.test { awaitItem().shouldBeRight((session.anilistToken == null && session.isSessionActive).not()) cancelAndIgnoreRemainingEvents() } diff --git a/common/session/domain/src/commonTest/kotlin/dev/alvr/katana/common/session/domain/usecases/ObserveActiveSessionUseCaseTest.kt b/common/session/domain/src/commonTest/kotlin/dev/alvr/katana/common/session/domain/usecases/ObserveActiveSessionUseCaseTest.kt index 5f513a275..9308177f6 100644 --- a/common/session/domain/src/commonTest/kotlin/dev/alvr/katana/common/session/domain/usecases/ObserveActiveSessionUseCaseTest.kt +++ b/common/session/domain/src/commonTest/kotlin/dev/alvr/katana/common/session/domain/usecases/ObserveActiveSessionUseCaseTest.kt @@ -17,7 +17,6 @@ import dev.mokkery.mock import dev.mokkery.verify import io.kotest.core.spec.style.FreeSpec import io.kotest.core.test.TestCase -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf import org.koin.test.KoinTest import org.koin.test.inject @@ -41,7 +40,7 @@ internal class ObserveActiveSessionUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeRight(false) awaitItem().shouldBeRight(true) awaitItem().shouldBeRight(false) @@ -58,7 +57,7 @@ internal class ObserveActiveSessionUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeLeft(SessionFailure.CheckingActiveSession) cancelAndConsumeRemainingEvents() } diff --git a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/managers/ApolloUserIdManagerTest.kt b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/managers/ApolloUserIdManagerTest.kt index c2640a27e..cef1fbaaf 100644 --- a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/managers/ApolloUserIdManagerTest.kt +++ b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/managers/ApolloUserIdManagerTest.kt @@ -25,8 +25,8 @@ internal class ApolloUserIdManagerTest : FreeSpec() { "retrieving the authenticated user" - { "the first time should make a HTTP request" { val query = UserIdQuery.Data { - this["viewer"] = buildUser { - this["id"] = 12345 + viewer = buildUser { + id = 12345 } } @@ -39,8 +39,8 @@ internal class ApolloUserIdManagerTest : FreeSpec() { "the second onwards it should be read from cache" { val query = UserIdQuery.Data { - this["viewer"] = buildUser { - this["id"] = 12345 + viewer = buildUser { + id = 12345 } } @@ -55,8 +55,8 @@ internal class ApolloUserIdManagerTest : FreeSpec() { "clearing the database" { val query = UserIdQuery.Data { - this["viewer"] = buildUser { - this["id"] = 12345 + viewer = buildUser { + id = 12345 } } diff --git a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/mappers/responses/UserIdMapperTest.kt b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/mappers/responses/UserIdMapperTest.kt index c7e3e84f8..3912a117e 100644 --- a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/mappers/responses/UserIdMapperTest.kt +++ b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/mappers/responses/UserIdMapperTest.kt @@ -2,26 +2,10 @@ package dev.alvr.katana.common.user.data.mappers.responses import dev.alvr.katana.common.user.data.UserIdQuery import io.kotest.assertions.throwables.shouldNotThrowExactlyUnit -import io.kotest.assertions.throwables.shouldThrowExactlyUnit import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.ints.shouldBeExactly -import io.kotest.matchers.throwable.shouldHaveMessage internal class UserIdMapperTest : FreeSpec({ - "null UserIdQuery Data" { - shouldThrowExactlyUnit { - val userId: UserIdQuery.Data? = null - userId() - } shouldHaveMessage "ViewerId is required." - } - - "UserIdQuery with null viewer" { - shouldThrowExactlyUnit { - val userId = UserIdQuery.Data(viewer = null) - userId() - } shouldHaveMessage "ViewerId is required." - } - "an UserIdQuery with a viewer should have same id" { shouldNotThrowExactlyUnit { UserIdQuery.Data(viewer = UserIdQuery.Viewer(37_384)) diff --git a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/repositories/UserRepositoryTest.kt b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/repositories/UserRepositoryTest.kt index 1cf801db0..46d2af12a 100644 --- a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/repositories/UserRepositoryTest.kt +++ b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/repositories/UserRepositoryTest.kt @@ -18,7 +18,6 @@ import dev.mokkery.mock import dev.mokkery.verify import dev.mokkery.verifySuspend import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.emptyFlow internal class UserRepositoryTest : FreeSpec() { @@ -68,7 +67,7 @@ internal class UserRepositoryTest : FreeSpec() { "observing userInfo" - { "the server returns no data" { every { userInfoSource.userInfo } returns emptyFlow() - repo.userInfo.test(100.milliseconds) { awaitComplete() } + repo.userInfo.test { awaitComplete() } verify { userInfoSource.userInfo } } } diff --git a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/id/UserIdRemoteSourceTest.kt b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/id/UserIdRemoteSourceTest.kt index 56d96368f..9ba59ae72 100644 --- a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/id/UserIdRemoteSourceTest.kt +++ b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/id/UserIdRemoteSourceTest.kt @@ -2,6 +2,7 @@ package dev.alvr.katana.common.user.data.sources.id import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.annotations.ApolloExperimental +import com.apollographql.apollo3.exception.JsonDataException import com.apollographql.apollo3.testing.QueueTestNetworkTransport import com.apollographql.apollo3.testing.enqueueTestResponse import dev.alvr.katana.common.user.data.UserIdQuery @@ -10,6 +11,7 @@ import dev.alvr.katana.core.domain.failures.Failure import dev.alvr.katana.core.remote.type.buildUser import dev.alvr.katana.core.tests.shouldBeLeft import dev.alvr.katana.core.tests.shouldBeRight +import io.kotest.assertions.throwables.shouldThrowExactlyUnit import io.kotest.core.spec.style.FreeSpec @OptIn(ApolloExperimental::class) @@ -25,13 +27,15 @@ internal class UserIdRemoteSourceTest : FreeSpec() { } "the server returns an empty userId" { - val query = UserIdQuery.Data { this["viewer"] = null } - client.enqueueTestResponse(UserIdQuery(), query) - source.getUserId().shouldBeLeft(Failure.Unknown) + shouldThrowExactlyUnit { + val query = UserIdQuery.Data { this["Viewer"] = null } + client.enqueueTestResponse(UserIdQuery(), query) + source.getUserId() + } } "the server returns a valid id" { - val query = UserIdQuery.Data { this["viewer"] = buildUser { this["id"] = 37_384 } } + val query = UserIdQuery.Data { this["Viewer"] = buildUser { id = 37_384 } } client.enqueueTestResponse(UserIdQuery(), query) source.getUserId().shouldBeRight(UserId(37_384)) } @@ -39,7 +43,7 @@ internal class UserIdRemoteSourceTest : FreeSpec() { "saving" - { "is successful" { - val query = UserIdQuery.Data { this["viewer"] = buildUser { this["id"] = 37_384 } } + val query = UserIdQuery.Data { this["viewer"] = buildUser { id = 37_384 } } client.enqueueTestResponse(UserIdQuery(), query) source.saveUserId().shouldBeRight() } diff --git a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/info/UserInfoRemoteSourceTest.kt b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/info/UserInfoRemoteSourceTest.kt index c21bfc8fe..d23437a99 100644 --- a/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/info/UserInfoRemoteSourceTest.kt +++ b/common/user/data/src/commonTest/kotlin/dev/alvr/katana/common/user/data/sources/info/UserInfoRemoteSourceTest.kt @@ -9,13 +9,11 @@ import com.apollographql.apollo3.testing.registerTestResponse import dev.alvr.katana.common.user.data.UserInfoQuery import dev.alvr.katana.common.user.domain.failures.UserFailure import dev.alvr.katana.common.user.domain.models.UserInfo -import dev.alvr.katana.core.common.empty import dev.alvr.katana.core.remote.type.buildUser import dev.alvr.katana.core.remote.type.buildUserAvatar import dev.alvr.katana.core.tests.shouldBeLeft import dev.alvr.katana.core.tests.shouldBeRight import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds @OptIn(ApolloExperimental::class) internal class UserInfoRemoteSourceTest : FreeSpec() { @@ -26,17 +24,8 @@ internal class UserInfoRemoteSourceTest : FreeSpec() { "observing the user info" - { "the server returns no data" { client.registerTestResponse(UserInfoQuery()) - source.userInfo.test(100.milliseconds) { - awaitItem().shouldBeRight(userInfoNoData) - cancelAndIgnoreRemainingEvents() - } - } - - "the server returns an empty user" { - val query = UserInfoQuery.Data { this["user"] = null } - client.registerTestResponse(UserInfoQuery(), query) - source.userInfo.test(100.milliseconds) { - awaitItem().shouldBeRight(userInfoNoData) + source.userInfo.test { + awaitItem().shouldBeLeft(UserFailure.GettingUserInfo) cancelAndIgnoreRemainingEvents() } } @@ -44,7 +33,7 @@ internal class UserInfoRemoteSourceTest : FreeSpec() { validUserInfoData.map { (query, userInfo) -> "the server returns valid user info ($query)" { client.registerTestResponse(UserInfoQuery(), query) - source.userInfo.test(100.milliseconds) { + source.userInfo.test { awaitItem().shouldBeRight(userInfo) cancelAndIgnoreRemainingEvents() } @@ -53,7 +42,7 @@ internal class UserInfoRemoteSourceTest : FreeSpec() { "there is a problem with the server" { client.registerTestNetworkError(UserInfoQuery()) - source.userInfo.test(100.milliseconds) { + source.userInfo.test { awaitItem().shouldBeLeft(UserFailure.GettingUserInfo) cancelAndIgnoreRemainingEvents() } @@ -66,34 +55,21 @@ internal class UserInfoRemoteSourceTest : FreeSpec() { const val AVATAR = "https://s4.anilist.co/file/anilistcdn/user/avatar/large/b37384-xJE9aA4X20Yr.png" const val BANNER = "https://s4.anilist.co/file/anilistcdn/user/banner/37384-jtds8dpQIGVG.jpg" - val userInfoNoData = UserInfo( - username = String.empty, - avatar = String.empty, - banner = String.empty, - ) - val validUserInfoData = listOf( UserInfoQuery.Data { - this["user"] = buildUser { - this["name"] = USER_NAME - this["avatar"] = buildUserAvatar { this["medium"] = AVATAR } - this["bannerImage"] = BANNER + this["Viewer"] = buildUser { + name = USER_NAME + avatar = buildUserAvatar { large = AVATAR } + bannerImage = BANNER } } to UserInfo(username = USER_NAME, avatar = AVATAR, banner = BANNER), UserInfoQuery.Data { - this["user"] = buildUser { - this["name"] = USER_NAME - this["avatar"] = buildUserAvatar { this["medium"] = null } - this["bannerImage"] = BANNER - } - } to UserInfo(username = USER_NAME, avatar = String.empty, banner = BANNER), - UserInfoQuery.Data { - this["user"] = buildUser { - this["name"] = USER_NAME - this["avatar"] = null - this["bannerImage"] = BANNER + this["Viewer"] = buildUser { + name = USER_NAME + avatar = buildUserAvatar { large = AVATAR } + bannerImage = null } - } to UserInfo(username = USER_NAME, avatar = String.empty, banner = BANNER), + } to UserInfo(username = USER_NAME, avatar = AVATAR, banner = null), ) } } diff --git a/common/user/domain/src/commonTest/kotlin/dev/alvr/katana/common/user/domain/usecases/ObserveUserInfoUseCaseTest.kt b/common/user/domain/src/commonTest/kotlin/dev/alvr/katana/common/user/domain/usecases/ObserveUserInfoUseCaseTest.kt index c67a3c3e6..41b704fc4 100644 --- a/common/user/domain/src/commonTest/kotlin/dev/alvr/katana/common/user/domain/usecases/ObserveUserInfoUseCaseTest.kt +++ b/common/user/domain/src/commonTest/kotlin/dev/alvr/katana/common/user/domain/usecases/ObserveUserInfoUseCaseTest.kt @@ -18,7 +18,6 @@ import dev.mokkery.mock import dev.mokkery.verify import io.kotest.core.spec.style.FreeSpec import io.kotest.core.test.TestCase -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf import org.koin.test.KoinTest import org.koin.test.inject @@ -35,7 +34,7 @@ internal class ObserveUserInfoUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeRight(userInfoMock) cancelAndConsumeRemainingEvents() } @@ -48,7 +47,7 @@ internal class ObserveUserInfoUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeLeft(UserFailure.GettingUserInfo) cancelAndConsumeRemainingEvents() } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/requests/MediaListMapperTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/requests/MediaListMapperTest.kt index 3743085c7..71ca330ee 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/requests/MediaListMapperTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/requests/MediaListMapperTest.kt @@ -27,26 +27,22 @@ internal class MediaListMapperTest : FreeSpec({ updatedAt = DateTimeTz.nowLocal(), ).toMutation() shouldBeEqual MediaListEntriesMutation( id = Int.zero, - score = Optional.Present(Double.zero), - progress = Optional.Present(Int.zero), - progressVolumes = Optional.Present(Int.zero), - repeat = Optional.Present(Int.zero), - private = Optional.Present(false), - notes = Optional.Present(String.empty), - hiddenFromStatusLists = Optional.Present(false), - startedAt = Optional.Present( - FuzzyDateInput( - year = Optional.Present(2022), - month = Optional.Present(7), - day = Optional.Present(20), - ), + score = Double.zero, + progress = Int.zero, + progressVolumes = Int.zero, + repeat = Int.zero, + private = false, + notes = String.empty, + hiddenFromStatusLists = false, + startedAt = FuzzyDateInput( + year = Optional.Present(2022), + month = Optional.Present(7), + day = Optional.Present(20), ), - completedAt = Optional.Present( - FuzzyDateInput( - year = Optional.Present(2022), - month = Optional.Present(7), - day = Optional.Present(20), - ), + completedAt = FuzzyDateInput( + year = Optional.Present(2022), + month = Optional.Present(7), + day = Optional.Present(20), ), ) } @@ -66,15 +62,15 @@ internal class MediaListMapperTest : FreeSpec({ updatedAt = null, ).toMutation() shouldBeEqual MediaListEntriesMutation( id = Int.zero, - score = Optional.Present(Double.zero), - progress = Optional.Present(Int.zero), - progressVolumes = Optional.Absent, - repeat = Optional.Present(Int.zero), - private = Optional.Present(false), - notes = Optional.Present(String.empty), - hiddenFromStatusLists = Optional.Present(false), - startedAt = Optional.Absent, - completedAt = Optional.Absent, + score = Double.zero, + progress = Int.zero, + progressVolumes = null, + repeat = Int.zero, + private = false, + notes = String.empty, + hiddenFromStatusLists = false, + startedAt = null, + completedAt = null, ) } }) diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/AnimeEntryMapperTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/AnimeEntryMapperTest.kt index b3dee0d3e..d05118c68 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/AnimeEntryMapperTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/AnimeEntryMapperTest.kt @@ -13,36 +13,15 @@ import korlibs.time.TimezoneOffset import dev.alvr.katana.features.lists.data.fragment.MediaEntry as MediaEntryFragment internal class AnimeEntryMapperTest : FreeSpec({ - "a null entry" { - val entry: MediaEntryFragment? = null - entry.animeEntry().also { result -> - result shouldBeEqual MediaEntry.Anime( - entry = CommonMediaEntry( - id = Int.zero, - title = String.empty, - coverImage = String.empty, - format = CommonMediaEntry.Format.UNKNOWN, - ), - episodes = null, - nextEpisode = null, - ) - result shouldBeEqual MediaEntry.Anime( - entry = entry.mediaEntry(), - episodes = null, - nextEpisode = null, - ) - } - } - "an entry with null values" { val entry = MediaEntryFragment( id = Int.zero, - title = null, + title = MediaEntryFragment.Title(String.empty), episodes = null, chapters = null, volumes = null, format = null, - coverImage = null, + coverImage = MediaEntryFragment.CoverImage(String.empty), nextAiringEpisode = null, ) entry.animeEntry().also { result -> @@ -67,12 +46,12 @@ internal class AnimeEntryMapperTest : FreeSpec({ "an entry with null values but data classes with null" { val entry = MediaEntryFragment( id = Int.zero, - title = MediaEntryFragment.Title(null), + title = MediaEntryFragment.Title(String.empty), episodes = null, chapters = null, volumes = null, format = null, - coverImage = MediaEntryFragment.CoverImage(null), + coverImage = MediaEntryFragment.CoverImage(String.empty), nextAiringEpisode = null, ) diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MangaEntryMapperTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MangaEntryMapperTest.kt index 55913345e..446e628d9 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MangaEntryMapperTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MangaEntryMapperTest.kt @@ -10,36 +10,15 @@ import io.kotest.matchers.equals.shouldBeEqual import dev.alvr.katana.features.lists.data.fragment.MediaEntry as MediaEntryFragment internal class MangaEntryMapperTest : FreeSpec({ - "a null entry" { - val entry: MediaEntryFragment? = null - entry.mangaEntry().also { result -> - result shouldBeEqual MediaEntry.Manga( - entry = CommonMediaEntry( - id = Int.zero, - title = String.empty, - coverImage = String.empty, - format = CommonMediaEntry.Format.UNKNOWN, - ), - chapters = null, - volumes = null, - ) - result shouldBeEqual MediaEntry.Manga( - entry = entry.mediaEntry(), - chapters = null, - volumes = null, - ) - } - } - "an entry with null values" { val entry = MediaEntryFragment( id = Int.zero, - title = null, + title = MediaEntryFragment.Title(String.empty), episodes = null, chapters = null, volumes = null, format = null, - coverImage = null, + coverImage = MediaEntryFragment.CoverImage(String.empty), nextAiringEpisode = null, ) entry.mangaEntry().also { result -> @@ -64,12 +43,12 @@ internal class MangaEntryMapperTest : FreeSpec({ "an entry with null values but data classes with null" { val entry = MediaEntryFragment( id = Int.zero, - title = MediaEntryFragment.Title(null), + title = MediaEntryFragment.Title(String.empty), episodes = null, chapters = null, volumes = null, format = null, - coverImage = MediaEntryFragment.CoverImage(null), + coverImage = MediaEntryFragment.CoverImage(String.empty), nextAiringEpisode = null, ) diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaEntryMapperTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaEntryMapperTest.kt index 4b602939c..017bcfddf 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaEntryMapperTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaEntryMapperTest.kt @@ -1,7 +1,9 @@ package dev.alvr.katana.features.lists.data.mappers.responses +import dev.alvr.katana.core.common.empty import dev.alvr.katana.core.common.zero import dev.alvr.katana.core.remote.type.MediaFormat +import dev.alvr.katana.features.lists.data.fragment.MediaEntry import dev.alvr.katana.features.lists.domain.models.entries.CommonMediaEntry import io.kotest.core.spec.style.FreeSpec import io.kotest.matchers.shouldBe @@ -9,17 +11,17 @@ import io.kotest.matchers.shouldNotBe import dev.alvr.katana.features.lists.data.fragment.MediaEntry as MediaEntryFragment internal class MediaEntryMapperTest : FreeSpec({ - MediaFormat.knownValues() + MediaFormat.knownEntries .forEach { format -> "MediaFormat $format should not be ${CommonMediaEntry.Format.UNKNOWN}" { MediaEntryFragment( id = Int.zero, - title = null, + title = MediaEntry.Title(String.empty), episodes = null, chapters = null, volumes = null, format = format, - coverImage = null, + coverImage = MediaEntry.CoverImage(String.empty), nextAiringEpisode = null, ).mediaEntry().format shouldNotBe CommonMediaEntry.Format.UNKNOWN } @@ -30,12 +32,12 @@ internal class MediaEntryMapperTest : FreeSpec({ "MediaFormat $format should be ${CommonMediaEntry.Format.UNKNOWN}" { MediaEntryFragment( id = Int.zero, - title = null, + title = MediaEntry.Title(String.empty), episodes = null, chapters = null, volumes = null, format = format, - coverImage = null, + coverImage = MediaEntry.CoverImage(String.empty), nextAiringEpisode = null, ).mediaEntry().format shouldBe CommonMediaEntry.Format.UNKNOWN } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaListMapperTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaListMapperTest.kt index 33d23a85a..a35423639 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaListMapperTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mappers/responses/MediaListMapperTest.kt @@ -40,38 +40,33 @@ import korlibs.time.TimezoneOffset internal class MediaListMapperTest : FreeSpec({ "a null response from server" { val data: MediaListCollectionQuery.Data? = null - data?.mediaList(MediaType.ANIME).shouldBeNull() - data?.mediaList(MediaType.MANGA).shouldBeNull() + data?.invoke(MediaType.ANIME).shouldBeNull() + data?.invoke(MediaType.MANGA).shouldBeNull() } "a collection without lists" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = emptyList() - user = null + user = buildUser { } } }.run { - mediaList(MediaType.ANIME).shouldBeEmpty() - mediaList(MediaType.MANGA).shouldBeEmpty() + this(MediaType.ANIME).shouldBeEmpty() + this(MediaType.MANGA).shouldBeEmpty() } } "a list without name" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( - buildMediaListGroup { - name = null - entries = emptyList() - }, buildMediaListGroup { name = String.empty entries = emptyList() }, - null, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { animeList = buildMediaListTypeOptions { sectionOrder = emptyList() } @@ -82,15 +77,15 @@ internal class MediaListMapperTest : FreeSpec({ } } }.run { - mediaList(MediaType.ANIME).forAll { list -> list.name.shouldBeEmpty() } - mediaList(MediaType.MANGA).forAll { list -> list.name.shouldBeEmpty() } + this(MediaType.ANIME).forAll { list -> list.name.shouldBeEmpty() } + this(MediaType.MANGA).forAll { list -> list.name.shouldBeEmpty() } } } "anime" - { "a collection of animes without entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rewatching" @@ -110,7 +105,7 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { animeList = buildMediaListTypeOptions { sectionOrder = listOf("Watching", "Rewatching", "Completed TV", "Paused") @@ -118,7 +113,7 @@ internal class MediaListMapperTest : FreeSpec({ } } } - }.mediaList(MediaType.ANIME).also { list -> + }.invoke(MediaType.ANIME).also { list -> list[0].name shouldBe "Watching" list[1].name shouldBe "Rewatching" list[2].name shouldBe "Completed TV" @@ -128,7 +123,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of animes without entries and sectionOrder" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rewatching" @@ -148,14 +143,14 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { animeList = buildMediaListTypeOptions { - sectionOrder = null + sectionOrder = emptyList() } } } } - }.mediaList(MediaType.ANIME).also { list -> + }.invoke(MediaType.ANIME).also { list -> list[0].name shouldBe "Rewatching" list[1].name shouldBe "Watching" list[2].name shouldBe "Paused" @@ -165,7 +160,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of animes without entries and mediaListOptions" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rewatching" @@ -184,11 +179,9 @@ internal class MediaListMapperTest : FreeSpec({ entries = emptyList() }, ) - user = buildUser { - this["mediaListOptions"] = null - } + user = buildUser { mediaListOptions = buildMediaListOptions { } } } - }.mediaList(MediaType.ANIME).also { list -> + }.invoke(MediaType.ANIME).also { list -> list[0].name shouldBe "Rewatching" list[1].name shouldBe "Watching" list[2].name shouldBe "Paused" @@ -198,7 +191,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of animes without entries and animeListSorting" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rewatching" @@ -218,12 +211,12 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { - animeList = null + mediaListOptions = buildMediaListOptions { + animeList = buildMediaListTypeOptions { } } } } - }.mediaList(MediaType.ANIME).also { list -> + }.invoke(MediaType.ANIME).also { list -> list[0].name shouldBe "Rewatching" list[1].name shouldBe "Watching" list[2].name shouldBe "Paused" @@ -233,7 +226,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of animes with valid entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Watching" @@ -278,7 +271,7 @@ internal class MediaListMapperTest : FreeSpec({ }, ) } - }.mediaList(MediaType.ANIME).forAll { list -> + }.invoke(MediaType.ANIME).forAll { list -> list.entries.forAll { entry -> with(entry.list) { id shouldBe 100 @@ -321,14 +314,14 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of animes with invalid entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Watching" entries = listOf( buildMediaList { id = Int.zero - score = null + score = Double.zero progress = null progressVolumes = null repeat = null @@ -340,10 +333,10 @@ internal class MediaListMapperTest : FreeSpec({ updatedAt = null media = buildMedia { id = Int.zero - title = null + title = buildMediaTitle { userPreferred = String.empty } episodes = null format = null - coverImage = null + coverImage = buildMediaCoverImage { large = String.empty } nextAiringEpisode = null } }, @@ -351,7 +344,7 @@ internal class MediaListMapperTest : FreeSpec({ }, ) } - }.mediaList(MediaType.ANIME).forAll { list -> + }.invoke(MediaType.ANIME).forAll { list -> list.entries.forAll { entry -> with(entry.list) { id shouldBe Int.zero @@ -387,7 +380,7 @@ internal class MediaListMapperTest : FreeSpec({ "manga" - { "a collection of mangas without entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rereading" @@ -407,15 +400,14 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { mangaList = buildMediaListTypeOptions { - sectionOrder = - listOf("Reading", "Rereading", "Completed Novel", "Paused") + sectionOrder = listOf("Reading", "Rereading", "Completed Novel", "Paused") } } } } - }.mediaList(MediaType.MANGA).also { list -> + }.invoke(MediaType.MANGA).also { list -> list[0].name shouldBe "Reading" list[1].name shouldBe "Rereading" list[2].name shouldBe "Completed Novel" @@ -425,7 +417,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of mangas without entries and sectionOrder" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rereading" @@ -445,14 +437,14 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { mangaList = buildMediaListTypeOptions { - sectionOrder = null + sectionOrder = emptyList() } } } } - }.mediaList(MediaType.MANGA).also { list -> + }.invoke(MediaType.MANGA).also { list -> list[0].name shouldBe "Rereading" list[1].name shouldBe "Reading" list[2].name shouldBe "Paused" @@ -462,7 +454,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of mangas without entries and mediaListOptions" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rereading" @@ -481,11 +473,9 @@ internal class MediaListMapperTest : FreeSpec({ entries = emptyList() }, ) - user = buildUser { - this["mediaListOptions"] = null - } + user = buildUser { mediaListOptions = buildMediaListOptions { } } } - }.mediaList(MediaType.MANGA).also { list -> + }.invoke(MediaType.MANGA).also { list -> list[0].name shouldBe "Rereading" list[1].name shouldBe "Reading" list[2].name shouldBe "Paused" @@ -495,7 +485,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of mangas without entries and mangaListSorting" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rereading" @@ -515,12 +505,12 @@ internal class MediaListMapperTest : FreeSpec({ }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { - mangaList = null + mediaListOptions = buildMediaListOptions { + mangaList = buildMediaListTypeOptions { } } } } - }.mediaList(MediaType.MANGA).also { list -> + }.invoke(MediaType.MANGA).also { list -> list[0].name shouldBe "Rereading" list[1].name shouldBe "Reading" list[2].name shouldBe "Paused" @@ -530,7 +520,7 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of mangas with valid entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Reading" @@ -573,7 +563,7 @@ internal class MediaListMapperTest : FreeSpec({ }, ) } - }.mediaList(MediaType.MANGA).forAll { list -> + }.invoke(MediaType.MANGA).forAll { list -> list.entries.forAll { entry -> with(entry.list) { id shouldBe 100 @@ -610,14 +600,14 @@ internal class MediaListMapperTest : FreeSpec({ "a collection of mangas with invalid entries" { MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Reading" entries = listOf( buildMediaList { id = Int.zero - score = null + score = Double.zero progress = null progressVolumes = null repeat = null @@ -629,11 +619,11 @@ internal class MediaListMapperTest : FreeSpec({ updatedAt = null media = buildMedia { id = Int.zero - title = null + title = buildMediaTitle { userPreferred = String.empty } chapters = null volumes = null format = null - coverImage = null + coverImage = buildMediaCoverImage { large = String.empty } nextAiringEpisode = null } }, @@ -641,7 +631,7 @@ internal class MediaListMapperTest : FreeSpec({ }, ) } - }.mediaList(MediaType.MANGA).forAll { list -> + }.invoke(MediaType.MANGA).forAll { list -> list.entries.forAll { entry -> with(entry.list) { id shouldBe Int.zero diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mocks.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mocks.kt index 3a6c764d7..884d530a0 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mocks.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/mocks.kt @@ -19,20 +19,21 @@ import korlibs.time.Date import korlibs.time.DateTimeTz internal val mediaListCollectionQueryMock = MediaListCollectionQuery( + user = null, type = Arb.enum().next(), ) internal val mediaListEntriesMutationMock = MediaListEntriesMutation( id = Arb.int().next(), - score = Optional.presentIfNotNull(Arb.positiveDouble().orNull().next()), - progress = Optional.presentIfNotNull(Arb.positiveInt().orNull().next()), - progressVolumes = Optional.presentIfNotNull(Arb.positiveInt().orNull().next()), - repeat = Optional.presentIfNotNull(Arb.positiveInt().orNull().next()), - private = Optional.presentIfNotNull(Arb.boolean().orNull().next()), - notes = Optional.presentIfNotNull(Arb.string().orNull().next()), - hiddenFromStatusLists = Optional.presentIfNotNull(Arb.boolean().orNull().next()), - startedAt = Optional.presentIfNotNull(Arb.fuzzyDate().orNull().next()), - completedAt = Optional.presentIfNotNull(Arb.fuzzyDate().orNull().next()), + score = Arb.positiveDouble().orNull().next(), + progress = Arb.positiveInt().orNull().next(), + progressVolumes = Arb.positiveInt().orNull().next(), + repeat = Arb.positiveInt().orNull().next(), + private = Arb.boolean().orNull().next(), + notes = Arb.string().orNull().next(), + hiddenFromStatusLists = Arb.boolean().orNull().next(), + startedAt = Arb.fuzzyDate().orNull().next(), + completedAt = Arb.fuzzyDate().orNull().next(), ) internal val mediaListMock = MediaList( @@ -49,13 +50,7 @@ internal val mediaListMock = MediaList( updatedAt = Arb.dateTimeTz().orNull().next(), ) -internal val apolloErrorMock = Error( - message = Arb.string().next(), - locations = emptyList(), - path = emptyList(), - extensions = emptyMap(), - nonStandardFields = emptyMap(), -) +internal val apolloErrorMock = Error.Builder(Arb.string().next()).build() private fun Arb.Companion.date() = arbitrary { Date( diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/repositories/ListsRepositoryTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/repositories/ListsRepositoryTest.kt index 06d7028f9..6b454fa23 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/repositories/ListsRepositoryTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/repositories/ListsRepositoryTest.kt @@ -15,6 +15,7 @@ import dev.alvr.katana.features.lists.domain.models.MediaCollection import dev.alvr.katana.features.lists.domain.models.entries.MediaEntry import dev.alvr.katana.features.lists.domain.repositories.ListsRepository import dev.mokkery.answering.returns +import dev.mokkery.answering.sequentially import dev.mokkery.every import dev.mokkery.everySuspend import dev.mokkery.matcher.any @@ -22,7 +23,6 @@ import dev.mokkery.mock import dev.mokkery.verify import dev.mokkery.verifySuspend import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf internal class ListsRepositoryTest : FreeSpec() { @@ -37,7 +37,7 @@ internal class ListsRepositoryTest : FreeSpec() { val collection = MediaCollection(emptyList()) every { animeSource.animeCollection } returns flowOf(collection.right()) - repo.animeCollection.test(100.milliseconds) { + repo.animeCollection.test { awaitItem().shouldBeRight(collection) awaitComplete() } @@ -49,7 +49,7 @@ internal class ListsRepositoryTest : FreeSpec() { val collection = MediaCollection(emptyList()) every { mangaSource.mangaCollection } returns flowOf(collection.right()) - repo.mangaCollection.test(100.milliseconds) { + repo.mangaCollection.test { awaitItem().shouldBeRight(collection) awaitComplete() } @@ -73,17 +73,5 @@ internal class ListsRepositoryTest : FreeSpec() { verifySuspend { commonSource.updateList(mediaListMock) } } } - -// "failure updating the list" { -// everySuspend { commonSource.updateList(any()) } sequentially { -// returns(ListsFailure.UpdatingList.left()) -// returns(Failure.Unknown.left()) -// } -// -// repo.updateList(mediaListMock).shouldBeLeft(ListsFailure.UpdatingList) -// repo.updateList(mediaListMock).shouldBeLeft(Failure.Unknown) -// -// verifySuspend { repo.updateList(mediaListMock) } -// } } } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ApolloListsRemoteSourceTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ApolloListsRemoteSourceTest.kt index 367a3dcd6..5ab37c102 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ApolloListsRemoteSourceTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ApolloListsRemoteSourceTest.kt @@ -9,8 +9,8 @@ import com.apollographql.apollo3.testing.MapTestNetworkTransport import com.apollographql.apollo3.testing.registerTestNetworkError import com.apollographql.apollo3.testing.registerTestResponse import dev.alvr.katana.common.user.domain.managers.UserIdManager +import dev.alvr.katana.core.common.empty import dev.alvr.katana.core.common.zero -import dev.alvr.katana.core.remote.optional import dev.alvr.katana.core.remote.type.MediaFormat import dev.alvr.katana.core.remote.type.MediaType import dev.alvr.katana.core.remote.type.buildAiringSchedule @@ -54,7 +54,6 @@ import korlibs.time.Date import korlibs.time.DateTime import korlibs.time.DateTimeTz import korlibs.time.TimezoneOffset -import kotlin.time.Duration.Companion.milliseconds @ApolloExperimental internal class ApolloListsRemoteSourceTest : FreeSpec() { @@ -63,7 +62,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { private val client = ApolloClient.Builder().networkTransport(MapTestNetworkTransport()).build() private val userId = 37_384.right() - private val userIdOpt = userId.optional() + private val userIdOpt = userId.getOrNull() private val source: CommonListsRemoteSource = CommonListsRemoteSourceImpl(client, userIdManager, reloadInterceptor) private val animeSource: AnimeListsRemoteSource = AnimeListsRemoteSourceImpl(source) @@ -77,7 +76,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "anime" - { "the collection has no lists" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = emptyList() } } @@ -87,7 +86,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeRight().lists.shouldBeEmpty() awaitComplete() } @@ -96,7 +95,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entries are empty" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Watching" @@ -110,13 +109,9 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { name = "Custom List" entries = emptyList() }, - buildMediaListGroup { - name = null - entries = emptyList() - }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { animeList = buildMediaListTypeOptions { sectionOrder = listOf("Watching", "Completed TV", "Custom List") } @@ -130,9 +125,9 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeRight().lists - .shouldHaveSize(4) + .shouldHaveSize(3) .also { lists -> with(lists.first()) { entries.shouldBeEmpty() @@ -146,14 +141,14 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entry has null values" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Watching" entries = listOf( buildMediaList { id = Int.zero - score = null + score = Double.zero progress = null progressVolumes = null repeat = null @@ -164,10 +159,10 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { completedAt = null media = buildMedia { id = Int.zero - title = null + title = buildMediaTitle { userPreferred = String.empty } episodes = null format = null - coverImage = null + coverImage = buildMediaCoverImage { large = String.empty } nextAiringEpisode = null } }, @@ -182,7 +177,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeRight().lists.also { lists -> val entry = lists.first().entries.shouldHaveSize(1).first() @@ -219,7 +214,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entry has values" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Watching" @@ -270,7 +265,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeRight().lists.also { lists -> val entry = lists.first().entries.shouldHaveSize(1).first() @@ -317,8 +312,8 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { null, ) - animeSource.animeCollection.test(100.milliseconds) { - awaitItem().shouldBeRight().lists.shouldBeEmpty() + animeSource.animeCollection.test { + awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) awaitComplete() } @@ -333,7 +328,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { ), ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) awaitComplete() } @@ -345,7 +340,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "manga" - { "the collection has no lists" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = emptyList() } } @@ -355,7 +350,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeRight().lists.shouldBeEmpty() awaitComplete() } @@ -365,7 +360,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entries are empty" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Rereading" @@ -379,13 +374,9 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { name = "Custom List" entries = emptyList() }, - buildMediaListGroup { - name = null - entries = emptyList() - }, ) user = buildUser { - this["mediaListOptions"] = buildMediaListOptions { + mediaListOptions = buildMediaListOptions { mangaList = buildMediaListTypeOptions { sectionOrder = listOf("Custom List", "Reading", "Rereading") } @@ -399,9 +390,9 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeRight().lists - .shouldHaveSize(4) + .shouldHaveSize(3) .also { lists -> with(lists.first()) { entries.shouldBeEmpty() @@ -415,14 +406,14 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entry has null values" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Reading" entries = listOf( buildMediaList { id = Int.zero - score = null + score = Double.zero progress = null progressVolumes = null repeat = null @@ -433,11 +424,11 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { completedAt = null media = buildMedia { id = Int.zero - title = null + title = buildMediaTitle { userPreferred = String.empty } chapters = null volumes = null format = null - coverImage = null + coverImage = buildMediaCoverImage { large = String.empty } nextAiringEpisode = null } }, @@ -452,7 +443,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeRight().lists.also { lists -> val entry = lists.first().entries.shouldHaveSize(1).first() @@ -489,7 +480,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { "the entry has values" { val query = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = listOf( buildMediaListGroup { name = "Reading" @@ -538,7 +529,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { query, ) - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeRight().lists.also { lists -> val entry = lists.first().entries.shouldHaveSize(1).first() @@ -579,8 +570,8 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { null, ) - mangaSource.mangaCollection.test(100.milliseconds) { - awaitItem().shouldBeRight().lists.shouldBeEmpty() + mangaSource.mangaCollection.test { + awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) awaitComplete() } @@ -595,7 +586,7 @@ internal class ApolloListsRemoteSourceTest : FreeSpec() { ), ) - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) awaitComplete() } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/CommonListsRemoteSourceTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/CommonListsRemoteSourceTest.kt index 4d8d0e344..4b0ff133c 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/CommonListsRemoteSourceTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/CommonListsRemoteSourceTest.kt @@ -6,19 +6,21 @@ import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.annotations.ApolloExperimental import com.apollographql.apollo3.api.ApolloResponse import com.apollographql.apollo3.interceptor.ApolloInterceptor -import com.apollographql.apollo3.mockserver.MockResponse import com.apollographql.apollo3.mockserver.MockServer +import com.apollographql.apollo3.mockserver.enqueueError +import com.apollographql.apollo3.mockserver.enqueueString import com.apollographql.apollo3.testing.QueueTestNetworkTransport import com.apollographql.apollo3.testing.enqueueTestResponse import com.benasher44.uuid.uuid4 import dev.alvr.katana.common.user.domain.managers.UserIdManager +import dev.alvr.katana.core.domain.failures.Failure import dev.alvr.katana.core.remote.type.MediaType import dev.alvr.katana.core.remote.type.buildMediaListCollection +import dev.alvr.katana.core.remote.type.buildUser import dev.alvr.katana.core.tests.shouldBeLeft import dev.alvr.katana.core.tests.shouldBeRight import dev.alvr.katana.features.lists.data.MediaListCollectionQuery import dev.alvr.katana.features.lists.data.apolloErrorMock -import dev.alvr.katana.features.lists.data.enqueueResponse import dev.alvr.katana.features.lists.data.mediaListCollectionQueryMock import dev.alvr.katana.features.lists.data.mediaListEntriesMutationMock import dev.alvr.katana.features.lists.data.mediaListMock @@ -30,8 +32,6 @@ import dev.mokkery.everySuspend import dev.mokkery.mock import dev.mokkery.verifySuspend import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.seconds @OptIn(ApolloExperimental::class) internal class CommonListsRemoteSourceTest : FreeSpec() { @@ -50,12 +50,16 @@ internal class CommonListsRemoteSourceTest : FreeSpec() { val response = ApolloResponse.Builder( operation = mediaListCollectionQueryMock, requestUuid = uuid4(), - data = data, - ).build() + ).data(data).build() client.enqueueTestResponse(response) - source.getMediaCollection(type).test(100.milliseconds) { - awaitItem().shouldBeRight(MediaCollection(emptyList())) + source.getMediaCollection(type).test { + if (data == null) { + awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) + } else { + awaitItem().shouldBeRight(MediaCollection(emptyList())) + } + cancelAndIgnoreRemainingEvents() } verifySuspend { userIdManager.getId() } @@ -66,12 +70,11 @@ internal class CommonListsRemoteSourceTest : FreeSpec() { val response = ApolloResponse.Builder( operation = mediaListCollectionQueryMock, requestUuid = uuid4(), - data = data, - ).errors(listOf(apolloErrorMock)).build() + ).data(data).errors(listOf(apolloErrorMock)).build() client.enqueueTestResponse(response) - source.getMediaCollection(type).test(100.milliseconds) { - awaitItem().shouldBeRight(MediaCollection(emptyList())) + source.getMediaCollection(type).test { + awaitItem().shouldBeLeft(Failure.Unknown) cancelAndIgnoreRemainingEvents() } verifySuspend { userIdManager.getId() } @@ -95,14 +98,14 @@ internal class CommonListsRemoteSourceTest : FreeSpec() { reloadInterceptor, ) - afterSpec { mockServer.stop() } + afterSpec { mockServer.close() } - badClient().forEach { (type, action) -> + mockServer.badClient().forEach { (type, enqueueAction) -> "a HTTP error occurs" { everySuspend { userIdManager.getId() } returns 37_384.right() - mockServer.enqueueResponse(action) + enqueueAction() - source.getMediaCollection(type).test(5.seconds) { + source.getMediaCollection(type).test { awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) cancelAndIgnoreRemainingEvents() } @@ -114,9 +117,9 @@ internal class CommonListsRemoteSourceTest : FreeSpec() { private fun queryList(): List> { val empty = MediaListCollectionQuery.Data { - this["collection"] = buildMediaListCollection { + this["MediaListCollection"] = buildMediaListCollection { lists = emptyList() - user = null + user = buildUser { } } } @@ -124,24 +127,20 @@ internal class CommonListsRemoteSourceTest : FreeSpec() { add(null) add(empty) } - val types = MediaType.knownValues() + val types = MediaType.knownEntries return buildList { values.forEach { v -> types.forEach { t -> add(v to t) } } } } - private fun badClient(): List Unit)>> { - fun apolloCommand( - block: MockResponse.Builder.() -> Unit, - ): MockResponse.Builder.() -> Unit = { MockResponse.Builder().apply(block) } - + private fun MockServer.badClient(): List Unit)>> { val commands = buildList { - add(apolloCommand { statusCode(500) }) - add(apolloCommand { body("Malformed body") }) - add(apolloCommand { body("""{"data": {"random": 42}}""") }) + add { enqueueError(500) } + add { enqueueString("Malformed body") } + add { enqueueString("""{"data": {"random": 42}}""") } } - val types = MediaType.knownValues() + val types = MediaType.knownEntries return buildList { commands.forEach { c -> types.forEach { t -> add(t to c) } } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ListsRemoteSourceTest.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ListsRemoteSourceTest.kt index 0c34f5eda..eb5e67ad6 100644 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ListsRemoteSourceTest.kt +++ b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/sources/ListsRemoteSourceTest.kt @@ -15,7 +15,6 @@ import dev.mokkery.every import dev.mokkery.mock import dev.mokkery.verify import io.kotest.core.spec.style.FreeSpec -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf internal class ListsRemoteSourceTest : FreeSpec() { @@ -33,12 +32,12 @@ internal class ListsRemoteSourceTest : FreeSpec() { MediaCollection(emptyList()).right(), ) - animeSource.animeCollection.test(100.milliseconds) { + animeSource.animeCollection.test { awaitItem().shouldBeRight(MediaCollection(emptyList())) cancelAndIgnoreRemainingEvents() } - mangaSource.mangaCollection.test(100.milliseconds) { + mangaSource.mangaCollection.test { awaitItem().shouldBeRight(MediaCollection(emptyList())) cancelAndIgnoreRemainingEvents() } diff --git a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/utils.kt b/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/utils.kt deleted file mode 100644 index 4952d415d..000000000 --- a/features/lists/data/src/commonTest/kotlin/dev/alvr/katana/features/lists/data/utils.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.alvr.katana.features.lists.data - -import com.apollographql.apollo3.annotations.ApolloExperimental -import com.apollographql.apollo3.mockserver.MockResponse -import com.apollographql.apollo3.mockserver.MockServer - -@OptIn(ApolloExperimental::class) -internal fun MockServer.enqueueResponse(builder: MockResponse.Builder.() -> Unit) { - repeat(4) { - enqueue(MockResponse.Builder().apply(builder).build()) - } -} diff --git a/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveAnimeListUseCaseTest.kt b/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveAnimeListUseCaseTest.kt index 1ddc8c156..e32e16314 100644 --- a/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveAnimeListUseCaseTest.kt +++ b/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveAnimeListUseCaseTest.kt @@ -19,7 +19,6 @@ import dev.mokkery.mock import dev.mokkery.verify import io.kotest.core.spec.style.FreeSpec import io.kotest.core.test.TestCase -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf import org.koin.test.KoinTest import org.koin.test.inject @@ -38,7 +37,7 @@ internal class ObserveAnimeListUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeRight(MediaCollection(emptyList())) cancelAndConsumeRemainingEvents() } @@ -53,7 +52,7 @@ internal class ObserveAnimeListUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) cancelAndConsumeRemainingEvents() } diff --git a/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveMangaListUseCaseTest.kt b/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveMangaListUseCaseTest.kt index f3bf55424..d44b1bb9c 100644 --- a/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveMangaListUseCaseTest.kt +++ b/features/lists/domain/src/commonTest/kotlin/dev/alvr/katana/features/lists/domain/usecases/ObserveMangaListUseCaseTest.kt @@ -19,7 +19,6 @@ import dev.mokkery.mock import dev.mokkery.verify import io.kotest.core.spec.style.FreeSpec import io.kotest.core.test.TestCase -import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.flow.flowOf import org.koin.test.KoinTest import org.koin.test.inject @@ -38,7 +37,7 @@ internal class ObserveMangaListUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeRight(MediaCollection(emptyList())) cancelAndConsumeRemainingEvents() } @@ -53,7 +52,7 @@ internal class ObserveMangaListUseCaseTest : FreeSpec(), KoinTest { useCase() - useCase.flow.test(100.milliseconds) { + useCase.flow.test { awaitItem().shouldBeLeft(ListsFailure.GetMediaCollection) cancelAndConsumeRemainingEvents() }