diff --git a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandler.kt b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandler.kt index 3b255e962..8d04d8d42 100644 --- a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandler.kt +++ b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandler.kt @@ -31,7 +31,8 @@ class PodcastHandler( fun findById(r: ServerRequest): ServerResponse { val id = r.pathVariable("id").let(UUID::fromString) - val podcast = podcastService.findById(id).block()!! + val podcast = podcastService.findById(id) + ?: return ServerResponse.notFound().build() val body = podcast.toHAL() @@ -40,7 +41,6 @@ class PodcastHandler( fun findAll(@Suppress("UNUSED_PARAMETER") r: ServerRequest): ServerResponse { val podcasts = podcastService.findAll() - .collectList().block()!! .map(Podcast::toHAL) val body = FindAllPodcastHAL(podcasts) @@ -52,7 +52,7 @@ class PodcastHandler( val creationRequest = r.body() .toPodcastCreation() - val podcast = podcastService.save(creationRequest).block()!! + val podcast = podcastService.save(creationRequest) return ServerResponse.ok().body(podcast.toHAL()) } @@ -61,7 +61,7 @@ class PodcastHandler( val updateRequest = r.body() .toPodcastUpdate() - val podcast = podcastService.update(updateRequest).block()!! + val podcast = podcastService.update(updateRequest) return ServerResponse.ok().body(podcast.toHAL()) } @@ -69,7 +69,7 @@ class PodcastHandler( fun delete(r: ServerRequest): ServerResponse { val id = r.pathVariable("id").let(UUID::fromString) - podcastService.deleteById(id).block() + podcastService.deleteById(id) return ServerResponse.noContent().build() } @@ -78,7 +78,8 @@ class PodcastHandler( val host = r.extractHost() val id = r.pathVariable("id").let(UUID::fromString) - val podcast = podcastService.findById(id).block()!! + val podcast = podcastService.findById(id) + ?: return ServerResponse.notFound().build() log.debug("the url of the podcast cover is {}", podcast.cover.url) @@ -92,9 +93,9 @@ class PodcastHandler( return ServerResponse.seeOther(uri).build() } - fun findStatByPodcastIdAndPubDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndPubDate(id, number).collectList().block()!! } - fun findStatByPodcastIdAndDownloadDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndDownloadDate(id, number).collectList().block()!! } - fun findStatByPodcastIdAndCreationDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndCreationDate(id, number).collectList().block()!! } + fun findStatByPodcastIdAndPubDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndPubDate(id, number) } + fun findStatByPodcastIdAndDownloadDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndDownloadDate(id, number) } + fun findStatByPodcastIdAndCreationDate(r: ServerRequest): ServerResponse = statsBy(r) { id, number -> podcastService.findStatByPodcastIdAndCreationDate(id, number) } private fun statsBy(r: ServerRequest, proj: (id: UUID, n: Int) -> List): ServerResponse { val id = UUID.fromString(r.pathVariable("id")) @@ -105,9 +106,9 @@ class PodcastHandler( return ServerResponse.ok().body(stats) } - fun findStatByTypeAndCreationDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndCreationDate(number).collectList().block()!! } - fun findStatByTypeAndPubDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndPubDate(number).collectList().block()!! } - fun findStatByTypeAndDownloadDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndDownloadDate(number).collectList().block()!! } + fun findStatByTypeAndCreationDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndCreationDate(number) } + fun findStatByTypeAndPubDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndPubDate(number) } + fun findStatByTypeAndDownloadDate(r: ServerRequest) = statsBy(r) { number -> podcastService.findStatByTypeAndDownloadDate(number) } private fun statsBy(r: ServerRequest, proj: (n: Int) -> List): ServerResponse { val numberOfMonths = r.paramOrNull("numberOfMonths")?.toInt() ?: 1 diff --git a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastService.kt b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastService.kt index a38f43e59..4dd92d4fd 100644 --- a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastService.kt +++ b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastService.kt @@ -6,94 +6,101 @@ import com.github.davinkevin.podcastserver.service.storage.FileStorageService import com.github.davinkevin.podcastserver.service.storage.MovePodcastRequest import com.github.davinkevin.podcastserver.tag.Tag import com.github.davinkevin.podcastserver.tag.TagRepository -import reactor.core.publisher.Flux -import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.toFlux -import reactor.kotlin.core.publisher.toMono -import reactor.kotlin.core.util.function.component1 -import reactor.kotlin.core.util.function.component2 import java.util.* class PodcastService( - private val repository: PodcastRepository, - private val coverRepository: CoverRepository, - private val tagRepository: TagRepository, - private val fileService: FileStorageService + private val repository: PodcastRepository, + private val coverRepository: CoverRepository, + private val tagRepository: TagRepository, + private val fileService: FileStorageService ) { - fun findAll(): Flux = repository.findAll() - fun findById(id: UUID): Mono = repository.findById(id) - - fun findStatByPodcastIdAndPubDate(id: UUID, numberOfMonths: Int) = repository.findStatByPodcastIdAndPubDate(id, numberOfMonths) - fun findStatByPodcastIdAndDownloadDate(id: UUID, numberOfMonths: Int) = repository.findStatByPodcastIdAndDownloadDate(id, numberOfMonths) - fun findStatByPodcastIdAndCreationDate(id: UUID, numberOfMonths: Int) = repository.findStatByPodcastIdAndCreationDate(id, numberOfMonths) - - fun findStatByTypeAndCreationDate(numberOfMonths: Int) = repository.findStatByTypeAndCreationDate(numberOfMonths) - fun findStatByTypeAndPubDate(numberOfMonths: Int) = repository.findStatByTypeAndPubDate(numberOfMonths) - fun findStatByTypeAndDownloadDate(numberOfMonths: Int) = repository.findStatByTypeAndDownloadDate(numberOfMonths) - - fun save(p: PodcastForCreation): Mono { - - val oldTags = p.tags.toFlux().filter { it.id != null }.map { Tag(it.id!!, it.name) } - val newTags = p.tags.toFlux().filter { it.id == null }.flatMap { Mono.defer { tagRepository.save(it.name).toMono() } } - - val tags = Flux.merge(oldTags, newTags).collectList() - val cover = coverRepository.save(p.cover) - - return Mono.zip(tags, cover) - .flatMap { (t, c) -> repository.save( - title = p.title, - url = p.url?.toASCIIString(), - hasToBeDeleted = p.hasToBeDeleted, - type = p.type, - tags = t, - cover = c) - } - .delayUntil { fileService.downloadPodcastCover(it) } + fun findAll(): List = repository.findAll().collectList().block()!! + fun findById(id: UUID): Podcast? = repository.findById(id).block() + + fun findStatByPodcastIdAndPubDate(id: UUID, numberOfMonths: Int): List = + repository.findStatByPodcastIdAndPubDate(id, numberOfMonths).collectList().block()!! + fun findStatByPodcastIdAndDownloadDate(id: UUID, numberOfMonths: Int): List = + repository.findStatByPodcastIdAndDownloadDate(id, numberOfMonths).collectList().block()!! + fun findStatByPodcastIdAndCreationDate(id: UUID, numberOfMonths: Int): List = + repository.findStatByPodcastIdAndCreationDate(id, numberOfMonths).collectList().block()!! + + fun findStatByTypeAndCreationDate(numberOfMonths: Int): List = + repository.findStatByTypeAndCreationDate(numberOfMonths).collectList().block()!! + fun findStatByTypeAndPubDate(numberOfMonths: Int): List = + repository.findStatByTypeAndPubDate(numberOfMonths).collectList().block()!! + fun findStatByTypeAndDownloadDate(numberOfMonths: Int): List = + repository.findStatByTypeAndDownloadDate(numberOfMonths).collectList().block()!! + + fun save(p: PodcastForCreation): Podcast { + val oldTags = p.tags.filter { it.id != null }.map { Tag(it.id!!, it.name) } + val newTags = p.tags.filter { it.id == null }.map { tagRepository.save(it.name) } + + val tags = oldTags + newTags + val cover = coverRepository.save(p.cover).block()!! + + val podcast = repository.save( + title = p.title, + url = p.url?.toASCIIString(), + hasToBeDeleted = p.hasToBeDeleted, + type = p.type, + tags = tags, + cover = cover + ).block()!! + + fileService.downloadPodcastCover(podcast).block() + + return podcast } - fun update(updatePodcast: PodcastForUpdate): Mono = findById(updatePodcast.id).flatMap { p -> + fun update(updatePodcast: PodcastForUpdate): Podcast { + val p = findById(updatePodcast.id) + ?: error("Trying to upgrade a non existent podcast") - val oldTags = updatePodcast.tags.toFlux().filter { it.id != null }.map { Tag(it.id!!, it.name) } - val newTags = updatePodcast.tags.toFlux().filter { it.id == null }.flatMap { Mono.defer { tagRepository.save(it.name).toMono() } } - val tags = Flux.merge(oldTags, newTags).collectList() + val oldTags = updatePodcast.tags.filter { it.id != null }.map { Tag(it.id!!, it.name) } + val newTags = updatePodcast.tags.filter { it.id == null }.map { tagRepository.save(it.name) } + val tags = oldTags + newTags val newCover = updatePodcast.cover val oldCover = p.cover - val cover = - if (!newCover.url.toASCIIString().startsWith("/") && oldCover.url != newCover.url) - coverRepository.save(newCover).delayUntil { - fileService.downloadPodcastCover(p.copy(cover = Cover(it.id, it.url, it.height, it.width))) - } - else Cover(oldCover.id, oldCover.url, oldCover.height, oldCover.width).toMono() - - val title = - if (p.title != updatePodcast.title) { - val movePodcastDetails = MovePodcastRequest( - id = updatePodcast.id, - from = p.title, - to = updatePodcast.title - ) - fileService.movePodcast(movePodcastDetails) - } else Mono.empty() - - Mono.zip(tags, cover) - .flatMap { (t, c) -> - repository.update( - id = updatePodcast.id, - title = updatePodcast.title, - url = updatePodcast.url?.toASCIIString(), - hasToBeDeleted = updatePodcast.hasToBeDeleted, - tags = t, - cover = c) - } - .delayUntil { title } + + val isLocalCover = newCover.url.toASCIIString().startsWith("/") + val coversAreIdentical = oldCover.url != newCover.url + val cover = if (!isLocalCover && coversAreIdentical) + coverRepository.save(newCover).block()!!.also { + fileService + .downloadPodcastCover(p.copy(cover = Cover(it.id, it.url, it.height, it.width))) + .block() + } + else Cover(oldCover.id, oldCover.url, oldCover.height, oldCover.width) + + val podcast = repository.update( + id = updatePodcast.id, + title = updatePodcast.title, + url = updatePodcast.url?.toASCIIString(), + hasToBeDeleted = updatePodcast.hasToBeDeleted, + tags = tags, + cover = cover + ).block()!! + + val podcastTitleHasChanged = p.title != updatePodcast.title + if (podcastTitleHasChanged) { + val movePodcastDetails = MovePodcastRequest( + id = updatePodcast.id, + from = p.title, + to = updatePodcast.title + ) + fileService.movePodcast(movePodcastDetails).block() + } + + return podcast } - fun deleteById(id: UUID): Mono = - repository - .deleteById(id) - .delayUntil { fileService.deletePodcast(it) } - .then() + fun deleteById(id: UUID) { + repository + .deleteById(id) + .delayUntil { fileService.deletePodcast(it) } + .block() + } } diff --git a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandler.kt b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandler.kt index 87351e904..ca28eac21 100644 --- a/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandler.kt +++ b/backend/src/main/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandler.kt @@ -30,7 +30,7 @@ class PodcastXmlHandler( fun opml(r: ServerRequest): ServerResponse { val host = r.extractHost() - val podcasts = podcastService.findAll().collectList().block()!! + val podcasts = podcastService.findAll() val outlines = podcasts .map { OpmlOutline(OpmlOutline.Podcast(it.id, it.title, it.description), host) } @@ -59,7 +59,8 @@ class PodcastXmlHandler( page = itemPageable, podcastId = podcastId ) - val podcast = podcastService.findById(podcastId).block()!! + val podcast = podcastService.findById(podcastId) + ?: return ServerResponse.notFound().build() val items = page.content.map { it.toRssItem(host) } val rss = podcast.toRssChannel(callUrl) diff --git a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandlerTest.kt b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandlerTest.kt index 32363b589..044785f66 100644 --- a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandlerTest.kt +++ b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastHandlerTest.kt @@ -12,19 +12,15 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.mockito.kotlin.any +import org.mockito.kotlin.doNothing import org.mockito.kotlin.whenever import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.autoconfigure.ImportAutoConfiguration -import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.context.annotation.Import import org.springframework.http.MediaType import org.springframework.test.web.reactive.server.WebTestClient -import reactor.core.publisher.Flux import reactor.core.publisher.Mono -import reactor.kotlin.core.publisher.toFlux import reactor.kotlin.core.publisher.toMono import java.net.URI import java.time.LocalDate @@ -68,7 +64,7 @@ class PodcastHandlerTest( @Test fun `should find by id`() { /* Given */ - whenever(podcastService.findById(podcast.id)).thenReturn(podcast.toMono()) + whenever(podcastService.findById(podcast.id)).thenReturn(podcast) /* When */ rest .get() @@ -161,7 +157,7 @@ class PodcastHandlerTest( fun `with 3 podcasts`() { /* Given */ val podcasts = listOf(podcast1, podcast2, podcast3) - whenever(podcastService.findAll()).thenReturn(podcasts.toFlux()) + whenever(podcastService.findAll()).thenReturn(podcasts) /* When */ rest .get() @@ -212,7 +208,7 @@ class PodcastHandlerTest( @Test fun `with no podcast`() { /* Given */ - whenever(podcastService.findAll()).thenReturn(Flux.empty()) + whenever(podcastService.findAll()).thenReturn(emptyList()) /* When */ rest .get() @@ -251,7 +247,7 @@ class PodcastHandlerTest( @Test fun `with standard information`() { /* Given */ - whenever(podcastService.save(any())).thenReturn(p.toMono()) + whenever(podcastService.save(any())).thenReturn(p) /* When */ rest .post() @@ -321,7 +317,7 @@ class PodcastHandlerTest( tags = listOf(), cover = Cover(UUID.fromString("d6d4033a-d499-4c09-8d3e-d74595ae0993"), URI("http://foo.bar.com/cover.png"), 1200, 600) ) - whenever(podcastService.save(any())).thenReturn(uploadPodcast.toMono()) + whenever(podcastService.save(any())).thenReturn(uploadPodcast) /* When */ rest @@ -374,7 +370,7 @@ class PodcastHandlerTest( tags = listOf(), cover = Cover(UUID.fromString("d6d4033a-d499-4c09-8d3e-d74595ae0993"), URI("http://foo.bar.com/cover.png"), 1200, 600) ) - whenever(podcastService.save(any())).thenReturn(uploadPodcast.toMono()) + whenever(podcastService.save(any())).thenReturn(uploadPodcast) /* When */ rest @@ -442,7 +438,7 @@ class PodcastHandlerTest( @Test fun `a podcast`() { /* Given */ - whenever(podcastService.update(any())).thenReturn(p.toMono()) + whenever(podcastService.update(any())).thenReturn(p) /* When */ rest .put() @@ -502,7 +498,7 @@ class PodcastHandlerTest( @Test fun `a podcast without URL`() { /* Given */ - whenever(podcastService.update(any())).thenReturn(p.toMono()) + whenever(podcastService.update(any())).thenReturn(p) /* When */ rest .put() @@ -563,7 +559,7 @@ class PodcastHandlerTest( fun `a podcast without tags`() { /* Given */ val podcastWithoutTags = p.copy(tags = emptyList()) - whenever(podcastService.update(any())).thenReturn(podcastWithoutTags.toMono()) + whenever(podcastService.update(any())).thenReturn(podcastWithoutTags) /* When */ rest .put() @@ -613,7 +609,7 @@ class PodcastHandlerTest( fun `a podcast by id`() { /* Given */ val id = UUID.randomUUID() - whenever(podcastService.deleteById(id)).thenReturn(Mono.empty()) + doNothing().whenever(podcastService).deleteById(id) /* When */ rest .delete() @@ -633,7 +629,7 @@ class PodcastHandlerTest( fun `by redirecting to local file server if cover exists locally`() { /* Given */ val host = URI.create("https://localhost:8080/") - whenever(podcastService.findById(podcast.id)).thenReturn(podcast.toMono()) + whenever(podcastService.findById(podcast.id)).thenReturn(podcast) whenever(fileService.coverExists(podcast)).thenReturn( Path(podcast.cover.url.toASCIIString().substringAfterLast("/")).toMono() ) @@ -653,7 +649,7 @@ class PodcastHandlerTest( @Test fun `by redirecting to external file if cover does not exist locally`() { /* Given */ - whenever(podcastService.findById(podcast.id)).thenReturn(podcast.toMono()) + whenever(podcastService.findById(podcast.id)).thenReturn(podcast) whenever(fileService.coverExists(podcast)).thenReturn(Mono.empty()) /* When */ @@ -694,7 +690,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndCreationDate(1)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndCreationDate(1)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byCreationDate").build() } @@ -732,7 +728,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndCreationDate(3)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndCreationDate(3)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byCreationDate") @@ -763,7 +759,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByTypeAndCreationDate(3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByTypeAndCreationDate(3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byCreationDate") @@ -795,7 +791,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndPubDate(1)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndPubDate(1)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byPubDate").build() } @@ -833,7 +829,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndPubDate(3)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndPubDate(3)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byPubDate") @@ -864,7 +860,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByTypeAndPubDate(3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByTypeAndPubDate(3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byPubDate") @@ -896,7 +892,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndDownloadDate(1)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndDownloadDate(1)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byDownloadDate").build() } @@ -934,7 +930,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-02-12"), 8), NumberOfItemByDateWrapper(LocalDate.parse("2019-02-28"), 1)) ) - whenever(podcastService.findStatByTypeAndDownloadDate(3)).thenReturn(Flux.just(youtube, rss)) + whenever(podcastService.findStatByTypeAndDownloadDate(3)).thenReturn(listOf(youtube, rss)) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byDownloadDate") @@ -965,7 +961,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByTypeAndDownloadDate(3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByTypeAndDownloadDate(3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/stats/byDownloadDate") @@ -999,7 +995,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 1)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 1)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byPubDate").build() } @@ -1022,7 +1018,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 3)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 3)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byPubDate") @@ -1043,7 +1039,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByPodcastIdAndPubDate(podcast.id, 3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byPubDate") @@ -1070,7 +1066,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 1)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 1)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byDownloadDate").build() } @@ -1093,7 +1089,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 3)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 3)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byDownloadDate") @@ -1114,7 +1110,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByPodcastIdAndDownloadDate(podcast.id, 3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byDownloadDate") @@ -1141,7 +1137,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 1)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 1)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byCreationDate").build() } @@ -1164,7 +1160,7 @@ class PodcastHandlerTest( NumberOfItemByDateWrapper(LocalDate.parse("2019-01-12"), 2), NumberOfItemByDateWrapper(LocalDate.parse("2019-01-28"), 6) ) - whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 3)).thenReturn(r.toFlux()) + whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 3)).thenReturn(r) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byCreationDate") @@ -1185,7 +1181,7 @@ class PodcastHandlerTest( @Test fun `with no data`() { /* Given */ - whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 3)).thenReturn(Flux.empty()) + whenever(podcastService.findStatByPodcastIdAndCreationDate(podcast.id, 3)).thenReturn(emptyList()) /* When */ rest.get() .uri { it.path("/api/v1/podcasts/${podcast.id}/stats/byCreationDate") diff --git a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastServiceTest.kt b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastServiceTest.kt index 261528414..f78fbfb07 100644 --- a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastServiceTest.kt +++ b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastServiceTest.kt @@ -20,7 +20,6 @@ import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.kotlin.core.publisher.toFlux import reactor.kotlin.core.publisher.toMono -import reactor.test.StepVerifier import java.net.URI import java.time.LocalDate import java.time.OffsetDateTime @@ -63,12 +62,12 @@ class PodcastServiceTest( fun `should find by id`() { /* Given */ whenever(repository.findById(podcast.id)).thenReturn(podcast.toMono()) + /* When */ - StepVerifier.create(service.findById(podcast.id)) - /* Then */ - .expectSubscription() - .expectNext(podcast) - .verifyComplete() + val p = service.findById(podcast.id) + + /* Then */ + assertThat(p).isSameAs(podcast) } @Nested @@ -132,23 +131,24 @@ class PodcastServiceTest( /* Given */ val podcasts = listOf(podcast1, podcast2, podcast3) whenever(repository.findAll()).thenReturn(podcasts.toFlux()) + /* When */ - StepVerifier.create(service.findAll()) - /* Then */ - .expectSubscription() - .expectNext(podcast1, podcast2, podcast3) - .verifyComplete() + val p = service.findAll() + + /* Then */ + assertThat(p).containsExactly(podcast1, podcast2, podcast3) } @Test fun `with 0 podcast`() { /* Given */ whenever(repository.findAll()).thenReturn(Flux.empty()) + /* When */ - StepVerifier.create(service.findAll()) - /* Then */ - .expectSubscription() - .verifyComplete() + val p = service.findAll() + + /* Then */ + assertThat(p).isEmpty() } } @@ -171,36 +171,36 @@ class PodcastServiceTest( fun `by pubDate`() { /* Given */ whenever(repository.findStatByPodcastIdAndPubDate(podcast.id, 3)).thenReturn(r.toFlux()) + /* When */ - StepVerifier.create(service.findStatByPodcastIdAndPubDate(podcast.id, 3)) - /* Then */ - .expectSubscription() - .expectNextSequence(r) - .verifyComplete() + val stats = service.findStatByPodcastIdAndPubDate(podcast.id, 3) + + /* Then */ + assertThat(stats).isEqualTo(r) } @Test fun `by downloadDate`() { /* Given */ whenever(repository.findStatByPodcastIdAndDownloadDate(podcast.id, 3)).thenReturn(r.toFlux()) + /* When */ - StepVerifier.create(service.findStatByPodcastIdAndDownloadDate(podcast.id, 3)) - /* Then */ - .expectSubscription() - .expectNextSequence(r) - .verifyComplete() + val stats = service.findStatByPodcastIdAndDownloadDate(podcast.id, 3) + + /* Then */ + assertThat(stats).isEqualTo(r) } @Test fun `by creationDate`() { /* Given */ whenever(repository.findStatByPodcastIdAndCreationDate(podcast.id, 3)).thenReturn(r.toFlux()) + /* When */ - StepVerifier.create(service.findStatByPodcastIdAndCreationDate(podcast.id, 3)) - /* Then */ - .expectSubscription() - .expectNextSequence(r) - .verifyComplete() + val stats = service.findStatByPodcastIdAndCreationDate(podcast.id, 3) + + /* Then */ + assertThat(stats).isEqualTo(r) } } @@ -221,39 +221,45 @@ class PodcastServiceTest( fun `by pubDate`() { /* Given */ whenever(repository.findStatByTypeAndPubDate(3)).thenReturn(Flux.just(youtube, rss)) + /* When */ - StepVerifier.create(service.findStatByTypeAndPubDate(3)) - /* Then */ - .expectSubscription() - .expectNext(youtube) - .expectNext(rss) - .verifyComplete() + val stats = service.findStatByTypeAndPubDate(3) + + /* Then */ + assertThat(stats).containsExactly( + youtube, + rss, + ) } @Test fun `by downloadDate`() { /* Given */ whenever(repository.findStatByTypeAndDownloadDate(3)).thenReturn(Flux.just(youtube, rss)) + /* When */ - StepVerifier.create(service.findStatByTypeAndDownloadDate(3)) - /* Then */ - .expectSubscription() - .expectNext(youtube) - .expectNext(rss) - .verifyComplete() + val stats = service.findStatByTypeAndDownloadDate(3) + + /* Then */ + assertThat(stats).containsExactly( + youtube, + rss, + ) } @Test fun `by creationDate`() { /* Given */ whenever(repository.findStatByTypeAndCreationDate(3)).thenReturn(Flux.just(youtube, rss)) + /* When */ - StepVerifier.create(service.findStatByTypeAndCreationDate(3)) - /* Then */ - .expectSubscription() - .expectNext(youtube) - .expectNext(rss) - .verifyComplete() + val stats = service.findStatByTypeAndCreationDate(3) + + /* Then */ + assertThat(stats).containsExactly( + youtube, + rss, + ) } } @@ -298,18 +304,17 @@ class PodcastServiceTest( fun `with no tags and just a cover`() { /* Given */ val tags = emptySet() - val p = podcastForCreation.copy(tags = tags) - val savedCover = p.cover.toCover() - whenever(coverRepository.save(p.cover)).thenReturn(savedCover.toMono()) - whenever(repository.save(eq(p.title), eq(p.url!!.toASCIIString()), eq(p.hasToBeDeleted), eq(p.type), argThat { isEmpty() }, eq(savedCover))) + val pBeforeUpdate = podcastForCreation.copy(tags = tags) + val savedCover = pBeforeUpdate.cover.toCover() + whenever(coverRepository.save(pBeforeUpdate.cover)).thenReturn(savedCover.toMono()) + whenever(repository.save(eq(pBeforeUpdate.title), eq(pBeforeUpdate.url!!.toASCIIString()), eq(pBeforeUpdate.hasToBeDeleted), eq(pBeforeUpdate.type), argThat { isEmpty() }, eq(savedCover))) .thenReturn(podcast.toMono()) /* When */ - StepVerifier.create(service.save(p)) - /* Then */ - .expectSubscription() - .assertNext { assertThat(it).isSameAs(podcast) } - .verifyComplete() + val savedPodcast = service.save(pBeforeUpdate) + + /* Then */ + assertThat(savedPodcast).isSameAs(podcast) } @@ -335,11 +340,10 @@ class PodcastServiceTest( .thenReturn(podcast.toMono()) /* When */ - StepVerifier.create(service.save(p)) - /* Then */ - .expectSubscription() - .assertNext { assertThat(it).isSameAs(podcast) } - .verifyComplete() + val savedPodcast = service.save(p) + + /* Then */ + assertThat(savedPodcast).isSameAs(podcast) } @Test @@ -367,11 +371,10 @@ class PodcastServiceTest( .thenReturn(podcast.toMono()) /* When */ - StepVerifier.create(service.save(p)) - /* Then */ - .expectSubscription() - .assertNext { assertThat(it).isSameAs(podcast) } - .verifyComplete() + val savedPodcast = service.save(p) + + /* Then */ + assertThat(savedPodcast).isSameAs(podcast) } } @@ -412,17 +415,16 @@ class PodcastServiceTest( tags = argThat { isEmpty() }, cover = argThat { id == UUID.fromString("1e275238-4cbe-4abb-bbca-95a0e4ebbeea") && - url == java.net.URI("https://external.domain.tld/cover.png") && + url == URI("https://external.domain.tld/cover.png") && height == 200 && width == 200 } )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(podcastForUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(podcastForUpdate) + + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(tagRepository, never()).save(any()) verify(coverRepository, never()).save(any()) @@ -456,18 +458,16 @@ class PodcastServiceTest( tags = argThat { contains(newTagsInDb) && size == 1 }, cover = argThat { id == UUID.fromString("1e275238-4cbe-4abb-bbca-95a0e4ebbeea") && - url == java.net.URI("https://external.domain.tld/cover.png") && + url == URI("https://external.domain.tld/cover.png") && height == 200 && width == 200 } )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(tagRepository, times(1)).save(any()) } @@ -489,12 +489,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(tagRepository, times(1)).save(any()) } @@ -516,12 +514,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(tagRepository, never()).save(any()) } @@ -543,7 +539,7 @@ class PodcastServiceTest( val p = podcast.copy(tags = allTagsInDb) whenever(tagRepository.save(argThat { this in listOfNewTags.map { it.name } })) - .then { allTagsInDb.first() { t -> t.name == it.getArgument(0) } } + .then { allTagsInDb.first { t -> t.name == it.getArgument(0) } } whenever(repository.findById(p.id)).thenReturn(podcast.toMono()) whenever(repository.update( id = eq(p.id), @@ -555,12 +551,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(tagRepository, times(listOfNewTags.size)).save(any()) } @@ -600,12 +594,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(coverRepository, never()).save(any()) } @@ -632,12 +624,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(coverRepository, never()).save(any()) } @@ -663,12 +653,10 @@ class PodcastServiceTest( )).thenReturn(p.toMono()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(p) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(p) verify(coverRepository, times(1)).save(any()) verify(fileService, times(1)).downloadPodcastCover(any()) } @@ -709,12 +697,10 @@ class PodcastServiceTest( whenever(fileService.movePodcast(moveOperation)).thenReturn(Mono.empty()) /* When */ - StepVerifier.create(service.update(pToUpdate)) - /* Then */ - .expectSubscription() - .expectNext(pAfterUpdate) - .verifyComplete() + val podcastAfterUpdate = service.update(pToUpdate) + /* Then */ + assertThat(podcastAfterUpdate).isEqualTo(pAfterUpdate) verify(tagRepository, never()).save(any()) verify(coverRepository, never()).save(any()) verify(fileService, never()).downloadPodcastCover(any()) @@ -740,11 +726,9 @@ class PodcastServiceTest( whenever(fileService.deletePodcast(information)).thenReturn(Mono.empty()) /* When */ - StepVerifier.create(service.deleteById(id)) - /* Then */ - .expectSubscription() - .verifyComplete() + service.deleteById(id) + /* Then */ verify(fileService, times(1)).deletePodcast(information) } @@ -755,11 +739,9 @@ class PodcastServiceTest( whenever(repository.deleteById(id)).thenReturn(Mono.empty()) /* When */ - StepVerifier.create(service.deleteById(id)) - /* Then */ - .expectSubscription() - .verifyComplete() + service.deleteById(id) + /* Then */ verify(fileService, never()).deletePodcast(any()) } } diff --git a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandlerTest.kt b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandlerTest.kt index e1d161f2a..d5cc5e52a 100644 --- a/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandlerTest.kt +++ b/backend/src/test/kotlin/com/github/davinkevin/podcastserver/podcast/PodcastXmlHandlerTest.kt @@ -21,9 +21,6 @@ import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.context.annotation.Import import org.springframework.test.web.reactive.server.WebTestClient -import reactor.core.publisher.Flux -import reactor.kotlin.core.publisher.toFlux -import reactor.kotlin.core.publisher.toMono import java.net.URI import java.time.OffsetDateTime import java.time.ZoneOffset @@ -116,7 +113,7 @@ class PodcastXmlHandlerTest( @Test fun `with no podcast`() { /* Given */ - whenever(podcastService.findAll()).thenReturn(Flux.empty()) + whenever(podcastService.findAll()).thenReturn(emptyList()) /* When */ rest @@ -140,7 +137,7 @@ class PodcastXmlHandlerTest( @Test fun `with one podcast`() { /* Given */ - whenever(podcastService.findAll()).thenReturn(listOf(podcast1).toFlux()) + whenever(podcastService.findAll()).thenReturn(listOf(podcast1)) /* When */ rest @@ -166,7 +163,7 @@ class PodcastXmlHandlerTest( @Test fun `with 3 podcasts`() { /* Given */ - whenever(podcastService.findAll()).thenReturn(listOf(podcast1, podcast2, podcast3).toFlux()) + whenever(podcastService.findAll()).thenReturn(listOf(podcast1, podcast2, podcast3)) /* When */ rest @@ -197,7 +194,7 @@ class PodcastXmlHandlerTest( @BeforeEach fun beforeEach() { - whenever(podcastService.findAll()).thenReturn(listOf(podcast1).toFlux()) + whenever(podcastService.findAll()).thenReturn(listOf(podcast1)) } @Test @@ -420,7 +417,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-50-items.xml") @@ -445,7 +442,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-200-items.xml") @@ -480,7 +477,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = xmlWith(numberOfItem) @@ -505,7 +502,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) - whenever(podcastService.findById(podcastId)).thenReturn(podcast.toMono()) + whenever(podcastService.findById(podcastId)).thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-lots-of-parameters.xml") @@ -530,7 +527,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-x-forwarded-port.xml") @@ -556,7 +553,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-port-not-defined-and-http.xml") @@ -581,7 +578,7 @@ class PodcastXmlHandlerTest( whenever(itemService.search(anyOrNull(), eq(listOf()), eq(listOf()), eq(page), eq(podcastId))) .thenReturn(result) whenever(podcastService.findById(podcastId)) - .thenReturn(podcast.toMono()) + .thenReturn(podcast) val xml = fileAsString("/xml/podcast-with-port-not-defined-and-https.xml")