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