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