2 * Sone - SoneMentionDetectorTest.kt - Copyright © 2019–2020 David ‘Bombe’ Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sone.text
20 import com.google.common.eventbus.*
21 import net.pterodactylus.sone.core.event.*
22 import net.pterodactylus.sone.data.*
23 import net.pterodactylus.sone.database.*
24 import net.pterodactylus.sone.test.*
25 import org.hamcrest.MatcherAssert.*
26 import org.hamcrest.Matchers.*
30 * Unit test for [SoneMentionDetector].
32 @Suppress("UnstableApiUsage")
33 class SoneMentionDetectorTest {
35 private val caughtExceptions = mutableListOf<Throwable>()
36 private val eventBus = EventBus { exception, _ -> caughtExceptions += exception }
37 private val soneProvider = TestSoneProvider()
38 private val postProvider = TestPostProvider()
39 private val soneTextParser = SoneTextParser(soneProvider, postProvider)
40 private val capturedFoundEvents = mutableListOf<MentionOfLocalSoneFoundEvent>()
41 private val capturedRemovedEvents = mutableListOf<MentionOfLocalSoneRemovedEvent>()
42 private val postReplyProvider = TestPostReplyProvider()
45 eventBus.register(SoneMentionDetector(eventBus, soneTextParser, postReplyProvider))
46 eventBus.register(object : Any() {
48 fun captureFoundEvent(mentionOfLocalSoneFoundEvent: MentionOfLocalSoneFoundEvent) {
49 capturedFoundEvents += mentionOfLocalSoneFoundEvent
53 fun captureRemovedEvent(event: MentionOfLocalSoneRemovedEvent) {
54 capturedRemovedEvents += event
60 fun `detector does not emit event on post that does not contain any sones`() {
61 val post = createPost()
62 eventBus.post(NewPostFoundEvent(post))
63 assertThat(capturedFoundEvents, emptyIterable())
67 fun `detector does not emit event on post that does contain two remote sones`() {
68 val post = createPost("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.")
69 eventBus.post(NewPostFoundEvent(post))
70 assertThat(capturedFoundEvents, emptyIterable())
74 fun `detector emits event on post that contains links to a remote and a local sone`() {
75 val post = createPost("text mentions sone://${localSone1.id} and sone://${remoteSone2.id}.")
76 eventBus.post(NewPostFoundEvent(post))
77 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
81 fun `detector emits one event on post that contains two links to the same local sone`() {
82 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone1.id}.")
83 eventBus.post(NewPostFoundEvent(post))
84 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
88 fun `detector emits one event on post that contains links to two local sones`() {
89 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.")
90 eventBus.post(NewPostFoundEvent(post))
91 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
95 fun `detector does not emit event for post by local sone`() {
96 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", localSone1)
97 eventBus.post(NewPostFoundEvent(post))
98 assertThat(capturedFoundEvents, emptyIterable())
102 fun `detector does not emit event for reply that contains no sones`() {
103 val reply = emptyPostReply()
104 eventBus.post(NewPostReplyFoundEvent(reply))
105 assertThat(capturedFoundEvents, emptyIterable())
109 fun `detector does not emit event for reply that contains two links to remote sones`() {
110 val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.")
111 eventBus.post(NewPostReplyFoundEvent(reply))
112 assertThat(capturedFoundEvents, emptyIterable())
116 fun `detector emits event on reply that contains links to a remote and a local sone`() {
117 val post = createPost()
118 val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${localSone1.id}.", post)
119 eventBus.post(NewPostReplyFoundEvent(reply))
120 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
124 fun `detector emits one event on reply that contains two links to the same local sone`() {
125 val post = createPost()
126 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone1.id}.", post)
127 eventBus.post(NewPostReplyFoundEvent(reply))
128 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
132 fun `detector emits one event on reply that contains two links to local sones`() {
133 val post = createPost()
134 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", post)
135 eventBus.post(NewPostReplyFoundEvent(reply))
136 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
140 fun `detector does not emit event for reply by local sone`() {
141 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", sone = localSone1)
142 eventBus.post(NewPostReplyFoundEvent(reply))
143 assertThat(capturedFoundEvents, emptyIterable())
147 fun `detector does not emit event for reply without post`() {
148 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", post = null)
149 eventBus.post(NewPostReplyFoundEvent(reply))
150 assertThat(caughtExceptions, emptyIterable())
151 assertThat(capturedFoundEvents, emptyIterable())
155 fun `detector does not emit removed event when a post without mention is removed`() {
156 val post = createPost()
157 eventBus.post(PostRemovedEvent(post))
158 assertThat(capturedRemovedEvents, emptyIterable())
162 fun `detector does emit removed event when post with mention is removed`() {
163 val post = createPost("sone://${localSone1.id}")
164 eventBus.post(NewPostFoundEvent(post))
165 eventBus.post(PostRemovedEvent(post))
166 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
170 fun `detector does not emit removed event when a post without mention is marked as known`() {
171 val post = createPost()
172 eventBus.post(MarkPostKnownEvent(post))
173 assertThat(capturedRemovedEvents, emptyIterable())
177 fun `detector does emit removed event when post with mention is marked as known`() {
178 val post = createPost("sone://${localSone1.id}")
179 eventBus.post(NewPostFoundEvent(post))
180 eventBus.post(MarkPostKnownEvent(post))
181 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
185 fun `detector does emit removed event when reply with mention is removed and no more mentions in that post exist`() {
186 val post = createPost()
187 val reply = emptyPostReply("sone://${localSone1.id}", post)
188 postReplyProvider.postReplies[post.id] = listOf(reply)
189 eventBus.post(NewPostReplyFoundEvent(reply))
190 eventBus.post(PostReplyRemovedEvent(reply))
191 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
195 fun `detector does not emit removed event when reply with mention is removed and post mentions local sone`() {
196 val post = createPost("sone://${localSone1.id}")
197 val reply = emptyPostReply("sone://${localSone1.id}", post)
198 eventBus.post(NewPostReplyFoundEvent(reply))
199 eventBus.post(PostReplyRemovedEvent(reply))
200 assertThat(capturedRemovedEvents, emptyIterable())
204 fun `detector does emit removed event when reply with mention is removed and post mentions local sone but is known`() {
205 val post = createPost("sone://${localSone1.id}", known = true)
206 val reply = emptyPostReply("sone://${localSone1.id}", post)
207 eventBus.post(NewPostReplyFoundEvent(reply))
208 eventBus.post(PostReplyRemovedEvent(reply))
209 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
213 fun `detector does not emit removed event when reply with mention is removed and post has other replies with mentions`() {
214 val post = createPost()
215 val reply1 = emptyPostReply("sone://${localSone1.id}", post)
216 val reply2 = emptyPostReply("sone://${localSone1.id}", post)
217 postReplyProvider.postReplies[post.id] = listOf(reply1, reply2)
218 eventBus.post(NewPostReplyFoundEvent(reply1))
219 eventBus.post(PostReplyRemovedEvent(reply1))
220 assertThat(capturedRemovedEvents, emptyIterable())
224 fun `detector does emit removed event when reply with mention is removed and post has other replies with mentions which are known`() {
225 val post = createPost()
226 val reply1 = emptyPostReply("sone://${localSone1.id}", post)
227 val reply2 = emptyPostReply("sone://${localSone1.id}", post, known = true)
228 postReplyProvider.postReplies[post.id] = listOf(reply1, reply2)
229 eventBus.post(NewPostReplyFoundEvent(reply1))
230 eventBus.post(PostReplyRemovedEvent(reply1))
231 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
236 private class TestSoneProvider : SoneProvider {
238 override val sones: Collection<Sone> get() = remoteSones + localSones
239 override val localSones: Collection<Sone> get() = setOf(localSone1, localSone2)
240 override val remoteSones: Collection<Sone> get() = setOf(remoteSone1, remoteSone2)
241 override val soneLoader: (String) -> Sone? get() = this::getSone
242 override fun getSone(soneId: String): Sone? =
243 localSones.firstOrNull { it.id == soneId } ?: remoteSones.firstOrNull { it.id == soneId }
247 private class TestPostProvider : PostProvider {
249 override fun getPost(postId: String): Post? = null
250 override fun getPosts(soneId: String): Collection<Post> = emptyList()
251 override fun getDirectedPosts(recipientId: String): Collection<Post> = emptyList()
255 private class TestPostReplyProvider : PostReplyProvider {
257 val replies = mutableMapOf<String, PostReply>()
258 val postReplies = mutableMapOf<String, List<PostReply>>()
260 override fun getPostReply(id: String) = replies[id]
261 override fun getReplies(postId: String) = postReplies[postId] ?: emptyList()