Add unit test for search page
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / web / SearchPageTest.kt
1 package net.pterodactylus.sone.web
2
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.mock
11 import net.pterodactylus.sone.test.whenever
12 import org.hamcrest.MatcherAssert.assertThat
13 import org.hamcrest.Matchers.contains
14 import org.junit.Test
15
16 /**
17  * Unit test for [SearchPage].
18  */
19 class SearchPageTest : WebPageTest() {
20
21         private val page = SearchPage(template, webInterface)
22
23         override fun getPage() = page
24
25         @Test
26         fun `empty query redirects to index page`() {
27                 verifyRedirect("index.html")
28         }
29
30         @Test
31         fun `empty search phrases redirect to index page`() {
32                 addHttpRequestParameter("query", "\"\"")
33                 verifyRedirect("index.html")
34         }
35
36         @Test
37         fun `invalid search phrases redirect to index page`() {
38                 addHttpRequestParameter("query", "\"")
39                 verifyRedirect("index.html")
40         }
41
42         @Test
43         fun `searching for sone link redirects to view sone page`() {
44                 addSone("sone-id", mock<Sone>())
45                 addHttpRequestParameter("query", "sone://sone-id")
46                 verifyRedirect("viewSone.html?sone=sone-id")
47         }
48
49         @Test
50         fun `searching for sone link without prefix redirects to view sone page`() {
51                 addSone("sone-id", mock<Sone>())
52                 addHttpRequestParameter("query", "sone-id")
53                 verifyRedirect("viewSone.html?sone=sone-id")
54         }
55
56         @Test
57         fun `searching for a post link redirects to post page`() {
58                 addPost("post-id", mock<Post>())
59                 addHttpRequestParameter("query", "post://post-id")
60                 verifyRedirect("viewPost.html?post=post-id")
61         }
62
63         @Test
64         fun `searching for a post ID without prefix redirects to post page`() {
65                 addPost("post-id", mock<Post>())
66                 addHttpRequestParameter("query", "post-id")
67                 verifyRedirect("viewPost.html?post=post-id")
68         }
69
70         @Test
71         fun `searching for a reply link redirects to the post page`() {
72                 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
73                 addPostReply("reply-id", postReply)
74                 addHttpRequestParameter("query", "reply://reply-id")
75                 verifyRedirect("viewPost.html?post=post-id")
76         }
77
78         @Test
79         fun `searching for a reply ID redirects to the post page`() {
80                 val postReply = mock<PostReply>().apply { whenever(postId).thenReturn("post-id") }
81                 addPostReply("reply-id", postReply)
82                 addHttpRequestParameter("query", "reply-id")
83                 verifyRedirect("viewPost.html?post=post-id")
84         }
85
86         @Test
87         fun `searching for an album link redirects to the image browser`() {
88                 addAlbum("album-id", mock<Album>())
89                 addHttpRequestParameter("query", "album://album-id")
90                 verifyRedirect("imageBrowser.html?album=album-id")
91         }
92
93         @Test
94         fun `searching for an album ID redirects to the image browser`() {
95                 addAlbum("album-id", mock<Album>())
96                 addHttpRequestParameter("query", "album-id")
97                 verifyRedirect("imageBrowser.html?album=album-id")
98         }
99
100         @Test
101         fun `searching for an image link redirects to the image browser`() {
102                 addImage("image-id", mock<Image>())
103                 addHttpRequestParameter("query", "image://image-id")
104                 verifyRedirect("imageBrowser.html?image=image-id")
105         }
106
107         @Test
108         fun `searching for an image ID redirects to the image browser`() {
109                 addImage("image-id", mock<Image>())
110                 addHttpRequestParameter("query", "image-id")
111                 verifyRedirect("imageBrowser.html?image=image-id")
112         }
113
114         private fun createReply(text: String, postId: String? = null, sone: Sone? = null) = mock<PostReply>().apply {
115                 whenever(this.text).thenReturn(text)
116                 postId?.run { whenever(this@apply.postId).thenReturn(postId) }
117                 sone?.run { whenever(this@apply.sone).thenReturn(sone) }
118         }
119
120         private fun createPost(id: String, text: String) = mock<Post>().apply {
121                 whenever(this.id).thenReturn(id)
122                 whenever(recipient).thenReturn(absent())
123                 whenever(this.text).thenReturn(text)
124         }
125
126         private fun createSoneWithPost(post: Post) = mock<Sone>().apply {
127                 whenever(posts).thenReturn(listOf(post))
128                 whenever(profile).thenReturn(Profile(this))
129         }
130
131         @Test
132         @Suppress("UNCHECKED_CAST")
133         fun `searching for a single word finds the post`() {
134                 val postWithMatch = createPost("post-with-match", "the word here")
135                 val postWithoutMatch = createPost("post-without-match", "no match here")
136                 val soneWithMatch = createSoneWithPost(postWithMatch)
137                 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
138                 addSone("sone-with-match", soneWithMatch)
139                 addSone("sone-without-match", soneWithoutMatch)
140                 addHttpRequestParameter("query", "word")
141                 page.handleRequest(freenetRequest, templateContext)
142                 assertThat(templateContext["postHits"] as Collection<Post>, contains<Post>(postWithMatch))
143         }
144
145         @Test
146         @Suppress("UNCHECKED_CAST")
147         fun `searching for a single word locates word in reply`() {
148                 val postWithMatch = createPost("post-with-match", "no match here")
149                 val postWithoutMatch = createPost("post-without-match", "no match here")
150                 val soneWithMatch = createSoneWithPost(postWithMatch)
151                 val soneWithoutMatch = createSoneWithPost(postWithoutMatch)
152                 val replyWithMatch = createReply("the word here", "post-with-match", soneWithMatch)
153                 val replyWithoutMatch = createReply("no match here", "post-without-match", soneWithoutMatch)
154                 addPostReply("reply-with-match", replyWithMatch)
155                 addPostReply("reply-without-match", replyWithoutMatch)
156                 addSone("sone-with-match", soneWithMatch)
157                 addSone("sone-without-match", soneWithoutMatch)
158                 addHttpRequestParameter("query", "word")
159                 page.handleRequest(freenetRequest, templateContext)
160                 assertThat(templateContext["postHits"] as Collection<Post>, contains<Post>(postWithMatch))
161         }
162
163         private fun createSoneWithPost(idPostfix: String, text: String) =
164                         createPost("post-$idPostfix", text).apply {
165                                 addSone("sone-$idPostfix", createSoneWithPost(this))
166                         }
167
168         @Test
169         @Suppress("UNCHECKED_CAST")
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(templateContext["postHits"] as Collection<Post>, contains<Post>(postWithEarlyMatch, postWithLaterMatch))
176         }
177
178         @Test
179         @Suppress("UNCHECKED_CAST")
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(templateContext["postHits"] as Collection<Post>, contains<Post>(postWithRequiredMatch))
186         }
187
188         @Test
189         @Suppress("UNCHECKED_CAST")
190         fun `searching for forbidden word does not return posts with that word`() {
191                 createSoneWithPost("with-forbidden-match", "forbidden match")
192                 val postWithoutForbiddenMatch = createSoneWithPost("without-forbidden-match", "not a match")
193                 addHttpRequestParameter("query", "match -forbidden")
194                 page.handleRequest(freenetRequest, templateContext)
195                 assertThat(templateContext["postHits"] as Collection<Post>, contains<Post>(postWithoutForbiddenMatch))
196         }
197
198 }