♻️ Use SoneRequest instead of FreenetRequest
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / web / pages / WebPageTest.kt
1 package net.pterodactylus.sone.web.pages
2
3 import com.google.common.eventbus.EventBus
4 import freenet.clients.http.ToadletContext
5 import freenet.support.SimpleReadOnlyArrayBucket
6 import freenet.support.api.HTTPRequest
7 import freenet.support.api.HTTPUploadedFile
8 import net.pterodactylus.sone.core.Preferences
9 import net.pterodactylus.sone.data.Album
10 import net.pterodactylus.sone.data.Image
11 import net.pterodactylus.sone.data.Post
12 import net.pterodactylus.sone.data.PostReply
13 import net.pterodactylus.sone.data.Sone
14 import net.pterodactylus.sone.data.TemporaryImage
15 import net.pterodactylus.sone.freenet.wot.OwnIdentity
16 import net.pterodactylus.sone.test.deepMock
17 import net.pterodactylus.sone.test.get
18 import net.pterodactylus.sone.test.mock
19 import net.pterodactylus.sone.test.whenever
20 import net.pterodactylus.sone.utils.asList
21 import net.pterodactylus.sone.utils.asOptional
22 import net.pterodactylus.sone.web.WebInterface
23 import net.pterodactylus.sone.web.page.*
24 import net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException
25 import net.pterodactylus.util.notify.Notification
26 import net.pterodactylus.util.template.Template
27 import net.pterodactylus.util.template.TemplateContext
28 import net.pterodactylus.util.web.Method
29 import net.pterodactylus.util.web.Method.GET
30 import net.pterodactylus.util.web.Response
31 import org.junit.Assert.fail
32 import org.mockito.ArgumentMatchers.anyBoolean
33 import org.mockito.ArgumentMatchers.anyInt
34 import org.mockito.ArgumentMatchers.anyLong
35 import org.mockito.ArgumentMatchers.anyString
36 import org.mockito.ArgumentMatchers.eq
37 import java.io.ByteArrayOutputStream
38 import java.net.URI
39 import java.nio.charset.Charset
40 import kotlin.text.Charsets.UTF_8
41
42 /**
43  * Base class for web page tests.
44  */
45 open class WebPageTest(pageSupplier: (Template, WebInterface) -> SoneTemplatePage = { _, _ -> mock() }) {
46
47         val currentSone = mock<Sone>()
48         val template = mock<Template>()
49         val webInterface = deepMock<WebInterface>()
50         val core = webInterface.core
51         val eventBus = mock<EventBus>()
52         val preferences = Preferences(eventBus)
53         val l10n = webInterface.l10n!!
54
55         val page by lazy { pageSupplier(template, webInterface) }
56         val httpRequest = mock<HTTPRequest>()
57         val freenetRequest = mock<FreenetRequest>()
58         init {
59                 whenever(freenetRequest.l10n).thenReturn(l10n)
60                 whenever(freenetRequest.uri).thenReturn(mock())
61         }
62         val soneRequest by lazy { freenetRequest.toSoneRequest(core, webInterface) }
63         val templateContext = TemplateContext()
64         val toadletContext = deepMock<ToadletContext>()
65         val responseContent = ByteArrayOutputStream()
66         val response = Response(responseContent)
67
68         private val requestHeaders = mutableMapOf<String, String>()
69         private val getRequestParameters = mutableMapOf<String, MutableList<String>>()
70         private val postRequestParameters = mutableMapOf<String, ByteArray>()
71         private val uploadedFileNames = mutableMapOf<String, String>()
72         private val uploadedFileContentTypes = mutableMapOf<String, String>()
73         private val uploadedFileResources = mutableMapOf<String, String>()
74         private val ownIdentities = mutableSetOf<OwnIdentity>()
75         private val allSones = mutableMapOf<String, Sone>()
76         private val localSones = mutableMapOf<String, Sone>()
77         private val allPosts = mutableMapOf<String, Post>()
78         private val allPostReplies = mutableMapOf<String, PostReply>()
79         private val perPostReplies = mutableMapOf<String, PostReply>()
80         private val allAlbums = mutableMapOf<String, Album>()
81         private val allImages = mutableMapOf<String, Image>()
82         private val notifications = mutableMapOf<String, Notification>()
83         private val translations = mutableMapOf<String, String>()
84
85         init {
86                 setupCore()
87                 setupWebInterface()
88                 setupHttpRequest()
89                 setupFreenetRequest()
90                 setupTranslations()
91         }
92
93         private fun setupCore() {
94                 whenever(core.preferences).thenReturn(preferences)
95                 whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
96                 whenever(core.sones).then { allSones.values }
97                 whenever(core.getSone(anyString())).then { allSones[it[0]] }
98                 whenever(core.localSones).then { localSones.values }
99                 whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
100                 whenever(core.getPost(anyString())).then { allPosts[it[0]] }
101                 whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
102                 whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
103                 whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
104                 whenever(core.getImage(anyString())).then { allImages[it[0]]}
105                 whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
106                 whenever(core.getTemporaryImage(anyString())).thenReturn(null)
107         }
108
109         private fun setupWebInterface() {
110                 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
111                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
112                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
113                 whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
114                 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
115         }
116
117         private fun setupHttpRequest() {
118                 whenever(httpRequest.method).thenReturn("GET")
119                 whenever(httpRequest.getHeader(anyString())).then { requestHeaders[it.get<String>(0).toLowerCase()] }
120                 whenever(httpRequest.hasParameters()).then { getRequestParameters.isNotEmpty() }
121                 whenever(httpRequest.parameterNames).then { getRequestParameters.keys }
122                 whenever(httpRequest.isParameterSet(anyString())).then { it[0] in getRequestParameters }
123                 whenever(httpRequest.getParam(anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: "" }
124                 whenever(httpRequest.getParam(anyString(), anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: it[1] }
125                 whenever(httpRequest.getIntParam(anyString())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: 0 }
126                 whenever(httpRequest.getIntParam(anyString(), anyInt())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: it[1] }
127                 whenever(httpRequest.getLongParam(anyString(), anyLong())).then { getRequestParameters[it[0]]?.first()?.toLongOrNull() ?: it[1] }
128                 whenever(httpRequest.getMultipleParam(anyString())).then { getRequestParameters[it[0]]?.toTypedArray() ?: emptyArray<String>() }
129                 whenever(httpRequest.getMultipleIntParam(anyString())).then { getRequestParameters[it[0]]?.map { it.toIntOrNull() ?: 0 } ?: emptyArray<Int>() }
130                 whenever(httpRequest.isPartSet(anyString())).then { it[0] in postRequestParameters }
131                 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
132                 whenever(httpRequest.getUploadedFile(anyString())).then {
133                         it.get<String>(0).takeIf { it in uploadedFileNames }
134                                         ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
135                         }
136                 }
137         }
138
139         private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
140                 override fun getFilename() = filename
141                 override fun getContentType() = contentType
142                 override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
143         }
144
145         private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
146
147         private fun setupFreenetRequest() {
148                 whenever(freenetRequest.method).thenReturn(GET)
149                 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
150                 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
151         }
152
153         private fun setupTranslations() {
154                 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
155         }
156
157         fun setMethod(method: Method) {
158                 whenever(httpRequest.method).thenReturn(method.name)
159                 whenever(freenetRequest.method).thenReturn(method)
160         }
161
162         fun request(uri: String) {
163                 whenever(httpRequest.path).thenReturn(uri)
164                 whenever(freenetRequest.uri).thenReturn(URI(uri))
165         }
166
167         fun addHttpRequestHeader(name: String, value: String) {
168                 requestHeaders[name.toLowerCase()] = value
169         }
170
171         fun addHttpRequestParameter(name: String, value: String) {
172                 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
173         }
174
175         fun addHttpRequestPart(name: String, value: String) {
176                 postRequestParameters[name] = value.toByteArray(UTF_8)
177         }
178
179         fun unsetCurrentSone() {
180                 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null)
181                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
182                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null)
183         }
184
185         fun addOwnIdentity(ownIdentity: OwnIdentity) {
186                 ownIdentities += ownIdentity
187         }
188
189         fun addSone(id: String, sone: Sone) {
190                 allSones[id] = sone
191         }
192
193         fun addLocalSone(id: String, localSone: Sone) {
194                 localSones[id] = localSone
195         }
196
197         fun addPost(id: String, post: Post) {
198                 allPosts[id] = post
199         }
200
201         fun addPostReply(id: String, postReply: PostReply) {
202                 allPostReplies[id] = postReply
203                 postReply.postId?.also { perPostReplies[it] = postReply }
204         }
205
206         fun addAlbum(id: String, album: Album) {
207                 allAlbums[id] = album
208         }
209
210         fun addImage(id: String, image: Image) {
211                 allImages[id] = image
212         }
213
214         fun addTranslation(key: String, value: String) {
215                 translations[key] = value
216         }
217
218         fun addNotification(id: String, notification: Notification) {
219                 notifications[id] = notification
220         }
221
222         fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
223                 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
224         }
225
226         fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
227                 uploadedFileNames[name] = filename
228                 uploadedFileContentTypes[name] = contentType
229                 uploadedFileResources[name] = resource
230         }
231
232         fun verifyNoRedirect(assertions: () -> Unit) {
233                 var caughtException: Exception? = null
234                 try {
235                         page.handleRequest(freenetRequest, templateContext)
236                 } catch (e: Exception) {
237                         caughtException = e
238                 }
239                 caughtException?.run { throw this } ?: assertions()
240         }
241
242         fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
243                 try {
244                         page.handleRequest(freenetRequest, templateContext)
245                         fail()
246                 } catch (re: RedirectException) {
247                         if (re.target != target) {
248                                 throw re
249                         }
250                         assertions()
251                 } catch (e: Exception) {
252                         throw e
253                 }
254         }
255
256 }