♻️ Add L10n to FreenetRequest, WebInterface to SoneRequest
[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 templateContext = TemplateContext()
63         val toadletContext = deepMock<ToadletContext>()
64         val responseContent = ByteArrayOutputStream()
65         val response = Response(responseContent)
66
67         private val requestHeaders = mutableMapOf<String, String>()
68         private val getRequestParameters = mutableMapOf<String, MutableList<String>>()
69         private val postRequestParameters = mutableMapOf<String, ByteArray>()
70         private val uploadedFileNames = mutableMapOf<String, String>()
71         private val uploadedFileContentTypes = mutableMapOf<String, String>()
72         private val uploadedFileResources = mutableMapOf<String, String>()
73         private val ownIdentities = mutableSetOf<OwnIdentity>()
74         private val allSones = mutableMapOf<String, Sone>()
75         private val localSones = mutableMapOf<String, Sone>()
76         private val allPosts = mutableMapOf<String, Post>()
77         private val allPostReplies = mutableMapOf<String, PostReply>()
78         private val perPostReplies = mutableMapOf<String, PostReply>()
79         private val allAlbums = mutableMapOf<String, Album>()
80         private val allImages = mutableMapOf<String, Image>()
81         private val notifications = mutableMapOf<String, Notification>()
82         private val translations = mutableMapOf<String, String>()
83
84         init {
85                 setupCore()
86                 setupWebInterface()
87                 setupHttpRequest()
88                 setupFreenetRequest()
89                 setupTranslations()
90         }
91
92         private fun setupCore() {
93                 whenever(core.preferences).thenReturn(preferences)
94                 whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
95                 whenever(core.sones).then { allSones.values }
96                 whenever(core.getSone(anyString())).then { allSones[it[0]] }
97                 whenever(core.localSones).then { localSones.values }
98                 whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
99                 whenever(core.getPost(anyString())).then { allPosts[it[0]] }
100                 whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
101                 whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
102                 whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
103                 whenever(core.getImage(anyString())).then { allImages[it[0]]}
104                 whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
105                 whenever(core.getTemporaryImage(anyString())).thenReturn(null)
106         }
107
108         private fun setupWebInterface() {
109                 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
110                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
111                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
112                 whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
113                 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
114         }
115
116         private fun setupHttpRequest() {
117                 whenever(httpRequest.method).thenReturn("GET")
118                 whenever(httpRequest.getHeader(anyString())).then { requestHeaders[it.get<String>(0).toLowerCase()] }
119                 whenever(httpRequest.hasParameters()).then { getRequestParameters.isNotEmpty() }
120                 whenever(httpRequest.parameterNames).then { getRequestParameters.keys }
121                 whenever(httpRequest.isParameterSet(anyString())).then { it[0] in getRequestParameters }
122                 whenever(httpRequest.getParam(anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: "" }
123                 whenever(httpRequest.getParam(anyString(), anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: it[1] }
124                 whenever(httpRequest.getIntParam(anyString())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: 0 }
125                 whenever(httpRequest.getIntParam(anyString(), anyInt())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: it[1] }
126                 whenever(httpRequest.getLongParam(anyString(), anyLong())).then { getRequestParameters[it[0]]?.first()?.toLongOrNull() ?: it[1] }
127                 whenever(httpRequest.getMultipleParam(anyString())).then { getRequestParameters[it[0]]?.toTypedArray() ?: emptyArray<String>() }
128                 whenever(httpRequest.getMultipleIntParam(anyString())).then { getRequestParameters[it[0]]?.map { it.toIntOrNull() ?: 0 } ?: emptyArray<Int>() }
129                 whenever(httpRequest.isPartSet(anyString())).then { it[0] in postRequestParameters }
130                 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
131                 whenever(httpRequest.getUploadedFile(anyString())).then {
132                         it.get<String>(0).takeIf { it in uploadedFileNames }
133                                         ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
134                         }
135                 }
136         }
137
138         private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
139                 override fun getFilename() = filename
140                 override fun getContentType() = contentType
141                 override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
142         }
143
144         private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
145
146         private fun setupFreenetRequest() {
147                 whenever(freenetRequest.method).thenReturn(GET)
148                 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
149                 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
150         }
151
152         private fun setupTranslations() {
153                 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
154         }
155
156         fun setMethod(method: Method) {
157                 whenever(httpRequest.method).thenReturn(method.name)
158                 whenever(freenetRequest.method).thenReturn(method)
159         }
160
161         fun request(uri: String) {
162                 whenever(httpRequest.path).thenReturn(uri)
163                 whenever(freenetRequest.uri).thenReturn(URI(uri))
164         }
165
166         fun addHttpRequestHeader(name: String, value: String) {
167                 requestHeaders[name.toLowerCase()] = value
168         }
169
170         fun addHttpRequestParameter(name: String, value: String) {
171                 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
172         }
173
174         fun addHttpRequestPart(name: String, value: String) {
175                 postRequestParameters[name] = value.toByteArray(UTF_8)
176         }
177
178         fun unsetCurrentSone() {
179                 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null)
180                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
181                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null)
182         }
183
184         fun addOwnIdentity(ownIdentity: OwnIdentity) {
185                 ownIdentities += ownIdentity
186         }
187
188         fun addSone(id: String, sone: Sone) {
189                 allSones[id] = sone
190         }
191
192         fun addLocalSone(id: String, localSone: Sone) {
193                 localSones[id] = localSone
194         }
195
196         fun addPost(id: String, post: Post) {
197                 allPosts[id] = post
198         }
199
200         fun addPostReply(id: String, postReply: PostReply) {
201                 allPostReplies[id] = postReply
202                 postReply.postId?.also { perPostReplies[it] = postReply }
203         }
204
205         fun addAlbum(id: String, album: Album) {
206                 allAlbums[id] = album
207         }
208
209         fun addImage(id: String, image: Image) {
210                 allImages[id] = image
211         }
212
213         fun addTranslation(key: String, value: String) {
214                 translations[key] = value
215         }
216
217         fun addNotification(id: String, notification: Notification) {
218                 notifications[id] = notification
219         }
220
221         fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
222                 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
223         }
224
225         fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
226                 uploadedFileNames[name] = filename
227                 uploadedFileContentTypes[name] = contentType
228                 uploadedFileResources[name] = resource
229         }
230
231         fun verifyNoRedirect(assertions: () -> Unit) {
232                 var caughtException: Exception? = null
233                 try {
234                         page.handleRequest(freenetRequest, templateContext)
235                 } catch (e: Exception) {
236                         caughtException = e
237                 }
238                 caughtException?.run { throw this } ?: assertions()
239         }
240
241         fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
242                 try {
243                         page.handleRequest(freenetRequest, templateContext)
244                         fail()
245                 } catch (re: RedirectException) {
246                         if (re.target != target) {
247                                 throw re
248                         }
249                         assertions()
250                 } catch (e: Exception) {
251                         throw e
252                 }
253         }
254
255 }