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.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
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<SoneTemplatePage>() }) {
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>()
58 val templateContext = TemplateContext()
59 val toadletContext = deepMock<ToadletContext>()
60 val responseContent = ByteArrayOutputStream()
61 val response = Response(responseContent)
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>()
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)
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() }
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]!!)
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)
140 private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
142 private fun setupFreenetRequest() {
143 whenever(freenetRequest.method).thenReturn(GET)
144 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
145 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
148 private fun setupTranslations() {
149 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
152 fun setMethod(method: Method) {
153 whenever(httpRequest.method).thenReturn(method.name)
154 whenever(freenetRequest.method).thenReturn(method)
157 fun request(uri: String) {
158 whenever(httpRequest.path).thenReturn(uri)
159 whenever(freenetRequest.uri).thenReturn(URI(uri))
162 fun addHttpRequestHeader(name: String, value: String) {
163 requestHeaders[name.toLowerCase()] = value
166 fun addHttpRequestParameter(name: String, value: String) {
167 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf<String>() }.apply { add(value) }
170 fun addHttpRequestPart(name: String, value: String) {
171 postRequestParameters[name] = value.toByteArray(UTF_8)
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)
180 fun addOwnIdentity(ownIdentity: OwnIdentity) {
181 ownIdentities += ownIdentity
184 fun addSone(id: String, sone: Sone) {
188 fun addLocalSone(id: String, localSone: Sone) {
189 localSones[id] = localSone
192 fun addPost(id: String, post: Post) {
196 fun addPostReply(id: String, postReply: PostReply) {
197 allPostReplies[id] = postReply
198 postReply.postId?.also { perPostReplies[it] = postReply }
201 fun addAlbum(id: String, album: Album) {
202 allAlbums[id] = album
205 fun addImage(id: String, image: Image) {
206 allImages[id] = image
209 fun addTranslation(key: String, value: String) {
210 translations[key] = value
213 fun addNotification(id: String, notification: Notification) {
214 notifications[id] = notification
217 fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
218 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
221 fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
222 uploadedFileNames[name] = filename
223 uploadedFileContentTypes[name] = contentType
224 uploadedFileResources[name] = resource
227 fun verifyNoRedirect(assertions: () -> Unit) {
228 var caughtException: Exception? = null
230 page.handleRequest(freenetRequest, templateContext)
231 } catch (e: Exception) {
234 caughtException?.run { throw this } ?: assertions()
237 fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
239 page.handleRequest(freenetRequest, templateContext)
241 } catch (re: RedirectException) {
242 if (re.target != target) {
246 } catch (e: Exception) {