6ae437bd44f86bf3926651b72b7d68417b10a555
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / text / SoneMentionDetectorTest.kt
1 /**
2  * Sone - SoneMentionDetectorTest.kt - Copyright © 2019 David ‘Bombe’ Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.sone.text
19
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.*
27 import kotlin.test.*
28
29 /**
30  * Unit test for [SoneMentionDetector].
31  */
32 @Suppress("UnstableApiUsage")
33 class SoneMentionDetectorTest {
34
35         private val eventBus = EventBus()
36         private val soneProvider = TestSoneProvider()
37         private val postProvider = TestPostProvider()
38         private val soneTextParser = SoneTextParser(soneProvider, postProvider)
39         private val capturedFoundEvents = mutableListOf<MentionOfLocalSoneFoundEvent>()
40         private val capturedRemovedEvents = mutableListOf<MentionOfLocalSoneRemovedEvent>()
41         private val postReplyProvider = TestPostReplyProvider()
42
43         init {
44                 eventBus.register(SoneMentionDetector(eventBus, soneTextParser, postReplyProvider))
45                 eventBus.register(object : Any() {
46                         @Subscribe
47                         fun captureFoundEvent(mentionOfLocalSoneFoundEvent: MentionOfLocalSoneFoundEvent) {
48                                 capturedFoundEvents += mentionOfLocalSoneFoundEvent
49                         }
50
51                         @Subscribe
52                         fun captureRemovedEvent(event: MentionOfLocalSoneRemovedEvent) {
53                                 capturedRemovedEvents += event
54                         }
55                 })
56         }
57
58         @Test
59         fun `detector does not emit event on post that does not contain any sones`() {
60                 val post = createPost()
61                 eventBus.post(NewPostFoundEvent(post))
62                 assertThat(capturedFoundEvents, emptyIterable())
63         }
64
65         @Test
66         fun `detector does not emit event on post that does contain two remote sones`() {
67                 val post = createPost("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.")
68                 eventBus.post(NewPostFoundEvent(post))
69                 assertThat(capturedFoundEvents, emptyIterable())
70         }
71
72         @Test
73         fun `detector emits event on post that contains links to a remote and a local sone`() {
74                 val post = createPost("text mentions sone://${localSone1.id} and sone://${remoteSone2.id}.")
75                 eventBus.post(NewPostFoundEvent(post))
76                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
77         }
78
79         @Test
80         fun `detector emits one event on post that contains two links to the same local sone`() {
81                 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone1.id}.")
82                 eventBus.post(NewPostFoundEvent(post))
83                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
84         }
85
86         @Test
87         fun `detector emits one event on post that contains links to two local sones`() {
88                 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.")
89                 eventBus.post(NewPostFoundEvent(post))
90                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
91         }
92
93         @Test
94         fun `detector does not emit event for post by local sone`() {
95                 val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", localSone1)
96                 eventBus.post(NewPostFoundEvent(post))
97                 assertThat(capturedFoundEvents, emptyIterable())
98         }
99
100         @Test
101         fun `detector does not emit event for reply that contains no sones`() {
102                 val reply = emptyPostReply()
103                 eventBus.post(NewPostReplyFoundEvent(reply))
104                 assertThat(capturedFoundEvents, emptyIterable())
105         }
106
107         @Test
108         fun `detector does not emit event for reply that contains two links to remote sones`() {
109                 val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.")
110                 eventBus.post(NewPostReplyFoundEvent(reply))
111                 assertThat(capturedFoundEvents, emptyIterable())
112         }
113
114         @Test
115         fun `detector emits event on reply that contains links to a remote and a local sone`() {
116                 val post = createPost()
117                 val reply = emptyPostReply("text mentions sone://${remoteSone1.id} and sone://${localSone1.id}.", post)
118                 eventBus.post(NewPostReplyFoundEvent(reply))
119                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
120         }
121
122         @Test
123         fun `detector emits one event on reply that contains two links to the same local sone`() {
124                 val post = createPost()
125                 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone1.id}.", post)
126                 eventBus.post(NewPostReplyFoundEvent(reply))
127                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
128         }
129
130         @Test
131         fun `detector emits one event on reply that contains two links to local sones`() {
132                 val post = createPost()
133                 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", post)
134                 eventBus.post(NewPostReplyFoundEvent(reply))
135                 assertThat(capturedFoundEvents, contains(MentionOfLocalSoneFoundEvent(post)))
136         }
137
138         @Test
139         fun `detector does not emit event for reply by local sone`() {
140                 val reply = emptyPostReply("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", sone = localSone1)
141                 eventBus.post(NewPostReplyFoundEvent(reply))
142                 assertThat(capturedFoundEvents, emptyIterable())
143         }
144
145         @Test
146         fun `detector does not emit removed event when a post without mention is removed`() {
147                 val post = createPost()
148                 eventBus.post(PostRemovedEvent(post))
149                 assertThat(capturedRemovedEvents, emptyIterable())
150         }
151
152         @Test
153         fun `detector does emit removed event when post with mention is removed`() {
154                 val post = createPost("sone://${localSone1.id}")
155                 eventBus.post(NewPostFoundEvent(post))
156                 eventBus.post(PostRemovedEvent(post))
157                 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
158         }
159
160         @Test
161         fun `detector does not emit removed event when a post without mention is marked as known`() {
162                 val post = createPost()
163                 eventBus.post(MarkPostKnownEvent(post))
164                 assertThat(capturedRemovedEvents, emptyIterable())
165         }
166
167         @Test
168         fun `detector does emit removed event when post with mention is marked as known`() {
169                 val post = createPost("sone://${localSone1.id}")
170                 eventBus.post(NewPostFoundEvent(post))
171                 eventBus.post(MarkPostKnownEvent(post))
172                 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
173         }
174
175         @Test
176         fun `detector does emit removed event when reply with mention is removed and no more mentions in that post exist`() {
177                 val post = createPost()
178                 val reply = emptyPostReply("sone://${localSone1.id}", post)
179                 postReplyProvider.postReplies[post.id] = listOf(reply)
180                 eventBus.post(NewPostReplyFoundEvent(reply))
181                 eventBus.post(PostReplyRemovedEvent(reply))
182                 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
183         }
184
185         @Test
186         fun `detector does not emit removed event when reply with mention is removed and post mentions local sone`() {
187                 val post = createPost("sone://${localSone1.id}")
188                 val reply = emptyPostReply("sone://${localSone1.id}", post)
189                 eventBus.post(NewPostReplyFoundEvent(reply))
190                 eventBus.post(PostReplyRemovedEvent(reply))
191                 assertThat(capturedRemovedEvents, emptyIterable())
192         }
193
194         @Test
195         fun `detector does emit removed event when reply with mention is removed and post mentions local sone but is known`() {
196                 val post = createPost("sone://${localSone1.id}", known = true)
197                 val reply = emptyPostReply("sone://${localSone1.id}", post)
198                 eventBus.post(NewPostReplyFoundEvent(reply))
199                 eventBus.post(PostReplyRemovedEvent(reply))
200                 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
201         }
202
203         @Test
204         fun `detector does not emit removed event when reply with mention is removed and post has other replies with mentions`() {
205                 val post = createPost()
206                 val reply1 = emptyPostReply("sone://${localSone1.id}", post)
207                 val reply2 = emptyPostReply("sone://${localSone1.id}", post)
208                 postReplyProvider.postReplies[post.id] = listOf(reply1, reply2)
209                 eventBus.post(NewPostReplyFoundEvent(reply1))
210                 eventBus.post(PostReplyRemovedEvent(reply1))
211                 assertThat(capturedRemovedEvents, emptyIterable())
212         }
213
214         @Test
215         fun `detector does emit removed event when reply with mention is removed and post has other replies with mentions which are known`() {
216                 val post = createPost()
217                 val reply1 = emptyPostReply("sone://${localSone1.id}", post)
218                 val reply2 = emptyPostReply("sone://${localSone1.id}", post, known = true)
219                 postReplyProvider.postReplies[post.id] = listOf(reply1, reply2)
220                 eventBus.post(NewPostReplyFoundEvent(reply1))
221                 eventBus.post(PostReplyRemovedEvent(reply1))
222                 assertThat(capturedRemovedEvents, contains(MentionOfLocalSoneRemovedEvent(post)))
223         }
224
225 }
226
227 private class TestSoneProvider : SoneProvider {
228
229         override val sones: Collection<Sone> get() = remoteSones + localSones
230         override val localSones: Collection<Sone> get() = setOf(localSone1, localSone2)
231         override val remoteSones: Collection<Sone> get() = setOf(remoteSone1, remoteSone2)
232         override val soneLoader: (String) -> Sone? get() = this::getSone
233         override fun getSone(soneId: String): Sone? =
234                         localSones.firstOrNull { it.id == soneId } ?: remoteSones.firstOrNull { it.id == soneId }
235
236 }
237
238 private class TestPostProvider : PostProvider {
239
240         override fun getPost(postId: String): Post? = null
241         override fun getPosts(soneId: String): Collection<Post> = emptyList()
242         override fun getDirectedPosts(recipientId: String): Collection<Post> = emptyList()
243
244 }
245
246 private class TestPostReplyProvider : PostReplyProvider {
247
248         val replies = mutableMapOf<String, PostReply>()
249         val postReplies = mutableMapOf<String, List<PostReply>>()
250
251         override fun getPostReply(id: String) = replies[id]
252         override fun getReplies(postId: String) = postReplies[postId] ?: emptyList()
253
254 }