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