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