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.Post
13 import net.pterodactylus.sone.data.PostReply
14 import net.pterodactylus.sone.data.Profile
15 import net.pterodactylus.sone.data.Sone
16 import net.pterodactylus.sone.data.Sone.SoneStatus
17 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
18 import net.pterodactylus.sone.test.deepMock
19 import net.pterodactylus.sone.test.get
20 import net.pterodactylus.sone.test.mock
21 import net.pterodactylus.sone.test.whenever
22 import net.pterodactylus.sone.utils.asOptional
23 import net.pterodactylus.sone.web.WebInterface
24 import net.pterodactylus.sone.web.page.FreenetRequest
25 import net.pterodactylus.util.notify.Notification
26 import net.pterodactylus.util.web.Method.GET
27 import org.hamcrest.MatcherAssert.assertThat
28 import org.hamcrest.Matchers.equalTo
29 import org.junit.Before
31 import org.mockito.ArgumentMatchers.anyBoolean
32 import org.mockito.ArgumentMatchers.anyInt
33 import org.mockito.ArgumentMatchers.anyString
34 import org.mockito.ArgumentMatchers.eq
35 import org.mockito.ArgumentMatchers.isNull
36 import java.util.NoSuchElementException
37 import javax.naming.SizeLimitExceededException
40 * Base class for tests for any [JsonPage] implementations.
42 abstract class JsonPageTest(
43 private val expectedPath: String,
44 private val requiresLogin: Boolean = true,
45 private val needsFormPassword: Boolean = true,
46 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
48 protected val webInterface = mock<WebInterface>()
49 protected val core = mock<Core>()
50 protected val eventBus = mock<EventBus>()
51 protected val preferences = Preferences(eventBus)
52 protected val elementLoader = mock<ElementLoader>()
53 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
54 protected val json by lazy { page.createJsonObject(freenetRequest)!! }
56 protected val toadletContext = mock<ToadletContext>()
57 protected val freenetRequest = mock<FreenetRequest>()
58 protected val httpRequest = mock<HTTPRequest>()
59 protected val currentSone = deepMock<Sone>()
60 protected val profile = Profile(currentSone)
62 private val requestHeaders = mutableMapOf<String, String>()
63 private val requestParameters = mutableMapOf<String, String>()
64 private val requestParts = mutableMapOf<String, String>()
65 private val localSones = mutableMapOf<String, Sone>()
66 private val remoteSones = mutableMapOf<String, Sone>()
67 private val posts = mutableMapOf<String, Post>()
68 private val newPosts = mutableMapOf<String, Post>()
69 private val replies = mutableMapOf<String, PostReply>()
70 private val newReplies = mutableMapOf<String, PostReply>()
71 private val linkedElements = mutableMapOf<String, LinkedElement>()
72 private val notifications = mutableMapOf<String, Notification>()
73 private val albums = mutableMapOf<String, Album>()
76 fun setupWebInterface() {
77 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
78 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
79 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
80 whenever(webInterface.core).thenReturn(core)
81 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
82 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
83 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
84 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
89 whenever(core.preferences).thenReturn(preferences)
90 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
91 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
92 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
93 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
94 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
98 fun setupElementLoader() {
99 whenever(elementLoader.loadElement(anyString())).thenAnswer {
100 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
105 fun setupCurrentSone() {
106 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
110 fun setupFreenetRequest() {
111 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
112 whenever(freenetRequest.method).thenReturn(GET)
113 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
117 fun setupHttpRequest() {
118 whenever(httpRequest.method).thenReturn("GET")
119 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
120 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
121 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
122 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
123 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
124 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
125 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() }
126 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
127 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() }
128 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
129 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
134 whenever(currentSone.profile).thenReturn(profile)
137 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
139 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
140 whenever(this.id).thenReturn(id)
141 whenever(this.name).thenReturn(name)
142 whenever(isLocal).thenReturn(local)
143 whenever(this.time).thenReturn(time)
144 whenever(this.status).thenReturn(status)
147 protected fun unsetCurrentSone() {
148 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
149 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
150 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
153 protected fun addRequestHeader(key: String, value: String) {
154 requestHeaders += key.toLowerCase() to value
157 protected fun addRequestParameter(key: String, value: String) {
158 requestParameters += key to value
161 protected fun addRequestPart(key: String, value: String) {
162 requestParts += key to value
165 protected fun addNotification(notification: Notification, notificationId: String? = null) {
166 notifications[notificationId ?: notification.id] = notification
169 protected fun addSone(sone: Sone, soneId: String? = null) {
170 remoteSones += (soneId ?: sone.id) to sone
173 protected fun addLocalSone(id: String, sone: Sone) {
174 localSones += id to sone
177 protected fun addPost(id: String, post: Post) {
181 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
183 whenever(this.id).thenReturn(id)
184 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
185 whenever(this.sone).thenReturn(sone)
186 whenever(this.time).thenReturn(time)
187 whenever(this.recipientId).thenReturn(recipientId.asOptional())
188 }.also { newPosts[id] = it }
190 protected fun addReply(id: String, reply: PostReply) {
194 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
195 newReplies[id] = mock<PostReply>().apply {
196 whenever(this.id).thenReturn(id)
197 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
198 whenever(this.sone).thenReturn(sone)
199 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
200 val post = mock<Post>().apply {
201 whenever(this.sone).thenReturn(postSone)
203 whenever(this.post).thenReturn(post.asOptional())
204 whenever(this.postId).thenReturn(postId)
208 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
209 linkedElements[link] = LinkedElement(link, failed, loading)
212 protected fun addAlbum(album: Album, albumId: String? = null) {
213 albums[albumId ?: album.id] = album
217 fun `page returns correct path`() {
218 assertThat(page.path, equalTo(expectedPath))
222 fun `page needs form password`() {
223 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
227 fun `page requires login`() {
228 assertThat(page.requiresLogin(), equalTo(requiresLogin))