🔀 Merge branch 'release/v82'
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / web / page / PageToadletTest.kt
1 /*
2  * Sone - PageToadletTest.kt - Copyright Â© 2020 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.web.page
19
20 import freenet.client.HighLevelSimpleClient
21 import freenet.clients.http.Cookie
22 import freenet.clients.http.FProxyFetchInProgress.REFILTER_POLICY
23 import freenet.clients.http.LinkEnabledCallback
24 import freenet.clients.http.PageMaker
25 import freenet.clients.http.ReceivedCookie
26 import freenet.clients.http.Toadlet
27 import freenet.clients.http.ToadletContainer
28 import freenet.clients.http.ToadletContext
29 import freenet.clients.http.bookmark.BookmarkManager
30 import freenet.node.useralerts.UserAlertManager
31 import freenet.support.HTMLNode
32 import freenet.support.MultiValueTable
33 import freenet.support.api.Bucket
34 import freenet.support.api.BucketFactory
35 import freenet.support.api.HTTPRequest
36 import freenet.support.io.ArrayBucket
37 import net.pterodactylus.sone.test.deepMock
38 import net.pterodactylus.sone.test.mock
39 import net.pterodactylus.sone.test.whenever
40 import net.pterodactylus.util.web.Method
41 import net.pterodactylus.util.web.Page
42 import net.pterodactylus.util.web.Response
43 import org.hamcrest.MatcherAssert.assertThat
44 import org.hamcrest.Matchers.arrayContaining
45 import org.hamcrest.Matchers.equalTo
46 import org.hamcrest.Matchers.sameInstance
47 import org.junit.Test
48 import org.mockito.ArgumentMatchers.anyLong
49 import java.net.URI
50 import java.util.Date
51 import kotlin.text.Charsets.UTF_8
52
53 /**
54  * Unit test for PageToadletTest.
55  */
56 class PageToadletTest {
57
58         private val highLevelSimpleClient = mock<HighLevelSimpleClient>()
59         private val httpRequest = mock<HTTPRequest>()
60         private val toadletContext = deepMock<ToadletContext>()
61
62         init {
63                 whenever(toadletContext.bucketFactory.makeBucket(anyLong())).then { ArrayBucket() }
64         }
65
66         @Test
67         fun `get request is forwarded to page correctly`() {
68                 var capturedRequest: FreenetRequest? = null
69                 val page = object : TestPage() {
70                         override fun handleRequest(request: FreenetRequest, response: Response) =
71                                         super.handleRequest(request, response)
72                                                         .also { capturedRequest = request }
73                 }
74                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
75                 pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
76                 assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
77                 assertThat(capturedRequest!!.method, equalTo(Method.GET))
78         }
79
80         @Test
81         fun `post request is forwarded to page correctly`() {
82                 var capturedRequest: FreenetRequest? = null
83                 val page = object : TestPage() {
84                         override fun handleRequest(request: FreenetRequest, response: Response) =
85                                         super.handleRequest(request, response)
86                                                         .also { capturedRequest = request }
87                 }
88                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
89                 pageToadlet.handleMethodPOST(URI("/test"), httpRequest, toadletContext)
90                 assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
91                 assertThat(capturedRequest!!.method, equalTo(Method.POST))
92         }
93
94         @Test
95         fun `content written to response is written to context`() {
96                 val page = object : TestPage() {
97                         override fun handleRequest(request: FreenetRequest, response: Response) =
98                                         response.apply {
99                                                 statusCode = 123
100                                                 statusText = "Works"
101                                                 contentType = "data/test"
102                                                 addHeader("Test", "Value")
103                                                 addHeader("More", "true")
104                                                 addHeader("Test", "Another")
105                                                 write("Content")
106                                         }
107                 }
108                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
109                 var writtenData: ByteArray? = null
110                 var capturedReply: CapturedReply? = null
111                 val toadletContext = object : DelegatingToadletContext(this.toadletContext) {
112                         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) =
113                                         sendReplyHeaders(code, desc, mvt, mimeType, length, false)
114
115                         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) {
116                                 capturedReply = CapturedReply(code, desc, mvt, mimeType, length)
117                         }
118
119                         override fun writeData(data: ByteArray?, offset: Int, length: Int) {
120                                 writtenData = data!!.copyOfRange(offset, offset + length)
121                         }
122
123                         override fun writeData(data: ByteArray?) = writeData(data, 0, data!!.size)
124                         override fun writeData(data: Bucket?) = writeData(data!!.inputStream.readBytes())
125                 }
126                 pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
127                 assertThat(capturedReply!!.code, equalTo(123))
128                 assertThat(capturedReply!!.status, equalTo("Works"))
129                 assertThat(capturedReply!!.mimeType, equalTo("data/test"))
130                 assertThat(capturedReply!!.length, equalTo(7L))
131                 assertThat(capturedReply!!.headers!!.getArray("Test"), arrayContaining<Any>("Value", "Another"))
132                 assertThat(capturedReply!!.headers!!.getArray("More"), arrayContaining<Any>("true"))
133                 assertThat(writtenData!!.toString(UTF_8), equalTo("Content"))
134         }
135
136         @Test
137         fun `link-enabled is true for non-callback pages`() {
138                 val page = TestPage()
139                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
140                 assertThat(pageToadlet.isEnabled(toadletContext), equalTo(true))
141         }
142
143         @Test
144         fun `link-enabled is passed through for callback pages`() {
145                 var capturedToadletContext: ToadletContext? = null
146                 val page = object : TestPage(), LinkEnabledCallback {
147                         override fun isEnabled(ctx: ToadletContext?) = false.also { capturedToadletContext = toadletContext }
148                 }
149                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
150                 assertThat(pageToadlet.isEnabled(toadletContext), equalTo(false))
151                 assertThat(capturedToadletContext, sameInstance(toadletContext))
152         }
153
154         @Test
155         fun `link excemption is false for non-freenet pages`() {
156                 val page = TestPage()
157                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
158                 assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(false))
159         }
160
161         @Test
162         fun `link excemption is passed through for freenet pages`() {
163                 var capturedUri: URI? = null
164                 val page = object : TestPage(), FreenetPage {
165                         override fun isLinkExcepted(link: URI) = true.also { capturedUri = link }
166                 }
167                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
168                 assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(true))
169                 assertThat(capturedUri, equalTo(URI("/test")))
170         }
171
172         @Test
173         fun `path is created correctly from prefix and page path`() {
174                 val page = object : TestPage() {
175                         override fun getPath() = "test-path"
176                 }
177                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/")
178                 assertThat(pageToadlet.path(), equalTo("/path/test-path"))
179         }
180
181         @Test
182         fun `menu name is returned correctly`() {
183                 val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", TestPage(), "/path/")
184                 assertThat(pageToadlet.menuName, equalTo("MenuName"))
185         }
186
187 }
188
189 private data class CapturedReply(val code: Int, val status: String?, val headers: MultiValueTable<String, String>?, val mimeType: String?, val length: Long?)
190
191 private open class TestPage : Page<FreenetRequest> {
192         override fun getPath() = ""
193         override fun isPrefixPage() = false
194         override fun handleRequest(request: FreenetRequest, response: Response) = response
195 }
196
197 private open class DelegatingToadletContext(private val toadletContext: ToadletContext) : ToadletContext {
198         override fun activeToadlet(): Toadlet = toadletContext.activeToadlet()
199         override fun forceDisconnect() = toadletContext.forceDisconnect()
200         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length)
201         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, forceDisableJavascript)
202         @Suppress("DEPRECATION")
203         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, mTime)
204         override fun getUri(): URI = toadletContext.uri
205         override fun getPageMaker(): PageMaker = toadletContext.pageMaker
206         override fun getBucketFactory(): BucketFactory = toadletContext.bucketFactory
207         override fun getHeaders(): MultiValueTable<String, String> = toadletContext.headers
208         override fun checkFullAccess(toadlet: Toadlet?): Boolean = toadletContext.checkFullAccess(toadlet)
209         override fun doRobots(): Boolean = toadletContext.doRobots()
210         override fun getReFilterPolicy(): REFILTER_POLICY = toadletContext.reFilterPolicy
211         override fun getAlertManager(): UserAlertManager = toadletContext.alertManager
212         override fun checkFormPassword(request: HTTPRequest?, redirectTo: String?): Boolean = toadletContext.checkFormPassword(request, redirectTo)
213         override fun checkFormPassword(request: HTTPRequest?): Boolean = toadletContext.checkFormPassword(request)
214         override fun addFormChild(parentNode: HTMLNode?, target: String?, id: String?): HTMLNode = toadletContext.addFormChild(parentNode, target, id)
215         override fun sendReplyHeadersFProxy(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) = toadletContext.sendReplyHeadersFProxy(code, desc, mvt, mimeType, length)
216         override fun setCookie(newCookie: Cookie?) = toadletContext.setCookie(newCookie)
217         override fun isAdvancedModeEnabled(): Boolean = toadletContext.isAdvancedModeEnabled
218         override fun disableProgressPage(): Boolean = toadletContext.disableProgressPage()
219         override fun writeData(data: ByteArray?, offset: Int, length: Int) = toadletContext.writeData(data, offset, length)
220         override fun writeData(data: ByteArray?) = toadletContext.writeData(data)
221         override fun writeData(data: Bucket?) = toadletContext.writeData(data)
222         override fun getCookie(domain: URI?, path: URI?, name: String?): ReceivedCookie? = toadletContext.getCookie(domain, path, name)
223         override fun getUniqueId(): String = toadletContext.uniqueId
224         override fun sendReplyHeadersStatic(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeadersStatic(code, desc, mvt, mimeType, length, mTime)
225         override fun getBookmarkManager(): BookmarkManager = toadletContext.bookmarkManager
226         override fun isAllowedFullAccess(): Boolean = toadletContext.isAllowedFullAccess
227         override fun hasFormPassword(request: HTTPRequest?): Boolean = toadletContext.hasFormPassword(request)
228         override fun getFormPassword(): String = toadletContext.formPassword
229         override fun getContainer(): ToadletContainer = toadletContext.container
230 }