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.data.Album
12 import net.pterodactylus.sone.data.Image
13 import net.pterodactylus.sone.data.Post
14 import net.pterodactylus.sone.data.PostReply
15 import net.pterodactylus.sone.data.Profile
16 import net.pterodactylus.sone.data.Sone
17 import net.pterodactylus.sone.data.Sone.SoneStatus
18 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
19 import net.pterodactylus.sone.test.deepMock
20 import net.pterodactylus.sone.test.get
21 import net.pterodactylus.sone.test.mock
22 import net.pterodactylus.sone.test.whenever
23 import net.pterodactylus.sone.utils.asOptional
24 import net.pterodactylus.sone.web.WebInterface
25 import net.pterodactylus.sone.web.page.FreenetRequest
26 import net.pterodactylus.util.notify.Notification
27 import net.pterodactylus.util.web.Method.GET
28 import org.hamcrest.MatcherAssert.assertThat
29 import org.hamcrest.Matchers.equalTo
30 import org.junit.Before
32 import org.mockito.ArgumentMatchers.any
33 import org.mockito.ArgumentMatchers.anyBoolean
34 import org.mockito.ArgumentMatchers.anyInt
35 import org.mockito.ArgumentMatchers.anyString
36 import org.mockito.ArgumentMatchers.eq
37 import org.mockito.ArgumentMatchers.isNull
38 import java.util.NoSuchElementException
39 import javax.naming.SizeLimitExceededException
42 * Base class for tests for any [JsonPage] implementations.
44 abstract class JsonPageTest(
45 private val expectedPath: String,
46 private val requiresLogin: Boolean = true,
47 private val needsFormPassword: Boolean = true,
48 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
50 protected val webInterface = mock<WebInterface>()
51 protected val core = mock<Core>()
52 protected val eventBus = mock<EventBus>()
53 protected val preferences = Preferences(eventBus)
54 protected val elementLoader = mock<ElementLoader>()
55 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
56 protected val json by lazy { page.createJsonObject(freenetRequest) }
58 protected val toadletContext = mock<ToadletContext>()
59 protected val freenetRequest = mock<FreenetRequest>()
60 protected val httpRequest = mock<HTTPRequest>()
61 protected val currentSone = deepMock<Sone>()
62 protected val profile = Profile(currentSone)
64 private val requestHeaders = mutableMapOf<String, String>()
65 private val requestParameters = mutableMapOf<String, String>()
66 private val requestParts = mutableMapOf<String, String>()
67 private val localSones = mutableMapOf<String, Sone>()
68 private val remoteSones = mutableMapOf<String, Sone>()
69 private val posts = mutableMapOf<String, Post>()
70 private val postLikes = mutableMapOf<Post, Set<Sone>>()
71 private val newPosts = mutableMapOf<String, Post>()
72 private val replies = mutableMapOf<String, PostReply>()
73 private val replyLikes = mutableMapOf<PostReply, Set<Sone>>()
74 private val newReplies = mutableMapOf<String, PostReply>()
75 private val linkedElements = mutableMapOf<String, LinkedElement>()
76 private val notifications = mutableMapOf<String, Notification>()
77 private val albums = mutableMapOf<String, Album>()
78 private val images = mutableMapOf<String, Image>()
81 fun setupWebInterface() {
82 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
83 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
84 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
85 whenever(webInterface.core).thenReturn(core)
86 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
87 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
88 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
89 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
94 whenever(core.preferences).thenReturn(preferences)
95 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
96 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
97 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
98 whenever(core.getLikes(any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
99 whenever(core.getLikes(any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
100 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
101 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
102 whenever(core.getImage(anyString())).then { images[it[0]] }
103 whenever(core.getImage(anyString(), anyBoolean())).then { images[it[0]] }
107 fun setupElementLoader() {
108 whenever(elementLoader.loadElement(anyString())).thenAnswer {
109 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
114 fun setupCurrentSone() {
115 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
119 fun setupFreenetRequest() {
120 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
121 whenever(freenetRequest.method).thenReturn(GET)
122 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
126 fun setupHttpRequest() {
127 whenever(httpRequest.method).thenReturn("GET")
128 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
129 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
130 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
131 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
132 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
133 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
134 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() }
135 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
136 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() }
137 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
138 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
143 whenever(currentSone.profile).thenReturn(profile)
146 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
148 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
149 whenever(this.id).thenReturn(id)
150 whenever(this.name).thenReturn(name)
151 whenever(isLocal).thenReturn(local)
152 whenever(this.time).thenReturn(time)
153 whenever(this.status).thenReturn(status)
156 protected fun unsetCurrentSone() {
157 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
158 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
159 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
162 protected fun addRequestHeader(key: String, value: String) {
163 requestHeaders += key.toLowerCase() to value
166 protected fun addRequestParameter(key: String, value: String) {
167 requestParameters += key to value
170 protected fun addRequestPart(key: String, value: String) {
171 requestParts += key to value
174 protected fun addNotification(notification: Notification, notificationId: String? = null) {
175 notifications[notificationId ?: notification.id] = notification
178 protected fun addSone(sone: Sone, soneId: String? = null) {
179 remoteSones += (soneId ?: sone.id) to sone
182 protected fun addLocalSone(id: String, sone: Sone) {
183 localSones += id to sone
186 protected fun addPost(post: Post, id: String? = null) {
187 posts[id ?: post.id] = post
190 protected fun addLikes(post: Post, vararg sones: Sone) {
191 postLikes[post] = setOf(*sones)
194 protected fun addLikes(reply: PostReply, vararg sones: Sone) {
195 replyLikes[reply] = setOf(*sones)
198 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
200 whenever(this.id).thenReturn(id)
201 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
202 whenever(this.sone).thenReturn(sone)
203 whenever(this.time).thenReturn(time)
204 whenever(this.recipientId).thenReturn(recipientId.asOptional())
205 }.also { newPosts[id] = it }
207 protected fun addReply(reply: PostReply, id: String? = null) {
208 replies[id ?: reply.id] = reply
211 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
212 newReplies[id] = mock<PostReply>().apply {
213 whenever(this.id).thenReturn(id)
214 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
215 whenever(this.sone).thenReturn(sone)
216 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
217 val post = mock<Post>().apply {
218 whenever(this.sone).thenReturn(postSone)
220 whenever(this.post).thenReturn(post.asOptional())
221 whenever(this.postId).thenReturn(postId)
225 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
226 linkedElements[link] = LinkedElement(link, failed, loading)
229 protected fun addAlbum(album: Album, albumId: String? = null) {
230 albums[albumId ?: album.id] = album
233 protected fun addImage(image: Image, imageId: String? = null) {
234 images[imageId ?: image.id] = image
238 fun `page returns correct path`() {
239 assertThat(page.path, equalTo(expectedPath))
243 fun `page needs form password`() {
244 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
248 fun `page requires login`() {
249 assertThat(page.requiresLogin(), equalTo(requiresLogin))