package net.pterodactylus.sone.web.pages
import com.google.common.base.Optional.absent
+import com.google.common.base.Ticker
import net.pterodactylus.sone.data.Album
import net.pterodactylus.sone.data.Image
import net.pterodactylus.sone.data.Post
import net.pterodactylus.sone.data.PostReply
import net.pterodactylus.sone.data.Profile
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.asOptional
+import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isOnPage
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
+import net.pterodactylus.sone.utils.asOptional
+import net.pterodactylus.sone.web.baseInjector
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.contains
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.notNullValue
import org.junit.Test
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicInteger
/**
* Unit test for [SearchPage].
*/
-class SearchPageTest : WebPageTest() {
+class SearchPageTest: WebPageTest({ template, webInterface -> SearchPage(template, webInterface, ticker) }) {
- private val page = SearchPage(template, webInterface)
+ companion object {
+ val ticker = mock<Ticker>()
+ }
+
+ @Test
+ fun `page returns correct path`() {
+ assertThat(page.path, equalTo("search.html"))
+ }
- override fun getPage() = page
+ @Test
+ fun `page does not require login`() {
+ assertThat(page.requiresLogin(), equalTo(false))
+ }
+
+ @Test
+ fun `page returns correct title`() {
+ addTranslation("Page.Search.Title", "search page title")
+ assertThat(page.getPageTitle(freenetRequest), equalTo("search page title"))
+ }
@Test
fun `empty query redirects to index page`() {
addSone("sone-with-match", soneWithMatch)
addSone("sone-without-match", soneWithoutMatch)
addHttpRequestParameter("query", "word")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithMatch))
+ }
}
@Test
addSone("sone-with-match", soneWithMatch)
addSone("sone-without-match", soneWithoutMatch)
addHttpRequestParameter("query", "word")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithMatch))
+ }
}
private fun createSoneWithPost(idPostfix: String, text: String, recipient: Sone? = null, sender: Sone? = null) =
val postWithEarlyMatch = createSoneWithPost("with-early-match", "optional match")
val postWithLaterMatch = createSoneWithPost("with-later-match", "match that is optional")
addHttpRequestParameter("query", "optional ")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithEarlyMatch, postWithLaterMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithEarlyMatch, postWithLaterMatch))
+ }
}
@Test
val postWithRequiredMatch = createSoneWithPost("with-required-match", "required match")
createPost("without-required-match", "not a match")
addHttpRequestParameter("query", "+required ")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithRequiredMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithRequiredMatch))
+ }
}
@Test
createSoneWithPost("with-forbidden-match", "forbidden match")
val postWithoutForbiddenMatch = createSoneWithPost("without-forbidden-match", "not a match")
addHttpRequestParameter("query", "match -forbidden")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithoutForbiddenMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithoutForbiddenMatch))
+ }
}
@Test
val postWithMatch = createSoneWithPost("with-match", "with + match")
createSoneWithPost("without-match", "without match")
addHttpRequestParameter("query", "+")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithMatch))
+ }
}
@Test
val postWithMatch = createSoneWithPost("with-match", "with - match")
createSoneWithPost("without-match", "without match")
addHttpRequestParameter("query", "-")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithMatch))
+ }
}
private fun createPost(id: String, text: String, recipient: Sone?) = mock<Post>().apply {
whenever(this.text).thenReturn(text)
}
- private fun createSone(id: String, firstName: String, middleName: String, lastName: String) = mock<Sone>().apply {
+ private fun createSone(id: String, firstName: String? = null, middleName: String? = null, lastName: String? = null) = mock<Sone>().apply {
whenever(this.id).thenReturn(id)
whenever(this.name).thenReturn(id)
whenever(this.profile).thenReturn(Profile(this).apply {
val postWithMatch = createSoneWithPost("with-match", "test", recipient)
createSoneWithPost("without-match", "no match")
addHttpRequestParameter("query", "recipient")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["postHits"], contains<Post>(postWithMatch))
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains<Post>(postWithMatch))
+ }
}
@Test
createSoneWithPost("with-match", "test", sender = soneWithProfileField)
createSoneWithPost("without-match", "no match")
addHttpRequestParameter("query", "value")
- page.handleRequest(freenetRequest, templateContext)
- assertThat(this["soneHits"], contains(soneWithProfileField))
+ verifyNoRedirect {
+ assertThat(this["soneHits"], contains(soneWithProfileField))
+ }
+ }
+
+ @Test
+ fun `sone hits are paginated correctly`() {
+ core.preferences.postsPerPage = 2
+ val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
+ .onEach { addSone(it.id, it) }
+ addHttpRequestParameter("query", "sone")
+ verifyNoRedirect {
+ assertThat(this["sonePagination"], isOnPage(0).hasPages(2))
+ assertThat(this["soneHits"], contains(sones[0], sones[2]))
+ }
+ }
+
+ @Test
+ fun `sone hits page 2 is shown correctly`() {
+ core.preferences.postsPerPage = 2
+ val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
+ .onEach { addSone(it.id, it) }
+ addHttpRequestParameter("query", "sone")
+ addHttpRequestParameter("sonePage", "1")
+ verifyNoRedirect {
+ assertThat(this["sonePagination"], isOnPage(1).hasPages(2))
+ assertThat(this["soneHits"], contains(sones[3]))
+ }
+ }
+
+ @Test
+ fun `post hits are paginated correctly`() {
+ core.preferences.postsPerPage = 2
+ val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
+ addHttpRequestParameter("query", "sone")
+ verifyNoRedirect {
+ assertThat(this["postPagination"], isOnPage(0).hasPages(2))
+ assertThat(this["postHits"], contains(sones[0], sones[2]))
+ }
+ }
+
+ @Test
+ fun `post hits page 2 is shown correctly`() {
+ core.preferences.postsPerPage = 2
+ val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
+ addHttpRequestParameter("query", "sone")
+ addHttpRequestParameter("postPage", "1")
+ verifyNoRedirect {
+ assertThat(this["postPagination"], isOnPage(1).hasPages(2))
+ assertThat(this["postHits"], contains(sones[3]))
+ }
+ }
+
+ @Test
+ fun `post search results are cached`() {
+ val post = createPost("with-match", "text")
+ val callCounter = AtomicInteger()
+ whenever(post.text).thenAnswer { callCounter.incrementAndGet(); "text" }
+ val sone = createSoneWithPost(post)
+ addSone("sone", sone)
+ addHttpRequestParameter("query", "text")
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains(post))
+ }
+ verifyNoRedirect {
+ assertThat(callCounter.get(), equalTo(1))
+ }
+ }
+
+ @Test
+ fun `post search results are cached for five minutes`() {
+ val post = createPost("with-match", "text")
+ val callCounter = AtomicInteger()
+ whenever(post.text).thenAnswer { callCounter.incrementAndGet(); "text" }
+ val sone = createSoneWithPost(post)
+ addSone("sone", sone)
+ addHttpRequestParameter("query", "text")
+ verifyNoRedirect {
+ assertThat(this["postHits"], contains(post))
+ }
+ whenever(ticker.read()).thenReturn(TimeUnit.MINUTES.toNanos(5) + 1)
+ verifyNoRedirect {
+ assertThat(callCounter.get(), equalTo(2))
+ }
}
@Suppress("UNCHECKED_CAST")
private operator fun <T> get(key: String): T? = templateContext[key] as? T
+ @Test
+ fun `page can be created by dependency injection`() {
+ assertThat(baseInjector.getInstance<SearchPage>(), notNullValue())
+ }
+
}