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.Post
12 import net.pterodactylus.sone.data.PostReply
13 import net.pterodactylus.sone.data.Profile
14 import net.pterodactylus.sone.data.Sone
15 import net.pterodactylus.sone.data.Sone.SoneStatus
16 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
17 import net.pterodactylus.sone.test.deepMock
18 import net.pterodactylus.sone.test.get
19 import net.pterodactylus.sone.test.mock
20 import net.pterodactylus.sone.test.whenever
21 import net.pterodactylus.sone.utils.asOptional
22 import net.pterodactylus.sone.web.WebInterface
23 import net.pterodactylus.sone.web.page.FreenetRequest
24 import net.pterodactylus.util.notify.Notification
25 import net.pterodactylus.util.web.Method.GET
26 import org.hamcrest.MatcherAssert.assertThat
27 import org.hamcrest.Matchers.equalTo
28 import org.junit.Before
30 import org.mockito.ArgumentMatchers.anyBoolean
31 import org.mockito.ArgumentMatchers.anyInt
32 import org.mockito.ArgumentMatchers.anyString
33 import org.mockito.ArgumentMatchers.eq
34 import org.mockito.ArgumentMatchers.isNull
35 import java.util.NoSuchElementException
36 import javax.naming.SizeLimitExceededException
39 * Base class for tests for any [JsonPage] implementations.
41 abstract class JsonPageTest(
42 private val expectedPath: String,
43 private val requiresLogin: Boolean = true,
44 private val needsFormPassword: Boolean = true,
45 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
47 protected val webInterface = mock<WebInterface>()
48 protected val core = mock<Core>()
49 protected val eventBus = mock<EventBus>()
50 protected val preferences = Preferences(eventBus)
51 protected val elementLoader = mock<ElementLoader>()
52 protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
53 protected val json by lazy { page.createJsonObject(freenetRequest)!! }
55 protected val toadletContext = mock<ToadletContext>()
56 protected val freenetRequest = mock<FreenetRequest>()
57 protected val httpRequest = mock<HTTPRequest>()
58 protected val currentSone = deepMock<Sone>()
59 protected val profile = Profile(currentSone)
61 private val requestHeaders = mutableMapOf<String, String>()
62 private val requestParameters = mutableMapOf<String, String>()
63 private val requestParts = mutableMapOf<String, String>()
64 private val localSones = mutableMapOf<String, Sone>()
65 private val remoteSones = mutableMapOf<String, Sone>()
66 private val posts = mutableMapOf<String, Post>()
67 private val newPosts = mutableMapOf<String, Post>()
68 private val replies = mutableMapOf<String, PostReply>()
69 private val newReplies = mutableMapOf<String, PostReply>()
70 private val linkedElements = mutableMapOf<String, LinkedElement>()
71 private val notifications = mutableMapOf<String, Notification>()
74 fun setupWebInterface() {
75 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
76 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
77 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
78 whenever(webInterface.core).thenReturn(core)
79 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
80 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
81 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
82 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
87 whenever(core.preferences).thenReturn(preferences)
88 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
89 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
90 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
91 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
95 fun setupElementLoader() {
96 whenever(elementLoader.loadElement(anyString())).thenAnswer {
97 linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
102 fun setupCurrentSone() {
103 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
107 fun setupFreenetRequest() {
108 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
109 whenever(freenetRequest.method).thenReturn(GET)
110 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
114 fun setupHttpRequest() {
115 whenever(httpRequest.method).thenReturn("GET")
116 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
117 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
118 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
119 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
120 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
121 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
122 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() }
123 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
124 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() }
125 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
126 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
131 whenever(currentSone.profile).thenReturn(profile)
134 protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
136 protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
137 whenever(this.id).thenReturn(id)
138 whenever(this.name).thenReturn(name)
139 whenever(isLocal).thenReturn(local)
140 whenever(this.time).thenReturn(time)
141 whenever(this.status).thenReturn(status)
144 protected fun unsetCurrentSone() {
145 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
146 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
147 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
150 protected fun addRequestHeader(key: String, value: String) {
151 requestHeaders += key.toLowerCase() to value
154 protected fun addRequestParameter(key: String, value: String) {
155 requestParameters += key to value
158 protected fun addRequestPart(key: String, value: String) {
159 requestParts += key to value
162 protected fun addNotification(notification: Notification, notificationId: String? = null) {
163 notifications[notificationId ?: notification.id] = notification
166 protected fun addSone(sone: Sone, soneId: String? = null) {
167 remoteSones += (soneId ?: sone.id) to sone
170 protected fun addLocalSone(id: String, sone: Sone) {
171 localSones += id to sone
174 protected fun addPost(id: String, post: Post) {
178 protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
180 whenever(this.id).thenReturn(id)
181 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
182 whenever(this.sone).thenReturn(sone)
183 whenever(this.time).thenReturn(time)
184 whenever(this.recipientId).thenReturn(recipientId.asOptional())
185 }.also { newPosts[id] = it }
187 protected fun addReply(id: String, reply: PostReply) {
191 protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
192 newReplies[id] = mock<PostReply>().apply {
193 whenever(this.id).thenReturn(id)
194 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
195 whenever(this.sone).thenReturn(sone)
196 val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
197 val post = mock<Post>().apply {
198 whenever(this.sone).thenReturn(postSone)
200 whenever(this.post).thenReturn(post.asOptional())
201 whenever(this.postId).thenReturn(postId)
205 protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
206 linkedElements[link] = LinkedElement(link, failed, loading)
210 fun `page returns correct path`() {
211 assertThat(page.path, equalTo(expectedPath))
215 fun `page needs form password`() {
216 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
220 fun `page requires login`() {
221 assertThat(page.requiresLogin(), equalTo(requiresLogin))