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 SoneTemplatePage.menuName get() = javaClass.getAnnotation(MenuName::class.java)?.value
59 val httpRequest = mock<HTTPRequest>()
60 val freenetRequest = mock<FreenetRequest>()
62 whenever(freenetRequest.l10n).thenReturn(l10n)
63 whenever(freenetRequest.sessionManager).thenReturn(sessionManager)
64 whenever(freenetRequest.uri).thenReturn(mock())
66 val soneRequest by lazy { freenetRequest.toSoneRequest(core, webInterface) }
67 val templateContext = TemplateContext()
68 val toadletContext = deepMock<ToadletContext>()
69 val responseContent = ByteArrayOutputStream()
70 val response = Response(responseContent)
72 private val requestHeaders = mutableMapOf<String, String>()
73 private val getRequestParameters = mutableMapOf<String, MutableList<String>>()
74 private val postRequestParameters = mutableMapOf<String, ByteArray>()
75 private val uploadedFileNames = mutableMapOf<String, String>()
76 private val uploadedFileContentTypes = mutableMapOf<String, String>()
77 private val uploadedFileResources = mutableMapOf<String, String>()
78 private val ownIdentities = mutableSetOf<OwnIdentity>()
79 private val allSones = mutableMapOf<String, Sone>()
80 private val localSones = mutableMapOf<String, Sone>()
81 private val allPosts = mutableMapOf<String, Post>()
82 private val allPostReplies = mutableMapOf<String, PostReply>()
83 private val perPostReplies = mutableMapOf<String, PostReply>()
84 private val allAlbums = mutableMapOf<String, Album>()
85 private val allImages = mutableMapOf<String, Image>()
86 private val notifications = mutableMapOf<String, Notification>()
87 private val translations = mutableMapOf<String, String>()
97 private fun setupCore() {
98 whenever(core.preferences).thenReturn(preferences)
99 whenever(core.identityManager.allOwnIdentities).then { ownIdentities }
100 whenever(core.sones).then { allSones.values }
101 whenever(core.getSone(anyString())).then { allSones[it[0]] }
102 whenever(core.localSones).then { localSones.values }
103 whenever(core.getLocalSone(anyString())).then { localSones[it[0]] }
104 whenever(core.getPost(anyString())).then { allPosts[it[0]] }
105 whenever(core.getPostReply(anyString())).then { allPostReplies[it[0]] }
106 whenever(core.getReplies(anyString())).then { perPostReplies[it[0]].asList() }
107 whenever(core.getAlbum(anyString())).then { allAlbums[it[0]] }
108 whenever(core.getImage(anyString())).then { allImages[it[0]]}
109 whenever(core.getImage(anyString(), anyBoolean())).then { allImages[it[0]]}
110 whenever(core.getTemporaryImage(anyString())).thenReturn(null)
113 private fun setupWebInterface() {
114 whenever(webInterface.sessionManager).thenReturn(sessionManager)
115 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone)
116 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
117 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
118 whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
119 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
122 private fun setupHttpRequest() {
123 whenever(httpRequest.method).thenReturn("GET")
124 whenever(httpRequest.getHeader(anyString())).then { requestHeaders[it.get<String>(0).toLowerCase()] }
125 whenever(httpRequest.hasParameters()).then { getRequestParameters.isNotEmpty() }
126 whenever(httpRequest.parameterNames).then { getRequestParameters.keys }
127 whenever(httpRequest.isParameterSet(anyString())).then { it[0] in getRequestParameters }
128 whenever(httpRequest.getParam(anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: "" }
129 whenever(httpRequest.getParam(anyString(), anyString())).then { getRequestParameters[it[0]]?.firstOrNull() ?: it[1] }
130 whenever(httpRequest.getIntParam(anyString())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: 0 }
131 whenever(httpRequest.getIntParam(anyString(), anyInt())).then { getRequestParameters[it[0]]?.first()?.toIntOrNull() ?: it[1] }
132 whenever(httpRequest.getLongParam(anyString(), anyLong())).then { getRequestParameters[it[0]]?.first()?.toLongOrNull() ?: it[1] }
133 whenever(httpRequest.getMultipleParam(anyString())).then { getRequestParameters[it[0]]?.toTypedArray() ?: emptyArray<String>() }
134 whenever(httpRequest.getMultipleIntParam(anyString())).then { getRequestParameters[it[0]]?.map { it.toIntOrNull() ?: 0 } ?: emptyArray<Int>() }
135 whenever(httpRequest.isPartSet(anyString())).then { it[0] in postRequestParameters }
136 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).then { postRequestParameters[it[0]]?.decode()?.take(it[1]) ?: "" }
137 whenever(httpRequest.getUploadedFile(anyString())).then {
138 it.get<String>(0).takeIf { it in uploadedFileNames }
139 ?.let { name -> UploadedFile(uploadedFileNames[name]!!, uploadedFileContentTypes[name]!!, uploadedFileResources[name]!!)
144 private class UploadedFile(private val filename: String, private val contentType: String, private val resourceName: String): HTTPUploadedFile {
145 override fun getFilename() = filename
146 override fun getContentType() = contentType
147 override fun getData() = javaClass.getResourceAsStream(resourceName).readBytes().let(::SimpleReadOnlyArrayBucket)
150 private fun ByteArray.decode(charset: Charset = UTF_8) = String(this, charset)
152 private fun setupFreenetRequest() {
153 whenever(freenetRequest.method).thenReturn(GET)
154 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
155 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
158 private fun setupTranslations() {
159 whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
162 fun setMethod(method: Method) {
163 whenever(httpRequest.method).thenReturn(method.name)
164 whenever(freenetRequest.method).thenReturn(method)
167 fun request(uri: String) {
168 whenever(httpRequest.path).thenReturn(uri)
169 whenever(freenetRequest.uri).thenReturn(URI(uri))
172 fun addHttpRequestHeader(name: String, value: String) {
173 requestHeaders[name.toLowerCase()] = value
176 fun addHttpRequestParameter(name: String, value: String) {
177 getRequestParameters[name] = getRequestParameters.getOrElse(name) { mutableListOf() }.apply { add(value) }
180 fun addHttpRequestPart(name: String, value: String) {
181 postRequestParameters[name] = value.toByteArray(UTF_8)
184 fun unsetCurrentSone() {
185 whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null)
186 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
187 whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null)
190 fun addOwnIdentity(ownIdentity: OwnIdentity) {
191 ownIdentities += ownIdentity
194 fun addSone(id: String, sone: Sone) {
198 fun addLocalSone(id: String, localSone: Sone) {
199 localSones[id] = localSone
202 fun addPost(id: String, post: Post) {
206 fun addPostReply(id: String, postReply: PostReply) {
207 allPostReplies[id] = postReply
208 postReply.postId?.also { perPostReplies[it] = postReply }
211 fun addAlbum(id: String, album: Album) {
212 allAlbums[id] = album
215 fun addImage(id: String, image: Image) {
216 allImages[id] = image
219 fun addTranslation(key: String, value: String) {
220 translations[key] = value
223 fun addNotification(id: String, notification: Notification) {
224 notifications[id] = notification
227 fun addTemporaryImage(id: String, temporaryImage: TemporaryImage) {
228 whenever(core.getTemporaryImage(id)).thenReturn(temporaryImage)
231 fun addUploadedFile(name: String, filename: String, contentType: String, resource: String) {
232 uploadedFileNames[name] = filename
233 uploadedFileContentTypes[name] = contentType
234 uploadedFileResources[name] = resource
237 fun verifyNoRedirect(assertions: () -> Unit) {
238 var caughtException: Exception? = null
240 page.handleRequest(freenetRequest, templateContext)
241 } catch (e: Exception) {
244 caughtException?.run { throw this } ?: assertions()
247 fun verifyRedirect(target: String, assertions: () -> Unit = {}) {
249 page.handleRequest(freenetRequest, templateContext)
251 } catch (re: RedirectException) {
252 if (re.target != target) {
256 } catch (e: Exception) {