/*
* Sone - PageToadletTest.kt - Copyright © 2020 David Roden
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package net.pterodactylus.sone.web.page
import freenet.client.HighLevelSimpleClient
import freenet.clients.http.Cookie
import freenet.clients.http.FProxyFetchInProgress.REFILTER_POLICY
import freenet.clients.http.LinkEnabledCallback
import freenet.clients.http.PageMaker
import freenet.clients.http.ReceivedCookie
import freenet.clients.http.SessionManager
import freenet.clients.http.Toadlet
import freenet.clients.http.ToadletContainer
import freenet.clients.http.ToadletContext
import freenet.clients.http.bookmark.BookmarkManager
import freenet.node.useralerts.UserAlertManager
import freenet.support.HTMLNode
import freenet.support.MultiValueTable
import freenet.support.api.Bucket
import freenet.support.api.BucketFactory
import freenet.support.api.HTTPRequest
import freenet.support.io.ArrayBucket
import net.pterodactylus.sone.test.deepMock
import net.pterodactylus.sone.test.mock
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.util.web.Method
import net.pterodactylus.util.web.Page
import net.pterodactylus.util.web.Response
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.arrayContaining
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.sameInstance
import org.junit.Test
import org.mockito.ArgumentMatchers.anyLong
import java.net.URI
import java.util.Date
import kotlin.text.Charsets.UTF_8
/**
* Unit test for PageToadletTest.
*/
class PageToadletTest {
private val highLevelSimpleClient = mock()
private val sessionManager = mock()
private val httpRequest = mock()
private val toadletContext = deepMock()
init {
whenever(toadletContext.bucketFactory.makeBucket(anyLong())).then { ArrayBucket() }
}
@Test
fun `get request is forwarded to page correctly`() {
var capturedRequest: FreenetRequest? = null
val page = object : TestPage() {
override fun handleRequest(request: FreenetRequest, response: Response) =
super.handleRequest(request, response)
.also { capturedRequest = request }
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
assertThat(capturedRequest!!.method, equalTo(Method.GET))
}
@Test
fun `post request is forwarded to page correctly`() {
var capturedRequest: FreenetRequest? = null
val page = object : TestPage() {
override fun handleRequest(request: FreenetRequest, response: Response) =
super.handleRequest(request, response)
.also { capturedRequest = request }
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
pageToadlet.handleMethodPOST(URI("/test"), httpRequest, toadletContext)
assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
assertThat(capturedRequest!!.method, equalTo(Method.POST))
}
@Test
fun `content written to response is written to context`() {
val page = object : TestPage() {
override fun handleRequest(request: FreenetRequest, response: Response) =
response.apply {
statusCode = 123
statusText = "Works"
contentType = "data/test"
addHeader("Test", "Value")
addHeader("More", "true")
addHeader("Test", "Another")
write("Content")
}
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
var writtenData: ByteArray? = null
var capturedReply: CapturedReply? = null
val toadletContext = object : DelegatingToadletContext(this.toadletContext) {
override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) =
sendReplyHeaders(code, desc, mvt, mimeType, length, false)
override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) {
capturedReply = CapturedReply(code, desc, mvt, mimeType, length)
}
override fun writeData(data: ByteArray?, offset: Int, length: Int) {
writtenData = data!!.copyOfRange(offset, offset + length)
}
override fun writeData(data: ByteArray?) = writeData(data, 0, data!!.size)
override fun writeData(data: Bucket?) = writeData(data!!.inputStream.readBytes())
}
pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
assertThat(capturedReply!!.code, equalTo(123))
assertThat(capturedReply!!.status, equalTo("Works"))
assertThat(capturedReply!!.mimeType, equalTo("data/test"))
assertThat(capturedReply!!.length, equalTo(7L))
assertThat(capturedReply!!.headers!!.getArray("Test"), arrayContaining("Value", "Another"))
assertThat(capturedReply!!.headers!!.getArray("More"), arrayContaining("true"))
assertThat(writtenData!!.toString(UTF_8), equalTo("Content"))
}
@Test
fun `link-enabled is true for non-callback pages`() {
val page = TestPage()
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
assertThat(pageToadlet.isEnabled(toadletContext), equalTo(true))
}
@Test
fun `link-enabled is passed through for callback pages`() {
var capturedToadletContext: ToadletContext? = null
val page = object : TestPage(), LinkEnabledCallback {
override fun isEnabled(ctx: ToadletContext?) = false.also { capturedToadletContext = toadletContext }
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
assertThat(pageToadlet.isEnabled(toadletContext), equalTo(false))
assertThat(capturedToadletContext, sameInstance(toadletContext))
}
@Test
fun `link excemption is false for non-freenet pages`() {
val page = TestPage()
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(false))
}
@Test
fun `link excemption is passed through for freenet pages`() {
var capturedUri: URI? = null
val page = object : TestPage(), FreenetPage {
override fun isLinkExcepted(link: URI) = true.also { capturedUri = link }
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(true))
assertThat(capturedUri, equalTo(URI("/test")))
}
@Test
fun `path is created correctly from prefix and page path`() {
val page = object : TestPage() {
override fun getPath() = "test-path"
}
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
assertThat(pageToadlet.path(), equalTo("/path/test-path"))
}
@Test
fun `menu name is returned correctly`() {
val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", TestPage(), "/path/")
assertThat(pageToadlet.menuName, equalTo("MenuName"))
}
}
private data class CapturedReply(val code: Int, val status: String?, val headers: MultiValueTable?, val mimeType: String?, val length: Long?)
private open class TestPage : Page {
override fun getPath() = ""
override fun isPrefixPage() = false
override fun handleRequest(request: FreenetRequest, response: Response) = response
}
private open class DelegatingToadletContext(private val toadletContext: ToadletContext) : ToadletContext {
override fun activeToadlet(): Toadlet = toadletContext.activeToadlet()
override fun forceDisconnect() = toadletContext.forceDisconnect()
override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length)
override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, forceDisableJavascript)
@Suppress("DEPRECATION")
override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, mTime)
override fun getUri(): URI = toadletContext.uri
override fun getPageMaker(): PageMaker = toadletContext.pageMaker
override fun getBucketFactory(): BucketFactory = toadletContext.bucketFactory
override fun getHeaders(): MultiValueTable = toadletContext.headers
override fun checkFullAccess(toadlet: Toadlet?): Boolean = toadletContext.checkFullAccess(toadlet)
override fun doRobots(): Boolean = toadletContext.doRobots()
override fun getReFilterPolicy(): REFILTER_POLICY = toadletContext.reFilterPolicy
override fun getAlertManager(): UserAlertManager = toadletContext.alertManager
override fun checkFormPassword(request: HTTPRequest?, redirectTo: String?): Boolean = toadletContext.checkFormPassword(request, redirectTo)
override fun checkFormPassword(request: HTTPRequest?): Boolean = toadletContext.checkFormPassword(request)
override fun addFormChild(parentNode: HTMLNode?, target: String?, id: String?): HTMLNode = toadletContext.addFormChild(parentNode, target, id)
override fun sendReplyHeadersFProxy(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = toadletContext.sendReplyHeadersFProxy(code, desc, mvt, mimeType, length)
override fun setCookie(newCookie: Cookie?) = toadletContext.setCookie(newCookie)
override fun isAdvancedModeEnabled(): Boolean = toadletContext.isAdvancedModeEnabled
override fun disableProgressPage(): Boolean = toadletContext.disableProgressPage()
override fun writeData(data: ByteArray?, offset: Int, length: Int) = toadletContext.writeData(data, offset, length)
override fun writeData(data: ByteArray?) = toadletContext.writeData(data)
override fun writeData(data: Bucket?) = toadletContext.writeData(data)
override fun getCookie(domain: URI?, path: URI?, name: String?): ReceivedCookie? = toadletContext.getCookie(domain, path, name)
override fun getUniqueId(): String = toadletContext.uniqueId
override fun sendReplyHeadersStatic(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeadersStatic(code, desc, mvt, mimeType, length, mTime)
override fun getBookmarkManager(): BookmarkManager = toadletContext.bookmarkManager
override fun isAllowedFullAccess(): Boolean = toadletContext.isAllowedFullAccess
override fun hasFormPassword(request: HTTPRequest?): Boolean = toadletContext.hasFormPassword(request)
override fun getFormPassword(): String = toadletContext.formPassword
override fun getContainer(): ToadletContainer = toadletContext.container
}