/** * Sone - SoneMentionDetectorTest.kt - Copyright © 2019–2020 David ‘Bombe’ Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package net.pterodactylus.sone.text import com.google.common.eventbus.* import net.pterodactylus.sone.core.event.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.database.* import net.pterodactylus.sone.test.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import kotlin.test.* /** * Unit test for [SoneMentionDetector]. */ @Suppress("UnstableApiUsage") class SoneMentionDetectorTest { private val caughtExceptions = mutableListOf() private val eventBus = EventBus { exception, _ -> caughtExceptions += exception } private val soneProvider = TestSoneProvider() private val postProvider = TestPostProvider() private val soneTextParser = SoneTextParser(soneProvider, postProvider) private val capturedFoundEvents = mutableListOf() private val capturedRemovedEvents = mutableListOf() private val postReplyProvider = TestPostReplyProvider() init { eventBus.register(SoneMentionDetector(eventBus, soneTextParser, postReplyProvider)) eventBus.register(object : Any() { @Subscribe fun captureFoundEvent(mentionOfLocalSoneFoundEvent: MentionOfLocalSoneFoundEvent) { capturedFoundEvents += mentionOfLocalSoneFoundEvent } @Subscribe fun captureRemovedEvent(event: MentionOfLocalSoneRemovedEvent) { capturedRemovedEvents += event } }) } @Test fun `detector does not emit event on post that does not contain any sones`() { val post = createPost() eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector does not emit event on post that does contain two remote sones`() { val post = createPost("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.") eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector emits event on post that contains links to a remote and a local sone`() { val post = createPost("text mentions sone://${localSone1.id} and sone://${remoteSone2.id}.") eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector emits one event on post that contains two links to the same local sone`() { val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone1.id}.") eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector emits one event on post that contains links to two local sones`() { val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.") eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector does not emit event for post by local sone`() { val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", localSone1) eventBus.post(NewPostFoundEvent(post)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector does not emit event for reply that contains no sones`() { val reply = emptyPostReply() eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector does not emit event for reply that contains two links to remote sones`() { val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.") eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector emits event on reply that contains links to a remote and a local sone`() { val post = createPost() val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${localSone1.id}.", post) eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector emits one event on reply that contains two links to the same local sone`() { val post = createPost() val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone1.id}.", post) eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector emits one event on reply that contains two links to local sones`() { val post = createPost() val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", post) eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post))) } @Test fun `detector does not emit event for reply by local sone`() { val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", sone = localSone1) eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector does not emit event for reply without post`() { val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", post = null) eventBus.post(NewPostReplyFoundEvent(reply)) assertThat(caughtExceptions, emptyIterable()) assertThat(capturedFoundEvents, emptyIterable()) } @Test fun `detector does not emit removed event when a post without mention is removed`() { val post = createPost() eventBus.post(PostRemovedEvent(post)) assertThat(capturedRemovedEvents, emptyIterable()) } @Test fun `detector does emit removed event when post with mention is removed`() { val post = createPost("sone://${localSone1.id}") eventBus.post(NewPostFoundEvent(post)) eventBus.post(PostRemovedEvent(post)) assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post))) } @Test fun `detector does not emit removed event when a post without mention is marked as known`() { val post = createPost() eventBus.post(MarkPostKnownEvent(post)) assertThat(capturedRemovedEvents, emptyIterable()) } @Test fun `detector does emit removed event when post with mention is marked as known`() { val post = createPost("sone://${localSone1.id}") eventBus.post(NewPostFoundEvent(post)) eventBus.post(MarkPostKnownEvent(post)) assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post))) } @Test fun `detector does emit removed event when reply with mention is removed and no more mentions in that post exist`() { val post = createPost() val reply = emptyPostReply("sone://${localSone1.id}", post) postReplyProvider.postReplies[post.id] = listOf(reply) eventBus.post(NewPostReplyFoundEvent(reply)) eventBus.post(PostReplyRemovedEvent(reply)) assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post))) } @Test fun `detector does not emit removed event when reply with mention is removed and post mentions local sone`() { val post = createPost("sone://${localSone1.id}") val reply = emptyPostReply("sone://${localSone1.id}", post) eventBus.post(NewPostReplyFoundEvent(reply)) eventBus.post(PostReplyRemovedEvent(reply)) assertThat(capturedRemovedEvents, emptyIterable()) } @Test fun `detector does emit removed event when reply with mention is removed and post mentions local sone but is known`() { val post = createPost("sone://${localSone1.id}", known = true) val reply = emptyPostReply("sone://${localSone1.id}", post) eventBus.post(NewPostReplyFoundEvent(reply)) eventBus.post(PostReplyRemovedEvent(reply)) assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post))) } @Test fun `detector does not emit removed event when reply with mention is removed and post has other replies with mentions`() { val post = createPost() val reply1 = emptyPostReply("sone://${localSone1.id}", post) val reply2 = emptyPostReply("sone://${localSone1.id}", post) postReplyProvider.postReplies[post.id] = listOf(reply1, reply2) eventBus.post(NewPostReplyFoundEvent(reply1)) eventBus.post(PostReplyRemovedEvent(reply1)) assertThat(capturedRemovedEvents, emptyIterable()) } @Test fun `detector does emit removed event when reply with mention is removed and post has other replies with mentions which are known`() { val post = createPost() val reply1 = emptyPostReply("sone://${localSone1.id}", post) val reply2 = emptyPostReply("sone://${localSone1.id}", post, known = true) postReplyProvider.postReplies[post.id] = listOf(reply1, reply2) eventBus.post(NewPostReplyFoundEvent(reply1)) eventBus.post(PostReplyRemovedEvent(reply1)) assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post))) } } private class TestSoneProvider : SoneProvider { override val sones: Collection get() = remoteSones + localSones override val localSones: Collection get() = setOf(localSone1, localSone2) override val remoteSones: Collection get() = setOf(remoteSone1, remoteSone2) override val soneLoader: (String) -> Sone? get() = this::getSone override fun getSone(soneId: String): Sone? = localSones.firstOrNull { it.id == soneId } ?: remoteSones.firstOrNull { it.id == soneId } } private class TestPostProvider : PostProvider { override fun getPost(postId: String): Post? = null override fun getPosts(soneId: String): Collection = emptyList() override fun getDirectedPosts(recipientId: String): Collection = emptyList() } private class TestPostReplyProvider : PostReplyProvider { val replies = mutableMapOf() val postReplies = mutableMapOf>() override fun getPostReply(id: String) = replies[id] override fun getReplies(postId: String) = postReplies[postId] ?: emptyList() }