1 package net.pterodactylus.sone.web
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")
143 page.handleRequest(freenetRequest, templateContext)
144 assertThat(this["postHits"], contains<Post>(postWithMatch))
148 fun `searching for a single word locates word in reply`() {
149 val postWithMatch = createPost("post-with-match", "no match here")
150 val postWithoutMatch = createPost("post-without-match", "no match here")
151 val soneWithMatch = createSoneWithPost(postWithMatch)
152 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
153 val replyWithMatch = createReply("the word here", "post-with-match", soneWithMatch)
154 val replyWithoutMatch = createReply("no match here", "post-without-match", soneWithoutMatch)
155 addPostReply("reply-with-match", replyWithMatch)
156 addPostReply("reply-without-match", replyWithoutMatch)
157 addSone("sone-with-match", soneWithMatch)
158 addSone("sone-without-match", soneWithoutMatch)
159 addHttpRequestParameter("query", "word")
160 page.handleRequest(freenetRequest, templateContext)
161 assertThat(this["postHits"], contains<Post>(postWithMatch))
164 private fun createSoneWithPost(idPostfix: String, text: String, recipient: Sone? = null, sender: Sone? = null) =
165 createPost("post-$idPostfix", text, recipient).apply {
166 addSone("sone-$idPostfix", createSoneWithPost(this, sender))
170 fun `earlier matches score higher than later matches`() {
171 val postWithEarlyMatch = createSoneWithPost("with-early-match", "optional match")
172 val postWithLaterMatch = createSoneWithPost("with-later-match", "match that is optional")
173 addHttpRequestParameter("query", "optional ")
174 page.handleRequest(freenetRequest, templateContext)
175 assertThat(this["postHits"], contains<Post>(postWithEarlyMatch, postWithLaterMatch))
179 fun `searching for required word does not return posts without that word`() {
180 val postWithRequiredMatch = createSoneWithPost("with-required-match", "required match")
181 createPost("without-required-match", "not a match")
182 addHttpRequestParameter("query", "+required ")
183 page.handleRequest(freenetRequest, templateContext)
184 assertThat(this["postHits"], contains<Post>(postWithRequiredMatch))
188 fun `searching for forbidden word does not return posts with that word`() {
189 createSoneWithPost("with-forbidden-match", "forbidden match")
190 val postWithoutForbiddenMatch = createSoneWithPost("without-forbidden-match", "not a match")
191 addHttpRequestParameter("query", "match -forbidden")
192 page.handleRequest(freenetRequest, templateContext)
193 assertThat(this["postHits"], contains<Post>(postWithoutForbiddenMatch))
197 fun `searching for a plus sign searches for optional plus sign`() {
198 val postWithMatch = createSoneWithPost("with-match", "with + match")
199 createSoneWithPost("without-match", "without match")
200 addHttpRequestParameter("query", "+")
201 page.handleRequest(freenetRequest, templateContext)
202 assertThat(this["postHits"], contains<Post>(postWithMatch))
206 fun `searching for a minus sign searches for optional minus sign`() {
207 val postWithMatch = createSoneWithPost("with-match", "with - match")
208 createSoneWithPost("without-match", "without match")
209 addHttpRequestParameter("query", "-")
210 page.handleRequest(freenetRequest, templateContext)
211 assertThat(this["postHits"], contains<Post>(postWithMatch))
214 private fun createPost(id: String, text: String, recipient: Sone?) = mock<Post>().apply {
215 whenever(this.id).thenReturn(id)
216 val recipientId = recipient?.id
217 whenever(this.recipientId).thenReturn(recipientId.asOptional())
218 whenever(this.recipient).thenReturn(recipient.asOptional())
219 whenever(this.text).thenReturn(text)
222 private fun createSone(id: String, firstName: String, middleName: String, lastName: String) = mock<Sone>().apply {
223 whenever(this.id).thenReturn(id)
224 whenever(this.name).thenReturn(id)
225 whenever(this.profile).thenReturn(Profile(this).apply {
226 this.firstName = firstName
227 this.middleName = middleName
228 this.lastName = lastName
233 fun `searching for a recipient finds the correct post`() {
234 val recipient = createSone("recipient", "reci", "pi", "ent")
235 val postWithMatch = createSoneWithPost("with-match", "test", recipient)
236 createSoneWithPost("without-match", "no match")
237 addHttpRequestParameter("query", "recipient")
238 page.handleRequest(freenetRequest, templateContext)
239 assertThat(this["postHits"], contains<Post>(postWithMatch))
243 fun `searching for a field value finds the correct sone`() {
244 val soneWithProfileField = createSone("sone", "s", "o", "ne")
245 soneWithProfileField.profile.addField("field").value = "value"
246 createSoneWithPost("with-match", "test", sender = soneWithProfileField)
247 createSoneWithPost("without-match", "no match")
248 addHttpRequestParameter("query", "value")
249 page.handleRequest(freenetRequest, templateContext)
250 assertThat(this["soneHits"], contains(soneWithProfileField))
253 @Suppress("UNCHECKED_CAST")
254 private operator fun <T> get(key: String): T? = templateContext[key] as? T