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