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.web.Method.GET
30 import org.hamcrest.MatcherAssert.assertThat
31 import org.hamcrest.Matchers.equalTo
32 import org.junit.Before
34 import org.mockito.ArgumentMatchers.any
35 import org.mockito.ArgumentMatchers.anyBoolean
36 import org.mockito.ArgumentMatchers.anyInt
37 import org.mockito.ArgumentMatchers.anyString
38 import org.mockito.ArgumentMatchers.eq
39 import org.mockito.ArgumentMatchers.isNull
40 import java.util.NoSuchElementException
41 import javax.naming.SizeLimitExceededException
44 * Base class for tests for any [JsonPage] implementations.
46 abstract class JsonPageTest(
47 private val expectedPath: String,
48 private val requiresLogin: Boolean = true,
49 private val needsFormPassword: Boolean = true,
50 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
52 protected val webInterface = mock<WebInterface>()
53 protected val core = mock<Core>()
54 protected val eventBus = mock<EventBus>()
55 protected val preferences = Preferences(eventBus)
56 protected val updateChecker = mock<UpdateChecker>()
57 protected val elementLoader = mock<ElementLoader>()
58 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
59 protected val json by lazy { page.createJsonObject(freenetRequest) }
61 protected val toadletContext = mock<ToadletContext>()
62 protected val freenetRequest = mock<FreenetRequest>()
63 protected val httpRequest = mock<HTTPRequest>()
64 protected val currentSone = deepMock<Sone>()
65 protected val profile = Profile(currentSone)
67 private val requestHeaders = mutableMapOf<String, String>()
68 private val requestParameters = mutableMapOf<String, String>()
69 private val requestParts = mutableMapOf<String, String>()
70 private val localSones = mutableMapOf<String, Sone>()
71 private val remoteSones = mutableMapOf<String, Sone>()
72 private val posts = mutableMapOf<String, Post>()
73 private val postLikes = mutableMapOf<Post, Set<Sone>>()
74 private val newPosts = mutableMapOf<String, Post>()
75 private val replies = mutableMapOf<String, PostReply>()
76 private val replyLikes = mutableMapOf<PostReply, Set<Sone>>()
77 private val newReplies = mutableMapOf<String, PostReply>()
78 private val linkedElements = mutableMapOf<String, LinkedElement>()
79 private val notifications = mutableMapOf<String, Notification>()
80 private val albums = mutableMapOf<String, Album>()
81 private val images = mutableMapOf<String, Image>()
84 fun setupWebInterface() {
85 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
86 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
87 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
88 whenever(webInterface.core).thenReturn(core)
89 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
90 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
91 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
92 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
97 whenever(core.preferences).thenReturn(preferences)
98 whenever(core.updateChecker).thenReturn(updateChecker)
99 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
100 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
101 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
102 whenever(core.getLikes(any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
103 whenever(core.getLikes(any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
104 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
105 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
106 whenever(core.getImage(anyString())).then { images[it[0]] }
107 whenever(core.getImage(anyString(), anyBoolean())).then { images[it[0]] }
111 fun setupElementLoader() {
112 whenever(elementLoader.loadElement(anyString())).thenAnswer {
113 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
118 fun setupCurrentSone() {
119 whenever(currentSone.options).thenReturn(DefaultSoneOptions())
120 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
124 fun setupFreenetRequest() {
125 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
126 whenever(freenetRequest.method).thenReturn(GET)
127 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
131 fun setupHttpRequest() {
132 whenever(httpRequest.method).thenReturn("GET")
133 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
134 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
135 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
136 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
137 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
138 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
139 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() }
140 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
141 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() }
142 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
143 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
148 whenever(currentSone.profile).thenReturn(profile)
151 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
153 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
154 whenever(this.id).thenReturn(id)
155 whenever(this.name).thenReturn(name)
156 whenever(isLocal).thenReturn(local)
157 whenever(this.time).thenReturn(time)
158 whenever(this.status).thenReturn(status)
161 protected fun unsetCurrentSone() {
162 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
163 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
164 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
167 protected fun addRequestHeader(key: String, value: String) {
168 requestHeaders += key.toLowerCase() to value
171 protected fun addRequestParameter(key: String, value: String) {
172 requestParameters += key to value
175 protected fun addRequestPart(key: String, value: String) {
176 requestParts += key to value
179 protected fun addNotification(notification: Notification, notificationId: String? = null) {
180 notifications[notificationId ?: notification.id] = notification
183 protected fun addSone(sone: Sone, soneId: String? = null) {
184 remoteSones += (soneId ?: sone.id) to sone
187 protected fun addLocalSone(id: String, sone: Sone) {
188 localSones += id to sone
191 protected fun addPost(post: Post, id: String? = null) {
192 posts[id ?: post.id] = post
195 protected fun addLikes(post: Post, vararg sones: Sone) {
196 postLikes[post] = setOf(*sones)
199 protected fun addLikes(reply: PostReply, vararg sones: Sone) {
200 replyLikes[reply] = setOf(*sones)
203 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
205 whenever(this.id).thenReturn(id)
206 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
207 whenever(this.sone).thenReturn(sone)
208 whenever(this.time).thenReturn(time)
209 whenever(this.recipientId).thenReturn(recipientId.asOptional())
210 }.also { newPosts[id] = it }
212 protected fun addReply(reply: PostReply, id: String? = null) {
213 replies[id ?: reply.id] = reply
216 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
217 newReplies[id] = mock<PostReply>().apply {
218 whenever(this.id).thenReturn(id)
219 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
220 whenever(this.sone).thenReturn(sone)
221 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
222 val post = mock<Post>().apply {
223 whenever(this.sone).thenReturn(postSone)
225 whenever(this.post).thenReturn(post.asOptional())
226 whenever(this.postId).thenReturn(postId)
230 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
231 linkedElements[link] = LinkedElement(link, failed, loading)
234 protected fun addAlbum(album: Album, albumId: String? = null) {
235 albums[albumId ?: album.id] = album
238 protected fun addImage(image: Image, imageId: String? = null) {
239 images[imageId ?: image.id] = image
243 fun `page returns correct path`() {
244 assertThat(page.path, equalTo(expectedPath))
248 fun `page needs form password`() {
249 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
253 fun `page requires login`() {
254 assertThat(page.requiresLogin(), equalTo(requiresLogin))