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.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!!
54 val sessionManager = mock<SessionManager>()
56 val page by lazy { pageSupplier(template, webInterface) }
58 val httpRequest = mock<HTTPRequest>()
59 val freenetRequest = mock<FreenetRequest>()
61 whenever(freenetRequest.l10n).thenReturn(l10n)
62 whenever(freenetRequest.sessionManager).thenReturn(sessionManager)
63 whenever(freenetRequest.uri).thenReturn(mock())
65 val soneRequest by lazy { freenetRequest.toSoneRequest(core, webInterface) }
66 val templateContext = TemplateContext()
67 val toadletContext = deepMock<ToadletContext>()
68 val responseContent = ByteArrayOutputStream()
69 val response = Response(responseContent)
71 private val requestHeaders = mutableMapOf<String, String>()
72 private val getRequestParameters = mutableMapOf<String, MutableList<String>>()
73 private val postRequestParameters = mutableMapOf<String, ByteArray>()
74 private val uploadedFileNames = mutableMapOf<String, String>()
75 private val uploadedFileContentTypes = mutableMapOf<String, String>()
76 private val uploadedFileResources = mutableMapOf<String, String>()
77 private val ownIdentities = mutableSetOf<OwnIdentity>()
78 private val allSones = mutableMapOf<String, Sone>()
79 private val localSones = mutableMapOf<String, Sone>()
80 private val allPosts = mutableMapOf<String, Post>()
81 private val allPostReplies = mutableMapOf<String, PostReply>()
82 private val perPostReplies = mutableMapOf<String, PostReply>()
83 private val allAlbums = mutableMapOf<String, Album>()
84 private val allImages = mutableMapOf<String, Image>()
85 private val notifications = mutableMapOf<String, Notification>()
86 private val translations = mutableMapOf<String, String>()
96 private fun setupCore() {
97 whenever(core.preferences).thenReturn(preferences)
98 whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
99 whenever(core.sones).then { allSones.values }
100 whenever(core.getSone(anyString())).then { allSones[it[0]] }
101 whenever(core.localSones).then { localSones.values }
102 whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
103 whenever(core.getPost(anyString())).then { allPosts[it[0]] }
104 whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
105 whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
106 whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
107 whenever(core.getImage(anyString())).then { allImages[it[0]]}
108 whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
109 whenever(core.getTemporaryImage(anyString())).thenReturn(null)
112 private fun setupWebInterface() {
113 whenever(webInterface.sessionManager).thenReturn(sessionManager)
114 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
115 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
116 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
117 whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
118 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
121 private fun setupHttpRequest() {
122 whenever(httpRequest.method).thenReturn("GET")
123 whenever(httpRequest.getHeader(anyString())).then { requestHeaders[it.get<String>(0).toLowerCase()] }
124 whenever(httpRequest.hasParameters()).then { getRequestParameters.isNotEmpty() }
125 whenever(httpRequest.parameterNames).then { getRequestParameters.keys }
126 whenever(httpRequest.isParameterSet(anyString())).then { it[0] in getRequestParameters }
127 whenever(httpRequest.getParam(anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: "" }
128 whenever(httpRequest.getParam(anyString(), anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: it[1] }
129 whenever(httpRequest.getIntParam(anyString())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: 0 }
130 whenever(httpRequest.getIntParam(anyString(), anyInt())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: it[1] }
131 whenever(httpRequest.getLongParam(anyString(), anyLong())).then { getRequestParameters[it[0]]?.first()?.toLongOrNull() ?: it[1] }
132 whenever(httpRequest.getMultipleParam(anyString())).then { getRequestParameters[it[0]]?.toTypedArray() ?: emptyArray<String>() }
133 whenever(httpRequest.getMultipleIntParam(anyString())).then { getRequestParameters[it[0]]?.map { it.toIntOrNull() ?: 0 } ?: emptyArray<Int>() }
134 whenever(httpRequest.isPartSet(anyString())).then { it[0] in postRequestParameters }
135 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
136 whenever(httpRequest.getUploadedFile(anyString())).then {
137 it.get<String>(0).takeIf { it in uploadedFileNames }
138 ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
143 private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
144 override fun getFilename() = filename
145 override fun getContentType() = contentType
146 override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
149 private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
151 private fun setupFreenetRequest() {
152 whenever(freenetRequest.method).thenReturn(GET)
153 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
154 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
157 private fun setupTranslations() {
158 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
161 fun setMethod(method: Method) {
162 whenever(httpRequest.method).thenReturn(method.name)
163 whenever(freenetRequest.method).thenReturn(method)
166 fun request(uri: String) {
167 whenever(httpRequest.path).thenReturn(uri)
168 whenever(freenetRequest.uri).thenReturn(URI(uri))
171 fun addHttpRequestHeader(name: String, value: String) {
172 requestHeaders[name.toLowerCase()] = value
175 fun addHttpRequestParameter(name: String, value: String) {
176 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
179 fun addHttpRequestPart(name: String, value: String) {
180 postRequestParameters[name] = value.toByteArray(UTF_8)
183 fun unsetCurrentSone() {
184 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null)
185 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
186 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null)
189 fun addOwnIdentity(ownIdentity: OwnIdentity) {
190 ownIdentities += ownIdentity
193 fun addSone(id: String, sone: Sone) {
197 fun addLocalSone(id: String, localSone: Sone) {
198 localSones[id] = localSone
201 fun addPost(id: String, post: Post) {
205 fun addPostReply(id: String, postReply: PostReply) {
206 allPostReplies[id] = postReply
207 postReply.postId?.also { perPostReplies[it] = postReply }
210 fun addAlbum(id: String, album: Album) {
211 allAlbums[id] = album
214 fun addImage(id: String, image: Image) {
215 allImages[id] = image
218 fun addTranslation(key: String, value: String) {
219 translations[key] = value
222 fun addNotification(id: String, notification: Notification) {
223 notifications[id] = notification
226 fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
227 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
230 fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
231 uploadedFileNames[name] = filename
232 uploadedFileContentTypes[name] = contentType
233 uploadedFileResources[name] = resource
236 fun verifyNoRedirect(assertions: () -> Unit) {
237 var caughtException: Exception? = null
239 page.handleRequest(freenetRequest, templateContext)
240 } catch (e: Exception) {
243 caughtException?.run { throw this } ?: assertions()
246 fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
248 page.handleRequest(freenetRequest, templateContext)
250 } catch (re: RedirectException) {
251 if (re.target != target) {
255 } catch (e: Exception) {