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