1 package net.pterodactylus.sone.web.pages
3 import com.google.common.base.Optional.absent
4 import net.pterodactylus.sone.data.Album
5 import net.pterodactylus.sone.data.Image
6 import net.pterodactylus.sone.data.Post
7 import net.pterodactylus.sone.data.PostReply
8 import net.pterodactylus.sone.data.Profile
9 import net.pterodactylus.sone.data.Sone
10 import net.pterodactylus.sone.test.asOptional
11 import net.pterodactylus.sone.test.mock
12 import net.pterodactylus.sone.test.whenever
13 import org.hamcrest.MatcherAssert.assertThat
14 import org.hamcrest.Matchers.contains
18 * Unit test for [SearchPage].
20 class SearchPageTest : WebPageTest() {
22 private val page = SearchPage(template, webInterface)
24 override fun getPage() = page
27 fun `empty query redirects to index page`() {
28 verifyRedirect("index.html")
32 fun `empty search phrases redirect to index page`() {
33 addHttpRequestParameter("query", "\"\"")
34 verifyRedirect("index.html")
38 fun `invalid search phrases redirect to index page`() {
39 addHttpRequestParameter("query", "\"")
40 verifyRedirect("index.html")
44 fun `searching for sone link redirects to view sone page`() {
45 addSone("sone-id", mock<Sone>())
46 addHttpRequestParameter("query", "sone://sone-id")
47 verifyRedirect("viewSone.html?sone=sone-id")
51 fun `searching for sone link without prefix redirects to view sone page`() {
52 addSone("sone-id", mock<Sone>())
53 addHttpRequestParameter("query", "sone-id")
54 verifyRedirect("viewSone.html?sone=sone-id")
58 fun `searching for a post link redirects to post page`() {
59 addPost("post-id", mock<Post>())
60 addHttpRequestParameter("query", "post://post-id")
61 verifyRedirect("viewPost.html?post=post-id")
65 fun `searching for a post ID without prefix redirects to post page`() {
66 addPost("post-id", mock<Post>())
67 addHttpRequestParameter("query", "post-id")
68 verifyRedirect("viewPost.html?post=post-id")
72 fun `searching for a reply link redirects to the post page`() {
73 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
74 addPostReply("reply-id", postReply)
75 addHttpRequestParameter("query", "reply://reply-id")
76 verifyRedirect("viewPost.html?post=post-id")
80 fun `searching for a reply ID redirects to the post page`() {
81 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
82 addPostReply("reply-id", postReply)
83 addHttpRequestParameter("query", "reply-id")
84 verifyRedirect("viewPost.html?post=post-id")
88 fun `searching for an album link redirects to the image browser`() {
89 addAlbum("album-id", mock<Album>())
90 addHttpRequestParameter("query", "album://album-id")
91 verifyRedirect("imageBrowser.html?album=album-id")
95 fun `searching for an album ID redirects to the image browser`() {
96 addAlbum("album-id", mock<Album>())
97 addHttpRequestParameter("query", "album-id")
98 verifyRedirect("imageBrowser.html?album=album-id")
102 fun `searching for an image link redirects to the image browser`() {
103 addImage("image-id", mock<Image>())
104 addHttpRequestParameter("query", "image://image-id")
105 verifyRedirect("imageBrowser.html?image=image-id")
109 fun `searching for an image ID redirects to the image browser`() {
110 addImage("image-id", mock<Image>())
111 addHttpRequestParameter("query", "image-id")
112 verifyRedirect("imageBrowser.html?image=image-id")
115 private fun createReply(text: String, postId: String? = null, sone: Sone? = null) = mock<PostReply>().apply {
116 whenever(this.text).thenReturn(text)
117 postId?.run { whenever(this@apply.postId).thenReturn(postId) }
118 sone?.run { whenever(this@apply.sone).thenReturn(sone) }
121 private fun createPost(id: String, text: String) = mock<Post>().apply {
122 whenever(this.id).thenReturn(id)
123 whenever(recipient).thenReturn(absent())
124 whenever(this.text).thenReturn(text)
127 private fun createSoneWithPost(post: Post, sone: Sone? = null) = sone?.apply {
128 whenever(posts).thenReturn(listOf(post))
129 } ?: mock<Sone>().apply {
130 whenever(posts).thenReturn(listOf(post))
131 whenever(profile).thenReturn(Profile(this))
135 fun `searching for a single word finds the post`() {
136 val postWithMatch = createPost("post-with-match", "the word here")
137 val postWithoutMatch = createPost("post-without-match", "no match here")
138 val soneWithMatch = createSoneWithPost(postWithMatch)
139 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
140 addSone("sone-with-match", soneWithMatch)
141 addSone("sone-without-match", soneWithoutMatch)
142 addHttpRequestParameter("query", "word")
144 assertThat(this["postHits"], contains<Post>(postWithMatch))
149 fun `searching for a single word locates word in reply`() {
150 val postWithMatch = createPost("post-with-match", "no match here")
151 val postWithoutMatch = createPost("post-without-match", "no match here")
152 val soneWithMatch = createSoneWithPost(postWithMatch)
153 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
154 val replyWithMatch = createReply("the word here", "post-with-match", soneWithMatch)
155 val replyWithoutMatch = createReply("no match here", "post-without-match", soneWithoutMatch)
156 addPostReply("reply-with-match", replyWithMatch)
157 addPostReply("reply-without-match", replyWithoutMatch)
158 addSone("sone-with-match", soneWithMatch)
159 addSone("sone-without-match", soneWithoutMatch)
160 addHttpRequestParameter("query", "word")
162 assertThat(this["postHits"], contains<Post>(postWithMatch))
166 private fun createSoneWithPost(idPostfix: String, text: String, recipient: Sone? = null, sender: Sone? = null) =
167 createPost("post-$idPostfix", text, recipient).apply {
168 addSone("sone-$idPostfix", createSoneWithPost(this, sender))
172 fun `earlier matches score higher than later matches`() {
173 val postWithEarlyMatch = createSoneWithPost("with-early-match", "optional match")
174 val postWithLaterMatch = createSoneWithPost("with-later-match", "match that is optional")
175 addHttpRequestParameter("query", "optional ")
177 assertThat(this["postHits"], contains<Post>(postWithEarlyMatch, postWithLaterMatch))
182 fun `searching for required word does not return posts without that word`() {
183 val postWithRequiredMatch = createSoneWithPost("with-required-match", "required match")
184 createPost("without-required-match", "not a match")
185 addHttpRequestParameter("query", "+required ")
187 assertThat(this["postHits"], contains<Post>(postWithRequiredMatch))
192 fun `searching for forbidden word does not return posts with that word`() {
193 createSoneWithPost("with-forbidden-match", "forbidden match")
194 val postWithoutForbiddenMatch = createSoneWithPost("without-forbidden-match", "not a match")
195 addHttpRequestParameter("query", "match -forbidden")
197 assertThat(this["postHits"], contains<Post>(postWithoutForbiddenMatch))
202 fun `searching for a plus sign searches for optional plus sign`() {
203 val postWithMatch = createSoneWithPost("with-match", "with + match")
204 createSoneWithPost("without-match", "without match")
205 addHttpRequestParameter("query", "+")
207 assertThat(this["postHits"], contains<Post>(postWithMatch))
212 fun `searching for a minus sign searches for optional minus sign`() {
213 val postWithMatch = createSoneWithPost("with-match", "with - match")
214 createSoneWithPost("without-match", "without match")
215 addHttpRequestParameter("query", "-")
217 assertThat(this["postHits"], contains<Post>(postWithMatch))
221 private fun createPost(id: String, text: String, recipient: Sone?) = mock<Post>().apply {
222 whenever(this.id).thenReturn(id)
223 val recipientId = recipient?.id
224 whenever(this.recipientId).thenReturn(recipientId.asOptional())
225 whenever(this.recipient).thenReturn(recipient.asOptional())
226 whenever(this.text).thenReturn(text)
229 private fun createSone(id: String, firstName: String, middleName: String, lastName: String) = mock<Sone>().apply {
230 whenever(this.id).thenReturn(id)
231 whenever(this.name).thenReturn(id)
232 whenever(this.profile).thenReturn(Profile(this).apply {
233 this.firstName = firstName
234 this.middleName = middleName
235 this.lastName = lastName
240 fun `searching for a recipient finds the correct post`() {
241 val recipient = createSone("recipient", "reci", "pi", "ent")
242 val postWithMatch = createSoneWithPost("with-match", "test", recipient)
243 createSoneWithPost("without-match", "no match")
244 addHttpRequestParameter("query", "recipient")
246 assertThat(this["postHits"], contains<Post>(postWithMatch))
251 fun `searching for a field value finds the correct sone`() {
252 val soneWithProfileField = createSone("sone", "s", "o", "ne")
253 soneWithProfileField.profile.addField("field").value = "value"
254 createSoneWithPost("with-match", "test", sender = soneWithProfileField)
255 createSoneWithPost("without-match", "no match")
256 addHttpRequestParameter("query", "value")
258 assertThat(this["soneHits"], contains(soneWithProfileField))
262 @Suppress("UNCHECKED_CAST")
263 private operator fun <T> get(key: String): T? = templateContext[key] as? T