From adde145530c533f0e0227bef4f71c410bf46497e Mon Sep 17 00:00:00 2001 From: Matt Ramotar Date: Wed, 27 Nov 2024 23:27:36 -0500 Subject: [PATCH] Cover RealPostFetcherServices Signed-off-by: Matt Ramotar --- .../store/fetcher/RealPostFetcherServices.kt | 5 + .../fetcher/RealPostFetcherServicesTest.kt | 229 ++++++++++++++++++ 2 files changed, 234 insertions(+) diff --git a/xplat/lib/repositories/post/impl/src/commonMain/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServices.kt b/xplat/lib/repositories/post/impl/src/commonMain/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServices.kt index ce49998..f38d266 100644 --- a/xplat/lib/repositories/post/impl/src/commonMain/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServices.kt +++ b/xplat/lib/repositories/post/impl/src/commonMain/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServices.kt @@ -146,6 +146,11 @@ class RealPostFetcherServices( ) ) + output.values.filterIsInstance().forEach { post -> + // Save the post and associated entities + postDAO.insertOneComposite(post) + } + emit(output) } diff --git a/xplat/lib/repositories/post/impl/src/commonTest/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServicesTest.kt b/xplat/lib/repositories/post/impl/src/commonTest/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServicesTest.kt index 0a77657..aa430a3 100644 --- a/xplat/lib/repositories/post/impl/src/commonTest/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServicesTest.kt +++ b/xplat/lib/repositories/post/impl/src/commonTest/kotlin/org/mobilenativefoundation/trails/xplat/lib/repositories/post/impl/store/fetcher/RealPostFetcherServicesTest.kt @@ -4,6 +4,7 @@ import app.cash.turbine.test import dev.mokkery.MockMode import dev.mokkery.answering.returns import dev.mokkery.everySuspend +import dev.mokkery.matcher.eq import dev.mokkery.matcher.ofType import dev.mokkery.mock import dev.mokkery.verifySuspend @@ -12,11 +13,15 @@ import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime +import org.mobilenativefoundation.trails.xplat.lib.models.post.Creator import org.mobilenativefoundation.trails.xplat.lib.models.post.Platform import org.mobilenativefoundation.trails.xplat.lib.models.post.Post import org.mobilenativefoundation.trails.xplat.lib.models.post.PostOutput import org.mobilenativefoundation.trails.xplat.lib.operations.io.Operation +import org.mobilenativefoundation.trails.xplat.lib.operations.io.Operation.Query.QueryMany +import org.mobilenativefoundation.trails.xplat.lib.operations.io.Operation.Query.QueryOne import org.mobilenativefoundation.trails.xplat.lib.operations.query.DataSources +import org.mobilenativefoundation.trails.xplat.lib.operations.query.Query import org.mobilenativefoundation.trails.xplat.lib.repositories.post.impl.extensions.PostExtensions.asPostEntity import org.mobilenativefoundation.trails.xplat.lib.repositories.post.impl.store.database.PostDAO import org.mobilenativefoundation.trails.xplat.lib.rest.api.post.PostOperations @@ -29,6 +34,61 @@ class RealPostFetcherServicesTest { private val postDAO = mock(MockMode.autoUnit) private val postFetcherServices = RealPostFetcherServices(client, postDAO) + @Test + fun findAndEmitOne_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = Operation.Query.FindOne( + key = Post.Key(1), + dataSources = DataSources.all + ) + + val post1 = createPost(1) + + everySuspend { client.findOne(eq(operation.key)) }.returns(post1) + + // When + postFetcherServices.findAndEmitOne(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOne(post1.asPostEntity()) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Single(post1), output) + expectNoEvents() + } + } + + @Test + fun findAndEmitOneComposite_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = + runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = Operation.Query.FindOneComposite( + key = Post.Key(1), + dataSources = DataSources.all + ) + + val compositePost = createCompositePost(1) + + everySuspend { client.findOneComposite(eq(operation.key)) }.returns(compositePost) + + // When + postFetcherServices.findAndEmitOneComposite(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOneComposite(compositePost) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Single(compositePost), output) + expectNoEvents() + } + } + @Test fun findAndEmitMany_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = runTest { @@ -64,6 +124,153 @@ class RealPostFetcherServicesTest { } } + @Test + fun findAndEmitAll_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = + runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = Operation.Query.FindAll( + dataSources = DataSources.all + ) + + val post1 = createPost(1) + val post2 = createPost(2) + + val posts = listOf( + post1, + post2 + ) + + everySuspend { client.findAll() }.returns(posts) + + // When + postFetcherServices.findAndEmitAll(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOne(post1.asPostEntity()) } + verifySuspend { postDAO.insertOne(post2.asPostEntity()) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Collection(posts), output) + expectNoEvents() + } + } + + @Test + fun queryAndEmitOne_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = + runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = QueryOne( + query = Query.One( + dataSources = DataSources.all, + predicate = null, + order = null + ), + dataSources = DataSources.all + ) + + val post1 = createPost(1) + + everySuspend { client.queryOne(ofType()) }.returns(post1) + + // When + postFetcherServices.queryAndEmitOne(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOne(post1.asPostEntity()) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Single(post1), output) + expectNoEvents() + } + } + + @Test + fun queryAndEmitMany_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = + runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = QueryMany( + query = Query.Many( + dataSources = DataSources.all, + predicate = null, + order = null, + limit = null + ), + dataSources = DataSources.all + ) + + val post1 = createPost(1) + val post2 = createPost(2) + + val posts = listOf( + post1, + post2 + ) + + everySuspend { client.queryMany(ofType()) }.returns(posts) + + // When + postFetcherServices.queryAndEmitMany(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOne(post1.asPostEntity()) } + verifySuspend { postDAO.insertOne(post2.asPostEntity()) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Collection(posts), output) + expectNoEvents() + } + } + + + @Test + fun queryAndEmitManyComposite_givenNormalClientAndDb_whenSuccessfulNetworkRequest_thenShouldUpdateDbAndEmitOutput() = + runTest { + // Given + val flow = MutableSharedFlow(replay = 10) + + val operation = Operation.Query.QueryManyComposite( + query = Query.Many( + dataSources = DataSources.all, + predicate = null, + order = null, + limit = null + ), + dataSources = DataSources.all + ) + + val post1 = createCompositePost(1) + val post2 = createCompositePost(2) + + val posts = listOf( + post1, + post2 + ) + + everySuspend { client.queryManyComposite(ofType()) }.returns(PostOutput.Collection(posts)) + + // When + postFetcherServices.queryAndEmitManyComposite(operation) { flow.emit(it) } + + // Then + verifySuspend { postDAO.insertOneComposite(post1) } + verifySuspend { postDAO.insertOneComposite(post2) } + + flow.test { + val output = awaitItem() + assertEquals(PostOutput.Collection(posts), output) + expectNoEvents() + } + } + private fun createPost(id: Int) = Post.Node( key = Post.Key(id), properties = Post.Properties( @@ -80,4 +287,26 @@ class RealPostFetcherServicesTest { locationName = null, ) ) + + private fun createCompositePost(id: Int) = Post.Composite( + node = createPost(id), + edges = Post.Edges( + creator = createCreator(id), + hashtags = emptyList(), + mentions = emptyList(), + media = emptyList() + ) + ) + + private fun createCreator(id: Int) = Creator.Node( + key = Creator.Key(id), + properties = Creator.Properties( + username = "", + fullName = "", + profilePicURL = "", + isVerified = false, + bio = "", + platform = Platform.Instagram + ) + ) } \ No newline at end of file