1 package net.pterodactylus.sone.web.ajax
3 import com.google.common.eventbus.EventBus
4 import freenet.clients.http.ToadletContext
5 import freenet.l10n.BaseL10n
6 import freenet.support.SimpleReadOnlyArrayBucket
7 import freenet.support.api.HTTPRequest
8 import net.pterodactylus.sone.core.Core
9 import net.pterodactylus.sone.core.ElementLoader
10 import net.pterodactylus.sone.core.LinkedElement
11 import net.pterodactylus.sone.core.Preferences
12 import net.pterodactylus.sone.core.UpdateChecker
13 import net.pterodactylus.sone.data.Album
14 import net.pterodactylus.sone.data.Image
15 import net.pterodactylus.sone.data.Post
16 import net.pterodactylus.sone.data.PostReply
17 import net.pterodactylus.sone.data.Profile
18 import net.pterodactylus.sone.data.Sone
19 import net.pterodactylus.sone.data.Sone.SoneStatus
20 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
21 import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions
22 import net.pterodactylus.sone.test.deepMock
23 import net.pterodactylus.sone.test.get
24 import net.pterodactylus.sone.test.mock
25 import net.pterodactylus.sone.test.whenever
26 import net.pterodactylus.sone.utils.asOptional
27 import net.pterodactylus.sone.web.WebInterface
28 import net.pterodactylus.sone.web.page.FreenetRequest
29 import net.pterodactylus.util.notify.Notification
30 import net.pterodactylus.util.template.TemplateContextFactory
31 import net.pterodactylus.util.web.Method.GET
32 import org.hamcrest.MatcherAssert.assertThat
33 import org.hamcrest.Matchers.equalTo
34 import org.junit.Before
36 import org.mockito.ArgumentMatchers.any
37 import org.mockito.ArgumentMatchers.anyBoolean
38 import org.mockito.ArgumentMatchers.anyInt
39 import org.mockito.ArgumentMatchers.anyString
40 import org.mockito.ArgumentMatchers.eq
41 import org.mockito.ArgumentMatchers.isNull
42 import java.util.NoSuchElementException
43 import javax.naming.SizeLimitExceededException
46 * Base class for tests for any [JsonPage] implementations.
48 abstract class JsonPageTest(
49 private val expectedPath: String,
50 private val requiresLogin: Boolean = true,
51 private val needsFormPassword: Boolean = true,
52 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
54 protected val webInterface = mock<WebInterface>()
55 protected val l10n = mock<BaseL10n>()
56 protected val core = mock<Core>()
57 protected val eventBus = mock<EventBus>()
58 protected val preferences = Preferences(eventBus)
59 protected val updateChecker = mock<UpdateChecker>()
60 protected val elementLoader = mock<ElementLoader>()
61 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
62 protected val json by lazy { page.createJsonObject(freenetRequest) }
64 protected val toadletContext = mock<ToadletContext>()
65 protected val freenetRequest = mock<FreenetRequest>()
66 protected val httpRequest = mock<HTTPRequest>()
67 protected val currentSone = deepMock<Sone>()
68 protected val profile = Profile(currentSone)
70 private val requestHeaders = mutableMapOf<String, String>()
71 private val requestParameters = mutableMapOf<String, String>()
72 private val requestParts = mutableMapOf<String, String>()
73 private val localSones = mutableMapOf<String, Sone>()
74 private val remoteSones = mutableMapOf<String, Sone>()
75 private val posts = mutableMapOf<String, Post>()
76 private val postLikes = mutableMapOf<Post, Set<Sone>>()
77 private val newPosts = mutableMapOf<String, Post>()
78 private val replies = mutableMapOf<String, PostReply>()
79 private val replyLikes = mutableMapOf<PostReply, Set<Sone>>()
80 private val newReplies = mutableMapOf<String, PostReply>()
81 private val linkedElements = mutableMapOf<String, LinkedElement>()
82 private val notifications = mutableMapOf<String, Notification>()
83 private val albums = mutableMapOf<String, Album>()
84 private val images = mutableMapOf<String, Image>()
85 private val translations = mutableMapOf<String, String>()
88 fun setupWebInterface() {
89 whenever(webInterface.templateContextFactory).thenReturn(TemplateContextFactory())
90 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
91 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
92 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
93 whenever(webInterface.core).thenReturn(core)
94 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
95 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
96 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
97 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
98 whenever(webInterface.l10n).thenReturn(l10n)
102 fun setupTranslations() {
103 whenever(l10n.getString(anyString())).then { translations[it[0]] }
108 whenever(core.preferences).thenReturn(preferences)
109 whenever(core.updateChecker).thenReturn(updateChecker)
110 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
111 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
112 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
113 whenever(core.getLikes(any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
114 whenever(core.getLikes(any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
115 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
116 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
117 whenever(core.getImage(anyString())).then { images[it[0]] }
118 whenever(core.getImage(anyString(), anyBoolean())).then { images[it[0]] }
122 fun setupElementLoader() {
123 whenever(elementLoader.loadElement(anyString())).thenAnswer {
124 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
129 fun setupCurrentSone() {
130 whenever(currentSone.options).thenReturn(DefaultSoneOptions())
131 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
135 fun setupFreenetRequest() {
136 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
137 whenever(freenetRequest.method).thenReturn(GET)
138 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
142 fun setupHttpRequest() {
143 whenever(httpRequest.method).thenReturn("GET")
144 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
145 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
146 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
147 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
148 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
149 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
150 whenever(httpRequest.getPartAsBytesThrowing(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.let { it.toByteArray().let { if (it.size > invocation.getArgument<Int>(1)) throw SizeLimitExceededException() else it } } ?: throw NoSuchElementException() }
151 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
152 whenever(httpRequest.getPartAsStringThrowing(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.let { if (it.length > invocation.getArgument<Int>(1)) throw SizeLimitExceededException() else it } ?: throw NoSuchElementException() }
153 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
154 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
159 whenever(currentSone.profile).thenReturn(profile)
162 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
164 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
165 whenever(this.id).thenReturn(id)
166 whenever(this.name).thenReturn(name)
167 whenever(isLocal).thenReturn(local)
168 whenever(this.time).thenReturn(time)
169 whenever(this.status).thenReturn(status)
172 protected fun unsetCurrentSone() {
173 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
174 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
175 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
178 protected fun addRequestHeader(key: String, value: String) {
179 requestHeaders += key.toLowerCase() to value
182 protected fun addRequestParameter(key: String, value: String) {
183 requestParameters += key to value
186 protected fun addRequestPart(key: String, value: String) {
187 requestParts += key to value
190 protected fun addNotification(notification: Notification, notificationId: String? = null) {
191 notifications[notificationId ?: notification.id] = notification
194 protected fun addSone(sone: Sone, soneId: String? = null) {
195 remoteSones += (soneId ?: sone.id) to sone
198 protected fun addLocalSone(id: String, sone: Sone) {
199 localSones += id to sone
202 protected fun addPost(post: Post, id: String? = null) {
203 posts[id ?: post.id] = post
206 protected fun addLikes(post: Post, vararg sones: Sone) {
207 postLikes[post] = setOf(*sones)
210 protected fun addLikes(reply: PostReply, vararg sones: Sone) {
211 replyLikes[reply] = setOf(*sones)
214 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
216 whenever(this.id).thenReturn(id)
217 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
218 whenever(this.sone).thenReturn(sone)
219 whenever(this.time).thenReturn(time)
220 whenever(this.recipientId).thenReturn(recipientId.asOptional())
221 }.also { newPosts[id] = it }
223 protected fun addReply(reply: PostReply, id: String? = null) {
224 replies[id ?: reply.id] = reply
227 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
228 newReplies[id] = mock<PostReply>().apply {
229 whenever(this.id).thenReturn(id)
230 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
231 whenever(this.sone).thenReturn(sone)
232 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
233 val post = mock<Post>().apply {
234 whenever(this.sone).thenReturn(postSone)
236 whenever(this.post).thenReturn(post.asOptional())
237 whenever(this.postId).thenReturn(postId)
241 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
242 linkedElements[link] = LinkedElement(link, failed, loading)
245 protected fun addAlbum(album: Album, albumId: String? = null) {
246 albums[albumId ?: album.id] = album
249 protected fun addImage(image: Image, imageId: String? = null) {
250 images[imageId ?: image.id] = image
253 protected fun addTranslation(key: String, value: String) {
254 translations[key] = value
258 fun `page returns correct path`() {
259 assertThat(page.path, equalTo(expectedPath))
263 fun `page needs form password`() {
264 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
268 fun `page requires login`() {
269 assertThat(page.requiresLogin(), equalTo(requiresLogin))