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