1 package net.pterodactylus.sone.web.ajax
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 net.pterodactylus.sone.core.Core
8 import net.pterodactylus.sone.core.ElementLoader
9 import net.pterodactylus.sone.core.LinkedElement
10 import net.pterodactylus.sone.core.Preferences
11 import net.pterodactylus.sone.core.UpdateChecker
12 import net.pterodactylus.sone.data.Album
13 import net.pterodactylus.sone.data.Image
14 import net.pterodactylus.sone.data.Post
15 import net.pterodactylus.sone.data.PostReply
16 import net.pterodactylus.sone.data.Profile
17 import net.pterodactylus.sone.data.Sone
18 import net.pterodactylus.sone.data.Sone.SoneStatus
19 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
20 import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions
21 import net.pterodactylus.sone.test.deepMock
22 import net.pterodactylus.sone.test.get
23 import net.pterodactylus.sone.test.mock
24 import net.pterodactylus.sone.test.whenever
25 import net.pterodactylus.sone.utils.asOptional
26 import net.pterodactylus.sone.web.WebInterface
27 import net.pterodactylus.sone.web.page.FreenetRequest
28 import net.pterodactylus.util.notify.Notification
29 import net.pterodactylus.util.template.TemplateContextFactory
30 import net.pterodactylus.util.web.Method.GET
31 import org.hamcrest.MatcherAssert.assertThat
32 import org.hamcrest.Matchers.equalTo
33 import org.junit.Before
35 import org.mockito.ArgumentMatchers.any
36 import org.mockito.ArgumentMatchers.anyBoolean
37 import org.mockito.ArgumentMatchers.anyInt
38 import org.mockito.ArgumentMatchers.anyString
39 import org.mockito.ArgumentMatchers.eq
40 import org.mockito.ArgumentMatchers.isNull
41 import java.util.NoSuchElementException
42 import javax.naming.SizeLimitExceededException
45 * Base class for tests for any [JsonPage] implementations.
47 abstract class JsonPageTest(
48 private val expectedPath: String,
49 private val requiresLogin: Boolean = true,
50 private val needsFormPassword: Boolean = true,
51 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
53 protected val webInterface = mock<WebInterface>()
54 protected val core = mock<Core>()
55 protected val eventBus = mock<EventBus>()
56 protected val preferences = Preferences(eventBus)
57 protected val updateChecker = mock<UpdateChecker>()
58 protected val elementLoader = mock<ElementLoader>()
59 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
60 protected val json by lazy { page.createJsonObject(freenetRequest) }
62 protected val toadletContext = mock<ToadletContext>()
63 protected val freenetRequest = mock<FreenetRequest>()
64 protected val httpRequest = mock<HTTPRequest>()
65 protected val currentSone = deepMock<Sone>()
66 protected val profile = Profile(currentSone)
68 private val requestHeaders = mutableMapOf<String, String>()
69 private val requestParameters = mutableMapOf<String, String>()
70 private val requestParts = mutableMapOf<String, String>()
71 private val localSones = mutableMapOf<String, Sone>()
72 private val remoteSones = mutableMapOf<String, Sone>()
73 private val posts = mutableMapOf<String, Post>()
74 private val postLikes = mutableMapOf<Post, Set<Sone>>()
75 private val newPosts = mutableMapOf<String, Post>()
76 private val replies = mutableMapOf<String, PostReply>()
77 private val replyLikes = mutableMapOf<PostReply, Set<Sone>>()
78 private val newReplies = mutableMapOf<String, PostReply>()
79 private val linkedElements = mutableMapOf<String, LinkedElement>()
80 private val notifications = mutableMapOf<String, Notification>()
81 private val albums = mutableMapOf<String, Album>()
82 private val images = mutableMapOf<String, Image>()
85 fun setupWebInterface() {
86 whenever(webInterface.templateContextFactory).thenReturn(TemplateContextFactory())
87 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
88 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
89 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
90 whenever(webInterface.core).thenReturn(core)
91 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
92 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
93 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
94 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
99 whenever(core.preferences).thenReturn(preferences)
100 whenever(core.updateChecker).thenReturn(updateChecker)
101 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
102 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
103 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
104 whenever(core.getLikes(any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
105 whenever(core.getLikes(any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
106 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
107 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
108 whenever(core.getImage(anyString())).then { images[it[0]] }
109 whenever(core.getImage(anyString(), anyBoolean())).then { images[it[0]] }
113 fun setupElementLoader() {
114 whenever(elementLoader.loadElement(anyString())).thenAnswer {
115 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
120 fun setupCurrentSone() {
121 whenever(currentSone.options).thenReturn(DefaultSoneOptions())
122 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
126 fun setupFreenetRequest() {
127 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
128 whenever(freenetRequest.method).thenReturn(GET)
129 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
133 fun setupHttpRequest() {
134 whenever(httpRequest.method).thenReturn("GET")
135 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
136 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
137 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
138 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
139 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
140 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
141 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() }
142 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
143 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() }
144 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
145 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
150 whenever(currentSone.profile).thenReturn(profile)
153 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
155 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
156 whenever(this.id).thenReturn(id)
157 whenever(this.name).thenReturn(name)
158 whenever(isLocal).thenReturn(local)
159 whenever(this.time).thenReturn(time)
160 whenever(this.status).thenReturn(status)
163 protected fun unsetCurrentSone() {
164 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
165 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
166 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
169 protected fun addRequestHeader(key: String, value: String) {
170 requestHeaders += key.toLowerCase() to value
173 protected fun addRequestParameter(key: String, value: String) {
174 requestParameters += key to value
177 protected fun addRequestPart(key: String, value: String) {
178 requestParts += key to value
181 protected fun addNotification(notification: Notification, notificationId: String? = null) {
182 notifications[notificationId ?: notification.id] = notification
185 protected fun addSone(sone: Sone, soneId: String? = null) {
186 remoteSones += (soneId ?: sone.id) to sone
189 protected fun addLocalSone(id: String, sone: Sone) {
190 localSones += id to sone
193 protected fun addPost(post: Post, id: String? = null) {
194 posts[id ?: post.id] = post
197 protected fun addLikes(post: Post, vararg sones: Sone) {
198 postLikes[post] = setOf(*sones)
201 protected fun addLikes(reply: PostReply, vararg sones: Sone) {
202 replyLikes[reply] = setOf(*sones)
205 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
207 whenever(this.id).thenReturn(id)
208 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
209 whenever(this.sone).thenReturn(sone)
210 whenever(this.time).thenReturn(time)
211 whenever(this.recipientId).thenReturn(recipientId.asOptional())
212 }.also { newPosts[id] = it }
214 protected fun addReply(reply: PostReply, id: String? = null) {
215 replies[id ?: reply.id] = reply
218 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
219 newReplies[id] = mock<PostReply>().apply {
220 whenever(this.id).thenReturn(id)
221 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
222 whenever(this.sone).thenReturn(sone)
223 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
224 val post = mock<Post>().apply {
225 whenever(this.sone).thenReturn(postSone)
227 whenever(this.post).thenReturn(post.asOptional())
228 whenever(this.postId).thenReturn(postId)
232 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
233 linkedElements[link] = LinkedElement(link, failed, loading)
236 protected fun addAlbum(album: Album, albumId: String? = null) {
237 albums[albumId ?: album.id] = album
240 protected fun addImage(image: Image, imageId: String? = null) {
241 images[imageId ?: image.id] = image
245 fun `page returns correct path`() {
246 assertThat(page.path, equalTo(expectedPath))
250 fun `page needs form password`() {
251 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
255 fun `page requires login`() {
256 assertThat(page.requiresLogin(), equalTo(requiresLogin))