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