1 package net.pterodactylus.sone.web.pages
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
40 import java.nio.charset.Charset
41 import kotlin.text.Charsets.UTF_8
44 * Base class for web page tests.
46 open class WebPageTest(pageSupplier: (Template, WebInterface, Loaders) -> SoneTemplatePage = { _, _, _ -> mock() }) {
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>()
58 val page by lazy { pageSupplier(template, webInterface, loaders) }
60 val httpRequest = mock<HTTPRequest>()
61 val freenetRequest = mock<FreenetRequest>()
63 whenever(freenetRequest.l10n).thenReturn(l10n)
64 whenever(freenetRequest.sessionManager).thenReturn(sessionManager)
65 whenever(freenetRequest.uri).thenReturn(mock())
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)
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>()
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)
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() }
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]!!)
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)
151 private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
153 private fun setupFreenetRequest() {
154 whenever(freenetRequest.method).thenReturn(GET)
155 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
156 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
159 private fun setupTranslations() {
160 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
163 fun setMethod(method: Method) {
164 whenever(httpRequest.method).thenReturn(method.name)
165 whenever(freenetRequest.method).thenReturn(method)
168 fun request(uri: String) {
169 whenever(httpRequest.path).thenReturn(uri)
170 whenever(freenetRequest.uri).thenReturn(URI(uri))
173 fun addHttpRequestHeader(name: String, value: String) {
174 requestHeaders[name.toLowerCase()] = value
177 fun addHttpRequestParameter(name: String, value: String) {
178 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
181 fun addHttpRequestPart(name: String, value: String) {
182 postRequestParameters[name] = value.toByteArray(UTF_8)
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)
191 fun addOwnIdentity(ownIdentity: OwnIdentity) {
192 ownIdentities += ownIdentity
195 fun addSone(id: String, sone: Sone) {
199 fun addLocalSone(id: String, localSone: Sone) {
200 localSones[id] = localSone
203 fun addPost(id: String, post: Post) {
207 fun addPostReply(id: String, postReply: PostReply) {
208 allPostReplies[id] = postReply
209 postReply.postId?.also { perPostReplies[it] = postReply }
212 fun addAlbum(id: String, album: Album) {
213 allAlbums[id] = album
216 fun addImage(id: String, image: Image) {
217 allImages[id] = image
220 fun addTranslation(key: String, value: String) {
221 translations[key] = value
224 fun addNotification(id: String, notification: Notification) {
225 notifications[id] = notification
228 fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
229 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
232 fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
233 uploadedFileNames[name] = filename
234 uploadedFileContentTypes[name] = contentType
235 uploadedFileResources[name] = resource
238 fun verifyNoRedirect(assertions: () -> Unit) {
239 var caughtException: Exception? = null
241 page.handleRequest(freenetRequest, templateContext)
242 } catch (e: Exception) {
245 caughtException?.run { throw this } ?: assertions()
248 fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
250 page.handleRequest(freenetRequest, templateContext)
252 } catch (re: RedirectException) {
253 if (re.target != target) {
257 } catch (e: Exception) {