1 package net.pterodactylus.sone.web.ajax
3 import freenet.clients.http.ToadletContext
4 import freenet.support.SimpleReadOnlyArrayBucket
5 import freenet.support.api.HTTPRequest
6 import net.pterodactylus.sone.core.Core
7 import net.pterodactylus.sone.core.ElementLoader
8 import net.pterodactylus.sone.core.LinkedElement
9 import net.pterodactylus.sone.data.Post
10 import net.pterodactylus.sone.data.PostReply
11 import net.pterodactylus.sone.data.Profile
12 import net.pterodactylus.sone.data.Sone
13 import net.pterodactylus.sone.data.Sone.SoneStatus
14 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
15 import net.pterodactylus.sone.test.deepMock
16 import net.pterodactylus.sone.test.get
17 import net.pterodactylus.sone.test.mock
18 import net.pterodactylus.sone.test.whenever
19 import net.pterodactylus.sone.utils.asOptional
20 import net.pterodactylus.sone.web.WebInterface
21 import net.pterodactylus.sone.web.page.FreenetRequest
22 import net.pterodactylus.util.notify.Notification
23 import net.pterodactylus.util.web.Method.GET
24 import org.hamcrest.MatcherAssert.assertThat
25 import org.hamcrest.Matchers.equalTo
26 import org.junit.Before
28 import org.mockito.ArgumentMatchers.anyBoolean
29 import org.mockito.ArgumentMatchers.anyInt
30 import org.mockito.ArgumentMatchers.anyString
31 import org.mockito.ArgumentMatchers.eq
32 import org.mockito.ArgumentMatchers.isNull
33 import java.util.NoSuchElementException
34 import javax.naming.SizeLimitExceededException
37 * Base class for tests for any [JsonPage] implementations.
39 abstract class JsonPageTest(
40 private val expectedPath: String,
41 private val requiresLogin: Boolean = true,
42 private val needsFormPassword: Boolean = true,
43 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
45 protected val webInterface = mock<WebInterface>()
46 protected val core = mock<Core>()
47 protected val elementLoader = mock<ElementLoader>()
48 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
49 protected val json by lazy { page.createJsonObject(freenetRequest)!! }
51 protected val toadletContext = mock<ToadletContext>()
52 protected val freenetRequest = mock<FreenetRequest>()
53 protected val httpRequest = mock<HTTPRequest>()
54 protected val currentSone = deepMock<Sone>()
55 protected val profile = Profile(currentSone)
57 private val requestHeaders = mutableMapOf<String, String>()
58 private val requestParameters = mutableMapOf<String, String>()
59 private val requestParts = mutableMapOf<String, String>()
60 private val localSones = mutableMapOf<String, Sone>()
61 private val remoteSones = mutableMapOf<String, Sone>()
62 private val posts = mutableMapOf<String, Post>()
63 private val newPosts = mutableMapOf<String, Post>()
64 private val replies = mutableMapOf<String, PostReply>()
65 private val newReplies = mutableMapOf<String, PostReply>()
66 private val linkedElements = mutableMapOf<String, LinkedElement>()
67 private val notifications = mutableMapOf<String, Notification>()
70 fun setupWebInterface() {
71 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
72 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
73 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
74 whenever(webInterface.core).thenReturn(core)
75 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
76 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
77 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
78 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
83 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
84 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
85 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
86 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
90 fun setupElementLoader() {
91 whenever(elementLoader.loadElement(anyString())).thenAnswer {
92 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
97 fun setupCurrentSone() {
98 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
102 fun setupFreenetRequest() {
103 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
104 whenever(freenetRequest.method).thenReturn(GET)
105 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
109 fun setupHttpRequest() {
110 whenever(httpRequest.method).thenReturn("GET")
111 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
112 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
113 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
114 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
115 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
116 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
117 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() }
118 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
119 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() }
120 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
121 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
126 whenever(currentSone.profile).thenReturn(profile)
129 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
131 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
132 whenever(this.id).thenReturn(id)
133 whenever(this.name).thenReturn(name)
134 whenever(isLocal).thenReturn(local)
135 whenever(this.time).thenReturn(time)
136 whenever(this.status).thenReturn(status)
139 protected fun unsetCurrentSone() {
140 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
141 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
142 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
145 protected fun addRequestHeader(key: String, value: String) {
146 requestHeaders += key.toLowerCase() to value
149 protected fun addRequestParameter(key: String, value: String) {
150 requestParameters += key to value
153 protected fun addRequestPart(key: String, value: String) {
154 requestParts += key to value
157 protected fun addNotification(notification: Notification, notificationId: String? = null) {
158 notifications[notificationId ?: notification.id] = notification
161 protected fun addSone(sone: Sone) {
162 remoteSones += sone.id to sone
165 protected fun addLocalSone(id: String, sone: Sone) {
166 localSones += id to sone
169 protected fun addPost(id: String, post: Post) {
173 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
175 whenever(this.id).thenReturn(id)
176 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
177 whenever(this.sone).thenReturn(sone)
178 whenever(this.time).thenReturn(time)
179 whenever(this.recipientId).thenReturn(recipientId.asOptional())
180 }.also { newPosts[id] = it }
182 protected fun addReply(id: String, reply: PostReply) {
186 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
187 newReplies[id] = mock<PostReply>().apply {
188 whenever(this.id).thenReturn(id)
189 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
190 whenever(this.sone).thenReturn(sone)
191 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
192 val post = mock<Post>().apply {
193 whenever(this.sone).thenReturn(postSone)
195 whenever(this.post).thenReturn(post.asOptional())
196 whenever(this.postId).thenReturn(postId)
200 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
201 linkedElements[link] = LinkedElement(link, failed, loading)
205 fun `page returns correct path`() {
206 assertThat(page.path, equalTo(expectedPath))
210 fun `page needs form password`() {
211 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
215 fun `page requires login`() {
216 assertThat(page.requiresLogin(), equalTo(requiresLogin))