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 net.pterodactylus.sone.web.pages.SearchPage
14 import org.hamcrest.MatcherAssert.assertThat
15 import org.hamcrest.Matchers.contains
19 * Unit test for [SearchPage].
21 class SearchPageTest : WebPageTest() {
23 private val page = SearchPage(template, webInterface)
25 override fun getPage() = page
28 fun `empty query redirects to index page`() {
29 verifyRedirect("index.html")
33 fun `empty search phrases redirect to index page`() {
34 addHttpRequestParameter("query", "\"\"")
35 verifyRedirect("index.html")
39 fun `invalid search phrases redirect to index page`() {
40 addHttpRequestParameter("query", "\"")
41 verifyRedirect("index.html")
45 fun `searching for sone link redirects to view sone page`() {
46 addSone("sone-id", mock<Sone>())
47 addHttpRequestParameter("query", "sone://sone-id")
48 verifyRedirect("viewSone.html?sone=sone-id")
52 fun `searching for sone link without prefix redirects to view sone page`() {
53 addSone("sone-id", mock<Sone>())
54 addHttpRequestParameter("query", "sone-id")
55 verifyRedirect("viewSone.html?sone=sone-id")
59 fun `searching for a post link redirects to post page`() {
60 addPost("post-id", mock<Post>())
61 addHttpRequestParameter("query", "post://post-id")
62 verifyRedirect("viewPost.html?post=post-id")
66 fun `searching for a post ID without prefix redirects to post page`() {
67 addPost("post-id", mock<Post>())
68 addHttpRequestParameter("query", "post-id")
69 verifyRedirect("viewPost.html?post=post-id")
73 fun `searching for a reply link redirects to the post page`() {
74 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
75 addPostReply("reply-id", postReply)
76 addHttpRequestParameter("query", "reply://reply-id")
77 verifyRedirect("viewPost.html?post=post-id")
81 fun `searching for a reply ID redirects to the post page`() {
82 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
83 addPostReply("reply-id", postReply)
84 addHttpRequestParameter("query", "reply-id")
85 verifyRedirect("viewPost.html?post=post-id")
89 fun `searching for an album link redirects to the image browser`() {
90 addAlbum("album-id", mock<Album>())
91 addHttpRequestParameter("query", "album://album-id")
92 verifyRedirect("imageBrowser.html?album=album-id")
96 fun `searching for an album ID redirects to the image browser`() {
97 addAlbum("album-id", mock<Album>())
98 addHttpRequestParameter("query", "album-id")
99 verifyRedirect("imageBrowser.html?album=album-id")
103 fun `searching for an image link redirects to the image browser`() {
104 addImage("image-id", mock<Image>())
105 addHttpRequestParameter("query", "image://image-id")
106 verifyRedirect("imageBrowser.html?image=image-id")
110 fun `searching for an image ID redirects to the image browser`() {
111 addImage("image-id", mock<Image>())
112 addHttpRequestParameter("query", "image-id")
113 verifyRedirect("imageBrowser.html?image=image-id")
116 private fun createReply(text: String, postId: String? = null, sone: Sone? = null) = mock<PostReply>().apply {
117 whenever(this.text).thenReturn(text)
118 postId?.run { whenever(this@apply.postId).thenReturn(postId) }
119 sone?.run { whenever(this@apply.sone).thenReturn(sone) }
122 private fun createPost(id: String, text: String) = mock<Post>().apply {
123 whenever(this.id).thenReturn(id)
124 whenever(recipient).thenReturn(absent())
125 whenever(this.text).thenReturn(text)
128 private fun createSoneWithPost(post: Post, sone: Sone? = null) = sone?.apply {
129 whenever(posts).thenReturn(listOf(post))
130 } ?: mock<Sone>().apply {
131 whenever(posts).thenReturn(listOf(post))
132 whenever(profile).thenReturn(Profile(this))
136 fun `searching for a single word finds the post`() {
137 val postWithMatch = createPost("post-with-match", "the word here")
138 val postWithoutMatch = createPost("post-without-match", "no match here")
139 val soneWithMatch = createSoneWithPost(postWithMatch)
140 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
141 addSone("sone-with-match", soneWithMatch)
142 addSone("sone-without-match", soneWithoutMatch)
143 addHttpRequestParameter("query", "word")
144 page.handleRequest(freenetRequest, templateContext)
145 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")
161 page.handleRequest(freenetRequest, templateContext)
162 assertThat(this["postHits"], contains<Post>(postWithMatch))
165 private fun createSoneWithPost(idPostfix: String, text: String, recipient: Sone? = null, sender: Sone? = null) =
166 createPost("post-$idPostfix", text, recipient).apply {
167 addSone("sone-$idPostfix", createSoneWithPost(this, sender))
171 fun `earlier matches score higher than later matches`() {
172 val postWithEarlyMatch = createSoneWithPost("with-early-match", "optional match")
173 val postWithLaterMatch = createSoneWithPost("with-later-match", "match that is optional")
174 addHttpRequestParameter("query", "optional ")
175 page.handleRequest(freenetRequest, templateContext)
176 assertThat(this["postHits"], contains<Post>(postWithEarlyMatch, postWithLaterMatch))
180 fun `searching for required word does not return posts without that word`() {
181 val postWithRequiredMatch = createSoneWithPost("with-required-match", "required match")
182 createPost("without-required-match", "not a match")
183 addHttpRequestParameter("query", "+required ")
184 page.handleRequest(freenetRequest, templateContext)
185 assertThat(this["postHits"], contains<Post>(postWithRequiredMatch))
189 fun `searching for forbidden word does not return posts with that word`() {
190 createSoneWithPost("with-forbidden-match", "forbidden match")
191 val postWithoutForbiddenMatch = createSoneWithPost("without-forbidden-match", "not a match")
192 addHttpRequestParameter("query", "match -forbidden")
193 page.handleRequest(freenetRequest, templateContext)
194 assertThat(this["postHits"], contains<Post>(postWithoutForbiddenMatch))
198 fun `searching for a plus sign searches for optional plus sign`() {
199 val postWithMatch = createSoneWithPost("with-match", "with + match")
200 createSoneWithPost("without-match", "without match")
201 addHttpRequestParameter("query", "+")
202 page.handleRequest(freenetRequest, templateContext)
203 assertThat(this["postHits"], contains<Post>(postWithMatch))
207 fun `searching for a minus sign searches for optional minus sign`() {
208 val postWithMatch = createSoneWithPost("with-match", "with - match")
209 createSoneWithPost("without-match", "without match")
210 addHttpRequestParameter("query", "-")
211 page.handleRequest(freenetRequest, templateContext)
212 assertThat(this["postHits"], contains<Post>(postWithMatch))
215 private fun createPost(id: String, text: String, recipient: Sone?) = mock<Post>().apply {
216 whenever(this.id).thenReturn(id)
217 val recipientId = recipient?.id
218 whenever(this.recipientId).thenReturn(recipientId.asOptional())
219 whenever(this.recipient).thenReturn(recipient.asOptional())
220 whenever(this.text).thenReturn(text)
223 private fun createSone(id: String, firstName: String, middleName: String, lastName: String) = mock<Sone>().apply {
224 whenever(this.id).thenReturn(id)
225 whenever(this.name).thenReturn(id)
226 whenever(this.profile).thenReturn(Profile(this).apply {
227 this.firstName = firstName
228 this.middleName = middleName
229 this.lastName = lastName
234 fun `searching for a recipient finds the correct post`() {
235 val recipient = createSone("recipient", "reci", "pi", "ent")
236 val postWithMatch = createSoneWithPost("with-match", "test", recipient)
237 createSoneWithPost("without-match", "no match")
238 addHttpRequestParameter("query", "recipient")
239 page.handleRequest(freenetRequest, templateContext)
240 assertThat(this["postHits"], contains<Post>(postWithMatch))
244 fun `searching for a field value finds the correct sone`() {
245 val soneWithProfileField = createSone("sone", "s", "o", "ne")
246 soneWithProfileField.profile.addField("field").value = "value"
247 createSoneWithPost("with-match", "test", sender = soneWithProfileField)
248 createSoneWithPost("without-match", "no match")
249 addHttpRequestParameter("query", "value")
250 page.handleRequest(freenetRequest, templateContext)
251 assertThat(this["soneHits"], contains(soneWithProfileField))
254 @Suppress("UNCHECKED_CAST")
255 private operator fun <T> get(key: String): T? = templateContext[key] as? T