🚨 Suppress deprecation warning
[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.SessionManager
27 import freenet.clients.http.Toadlet
28 import freenet.clients.http.ToadletContainer
29 import freenet.clients.http.ToadletContext
30 import freenet.clients.http.bookmark.BookmarkManager
31 import freenet.node.useralerts.UserAlertManager
32 import freenet.support.HTMLNode
33 import freenet.support.MultiValueTable
34 import freenet.support.api.Bucket
35 import freenet.support.api.BucketFactory
36 import freenet.support.api.HTTPRequest
37 import freenet.support.io.ArrayBucket
38 import net.pterodactylus.sone.test.deepMock
39 import net.pterodactylus.sone.test.mock
40 import net.pterodactylus.sone.test.whenever
41 import net.pterodactylus.util.web.Method
42 import net.pterodactylus.util.web.Page
43 import net.pterodactylus.util.web.Response
44 import org.hamcrest.MatcherAssert.assertThat
45 import org.hamcrest.Matchers.arrayContaining
46 import org.hamcrest.Matchers.equalTo
47 import org.hamcrest.Matchers.sameInstance
48 import org.junit.Test
49 import org.mockito.ArgumentMatchers.anyLong
50 import java.net.URI
51 import java.util.Date
52 import kotlin.text.Charsets.UTF_8
53
54 /**
55  * Unit test for PageToadletTest.
56  */
57 class PageToadletTest {
58
59         private val highLevelSimpleClient = mock<HighLevelSimpleClient>()
60         private val sessionManager = mock<SessionManager>()
61         private val httpRequest = mock<HTTPRequest>()
62         private val toadletContext = deepMock<ToadletContext>()
63
64         init {
65                 whenever(toadletContext.bucketFactory.makeBucket(anyLong())).then { ArrayBucket() }
66         }
67
68         @Test
69         fun `get request is forwarded to page correctly`() {
70                 var capturedRequest: FreenetRequest? = null
71                 val page = object : TestPage() {
72                         override fun handleRequest(request: FreenetRequest, response: Response) =
73                                         super.handleRequest(request, response)
74                                                         .also { capturedRequest = request }
75                 }
76                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
77                 pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
78                 assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
79                 assertThat(capturedRequest!!.method, equalTo(Method.GET))
80         }
81
82         @Test
83         fun `post request is forwarded to page correctly`() {
84                 var capturedRequest: FreenetRequest? = null
85                 val page = object : TestPage() {
86                         override fun handleRequest(request: FreenetRequest, response: Response) =
87                                         super.handleRequest(request, response)
88                                                         .also { capturedRequest = request }
89                 }
90                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
91                 pageToadlet.handleMethodPOST(URI("/test"), httpRequest, toadletContext)
92                 assertThat(capturedRequest!!.uri, equalTo(URI("/test")))
93                 assertThat(capturedRequest!!.method, equalTo(Method.POST))
94         }
95
96         @Test
97         fun `content written to response is written to context`() {
98                 val page = object : TestPage() {
99                         override fun handleRequest(request: FreenetRequest, response: Response) =
100                                         response.apply {
101                                                 statusCode = 123
102                                                 statusText = "Works"
103                                                 contentType = "data/test"
104                                                 addHeader("Test", "Value")
105                                                 addHeader("More", "true")
106                                                 addHeader("Test", "Another")
107                                                 write("Content")
108                                         }
109                 }
110                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
111                 var writtenData: ByteArray? = null
112                 var capturedReply: CapturedReply? = null
113                 val toadletContext = object : DelegatingToadletContext(this.toadletContext) {
114                         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) =
115                                         sendReplyHeaders(code, desc, mvt, mimeType, length, false)
116
117                         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) {
118                                 capturedReply = CapturedReply(code, desc, mvt, mimeType, length)
119                         }
120
121                         override fun writeData(data: ByteArray?, offset: Int, length: Int) {
122                                 writtenData = data!!.copyOfRange(offset, offset + length)
123                         }
124
125                         override fun writeData(data: ByteArray?) = writeData(data, 0, data!!.size)
126                         override fun writeData(data: Bucket?) = writeData(data!!.inputStream.readBytes())
127                 }
128                 pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext)
129                 assertThat(capturedReply!!.code, equalTo(123))
130                 assertThat(capturedReply!!.status, equalTo("Works"))
131                 assertThat(capturedReply!!.mimeType, equalTo("data/test"))
132                 assertThat(capturedReply!!.length, equalTo(7L))
133                 assertThat(capturedReply!!.headers!!.getArray("Test"), arrayContaining<Any>("Value", "Another"))
134                 assertThat(capturedReply!!.headers!!.getArray("More"), arrayContaining<Any>("true"))
135                 assertThat(writtenData!!.toString(UTF_8), equalTo("Content"))
136         }
137
138         @Test
139         fun `link-enabled is true for non-callback pages`() {
140                 val page = TestPage()
141                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
142                 assertThat(pageToadlet.isEnabled(toadletContext), equalTo(true))
143         }
144
145         @Test
146         fun `link-enabled is passed through for callback pages`() {
147                 var capturedToadletContext: ToadletContext? = null
148                 val page = object : TestPage(), LinkEnabledCallback {
149                         override fun isEnabled(ctx: ToadletContext?) = false.also { capturedToadletContext = toadletContext }
150                 }
151                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
152                 assertThat(pageToadlet.isEnabled(toadletContext), equalTo(false))
153                 assertThat(capturedToadletContext, sameInstance(toadletContext))
154         }
155
156         @Test
157         fun `link excemption is false for non-freenet pages`() {
158                 val page = TestPage()
159                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
160                 assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(false))
161         }
162
163         @Test
164         fun `link excemption is passed through for freenet pages`() {
165                 var capturedUri: URI? = null
166                 val page = object : TestPage(), FreenetPage {
167                         override fun isLinkExcepted(link: URI) = true.also { capturedUri = link }
168                 }
169                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
170                 assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(true))
171                 assertThat(capturedUri, equalTo(URI("/test")))
172         }
173
174         @Test
175         fun `path is created correctly from prefix and page path`() {
176                 val page = object : TestPage() {
177                         override fun getPath() = "test-path"
178                 }
179                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/")
180                 assertThat(pageToadlet.path(), equalTo("/path/test-path"))
181         }
182
183         @Test
184         fun `menu name is returned correctly`() {
185                 val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", TestPage(), "/path/")
186                 assertThat(pageToadlet.menuName, equalTo("MenuName"))
187         }
188
189 }
190
191 private data class CapturedReply(val code: Int, val status: String?, val headers: MultiValueTable<String, String>?, val mimeType: String?, val length: Long?)
192
193 private open class TestPage : Page<FreenetRequest> {
194         override fun getPath() = ""
195         override fun isPrefixPage() = false
196         override fun handleRequest(request: FreenetRequest, response: Response) = response
197 }
198
199 private open class DelegatingToadletContext(private val toadletContext: ToadletContext) : ToadletContext {
200         override fun activeToadlet(): Toadlet = toadletContext.activeToadlet()
201         override fun forceDisconnect() = toadletContext.forceDisconnect()
202         override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length)
203         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)
204         @Suppress("DEPRECATION")
205         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)
206         override fun getUri(): URI = toadletContext.uri
207         override fun getPageMaker(): PageMaker = toadletContext.pageMaker
208         override fun getBucketFactory(): BucketFactory = toadletContext.bucketFactory
209         override fun getHeaders(): MultiValueTable<String, String> = toadletContext.headers
210         override fun checkFullAccess(toadlet: Toadlet?): Boolean = toadletContext.checkFullAccess(toadlet)
211         override fun doRobots(): Boolean = toadletContext.doRobots()
212         override fun getReFilterPolicy(): REFILTER_POLICY = toadletContext.reFilterPolicy
213         override fun getAlertManager(): UserAlertManager = toadletContext.alertManager
214         override fun checkFormPassword(request: HTTPRequest?, redirectTo: String?): Boolean = toadletContext.checkFormPassword(request, redirectTo)
215         override fun checkFormPassword(request: HTTPRequest?): Boolean = toadletContext.checkFormPassword(request)
216         override fun addFormChild(parentNode: HTMLNode?, target: String?, id: String?): HTMLNode = toadletContext.addFormChild(parentNode, target, id)
217         override fun sendReplyHeadersFProxy(code: Int, desc: String?, mvt: MultiValueTable<String, String>?, mimeType: String?, length: Long) = toadletContext.sendReplyHeadersFProxy(code, desc, mvt, mimeType, length)
218         override fun setCookie(newCookie: Cookie?) = toadletContext.setCookie(newCookie)
219         override fun isAdvancedModeEnabled(): Boolean = toadletContext.isAdvancedModeEnabled
220         override fun disableProgressPage(): Boolean = toadletContext.disableProgressPage()
221         override fun writeData(data: ByteArray?, offset: Int, length: Int) = toadletContext.writeData(data, offset, length)
222         override fun writeData(data: ByteArray?) = toadletContext.writeData(data)
223         override fun writeData(data: Bucket?) = toadletContext.writeData(data)
224         override fun getCookie(domain: URI?, path: URI?, name: String?): ReceivedCookie? = toadletContext.getCookie(domain, path, name)
225         override fun getUniqueId(): String = toadletContext.uniqueId
226         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)
227         override fun getBookmarkManager(): BookmarkManager = toadletContext.bookmarkManager
228         override fun isAllowedFullAccess(): Boolean = toadletContext.isAllowedFullAccess
229         override fun hasFormPassword(request: HTTPRequest?): Boolean = toadletContext.hasFormPassword(request)
230         override fun getFormPassword(): String = toadletContext.formPassword
231         override fun getContainer(): ToadletContainer = toadletContext.container
232 }