1 package net.pterodactylus.sone.web.pages
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.*
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
39 import java.nio.charset.Charset
40 import kotlin.text.Charsets.UTF_8
43 * Base class for web page tests.
45 open class WebPageTest(pageSupplier: (Template, WebInterface) -> SoneTemplatePage = { _, _ -> mock() }) {
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!!
55 val page by lazy { pageSupplier(template, webInterface) }
56 val httpRequest = mock<HTTPRequest>()
57 val freenetRequest = mock<FreenetRequest>()
59 whenever(freenetRequest.l10n).thenReturn(l10n)
60 whenever(freenetRequest.uri).thenReturn(mock())
62 val templateContext = TemplateContext()
63 val toadletContext = deepMock<ToadletContext>()
64 val responseContent = ByteArrayOutputStream()
65 val response = Response(responseContent)
67 private val requestHeaders = mutableMapOf<String, String>()
68 private val getRequestParameters = mutableMapOf<String, MutableList<String>>()
69 private val postRequestParameters = mutableMapOf<String, ByteArray>()
70 private val uploadedFileNames = mutableMapOf<String, String>()
71 private val uploadedFileContentTypes = mutableMapOf<String, String>()
72 private val uploadedFileResources = mutableMapOf<String, String>()
73 private val ownIdentities = mutableSetOf<OwnIdentity>()
74 private val allSones = mutableMapOf<String, Sone>()
75 private val localSones = mutableMapOf<String, Sone>()
76 private val allPosts = mutableMapOf<String, Post>()
77 private val allPostReplies = mutableMapOf<String, PostReply>()
78 private val perPostReplies = mutableMapOf<String, PostReply>()
79 private val allAlbums = mutableMapOf<String, Album>()
80 private val allImages = mutableMapOf<String, Image>()
81 private val notifications = mutableMapOf<String, Notification>()
82 private val translations = mutableMapOf<String, String>()
92 private fun setupCore() {
93 whenever(core.preferences).thenReturn(preferences)
94 whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
95 whenever(core.sones).then { allSones.values }
96 whenever(core.getSone(anyString())).then { allSones[it[0]] }
97 whenever(core.localSones).then { localSones.values }
98 whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
99 whenever(core.getPost(anyString())).then { allPosts[it[0]] }
100 whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
101 whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
102 whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
103 whenever(core.getImage(anyString())).then { allImages[it[0]]}
104 whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
105 whenever(core.getTemporaryImage(anyString())).thenReturn(null)
108 private fun setupWebInterface() {
109 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
110 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
111 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
112 whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
113 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
116 private fun setupHttpRequest() {
117 whenever(httpRequest.method).thenReturn("GET")
118 whenever(httpRequest.getHeader(anyString())).then { requestHeaders[it.get<String>(0).toLowerCase()] }
119 whenever(httpRequest.hasParameters()).then { getRequestParameters.isNotEmpty() }
120 whenever(httpRequest.parameterNames).then { getRequestParameters.keys }
121 whenever(httpRequest.isParameterSet(anyString())).then { it[0] in getRequestParameters }
122 whenever(httpRequest.getParam(anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: "" }
123 whenever(httpRequest.getParam(anyString(), anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: it[1] }
124 whenever(httpRequest.getIntParam(anyString())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: 0 }
125 whenever(httpRequest.getIntParam(anyString(), anyInt())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: it[1] }
126 whenever(httpRequest.getLongParam(anyString(), anyLong())).then { getRequestParameters[it[0]]?.first()?.toLongOrNull() ?: it[1] }
127 whenever(httpRequest.getMultipleParam(anyString())).then { getRequestParameters[it[0]]?.toTypedArray() ?: emptyArray<String>() }
128 whenever(httpRequest.getMultipleIntParam(anyString())).then { getRequestParameters[it[0]]?.map { it.toIntOrNull() ?: 0 } ?: emptyArray<Int>() }
129 whenever(httpRequest.isPartSet(anyString())).then { it[0] in postRequestParameters }
130 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
131 whenever(httpRequest.getUploadedFile(anyString())).then {
132 it.get<String>(0).takeIf { it in uploadedFileNames }
133 ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
138 private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
139 override fun getFilename() = filename
140 override fun getContentType() = contentType
141 override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
144 private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
146 private fun setupFreenetRequest() {
147 whenever(freenetRequest.method).thenReturn(GET)
148 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
149 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
152 private fun setupTranslations() {
153 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
156 fun setMethod(method: Method) {
157 whenever(httpRequest.method).thenReturn(method.name)
158 whenever(freenetRequest.method).thenReturn(method)
161 fun request(uri: String) {
162 whenever(httpRequest.path).thenReturn(uri)
163 whenever(freenetRequest.uri).thenReturn(URI(uri))
166 fun addHttpRequestHeader(name: String, value: String) {
167 requestHeaders[name.toLowerCase()] = value
170 fun addHttpRequestParameter(name: String, value: String) {
171 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
174 fun addHttpRequestPart(name: String, value: String) {
175 postRequestParameters[name] = value.toByteArray(UTF_8)
178 fun unsetCurrentSone() {
179 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null)
180 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
181 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null)
184 fun addOwnIdentity(ownIdentity: OwnIdentity) {
185 ownIdentities += ownIdentity
188 fun addSone(id: String, sone: Sone) {
192 fun addLocalSone(id: String, localSone: Sone) {
193 localSones[id] = localSone
196 fun addPost(id: String, post: Post) {
200 fun addPostReply(id: String, postReply: PostReply) {
201 allPostReplies[id] = postReply
202 postReply.postId?.also { perPostReplies[it] = postReply }
205 fun addAlbum(id: String, album: Album) {
206 allAlbums[id] = album
209 fun addImage(id: String, image: Image) {
210 allImages[id] = image
213 fun addTranslation(key: String, value: String) {
214 translations[key] = value
217 fun addNotification(id: String, notification: Notification) {
218 notifications[id] = notification
221 fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
222 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
225 fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
226 uploadedFileNames[name] = filename
227 uploadedFileContentTypes[name] = contentType
228 uploadedFileResources[name] = resource
231 fun verifyNoRedirect(assertions: () -> Unit) {
232 var caughtException: Exception? = null
234 page.handleRequest(freenetRequest, templateContext)
235 } catch (e: Exception) {
238 caughtException?.run { throw this } ?: assertions()
241 fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
243 page.handleRequest(freenetRequest, templateContext)
245 } catch (re: RedirectException) {
246 if (re.target != target) {
250 } catch (e: Exception) {