09e8e85d6b80fd72667210b07429364f4a5fee3b
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / web / ajax / JsonPageTest.kt
1 package net.pterodactylus.sone.web.ajax
2
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
30 import org.junit.Test
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
38
39 /**
40  * Base class for tests for any [JsonPage] implementations.
41  */
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>() }) {
47
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)!! }
55
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)
61
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>()
74
75         @Before
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 }
85         }
86
87         @Before
88         fun setupCore() {
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]] }
95         }
96
97         @Before
98         fun setupElementLoader() {
99                 whenever(elementLoader.loadElement(anyString())).thenAnswer {
100                         linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
101                 }
102         }
103
104         @Before
105         fun setupCurrentSone() {
106                 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
107         }
108
109         @Before
110         fun setupFreenetRequest() {
111                 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
112                 whenever(freenetRequest.method).thenReturn(GET)
113                 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
114         }
115
116         @Before
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 }
130         }
131
132         @Before
133         fun setupProfile() {
134                 whenever(currentSone.profile).thenReturn(profile)
135         }
136
137         protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
138
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)
145         }
146
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)
151         }
152
153         protected fun addRequestHeader(key: String, value: String) {
154                 requestHeaders += key.toLowerCase() to value
155         }
156
157         protected fun addRequestParameter(key: String, value: String) {
158                 requestParameters += key to value
159         }
160
161         protected fun addRequestPart(key: String, value: String) {
162                 requestParts += key to value
163         }
164
165         protected fun addNotification(notification: Notification, notificationId: String? = null) {
166                 notifications[notificationId ?: notification.id] = notification
167         }
168
169         protected fun addSone(sone: Sone, soneId: String? = null) {
170                 remoteSones += (soneId ?: sone.id) to sone
171         }
172
173         protected fun addLocalSone(id: String, sone: Sone) {
174                 localSones += id to sone
175         }
176
177         protected fun addPost(id: String, post: Post) {
178                 posts[id] = post
179         }
180
181         protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
182                         mock<Post>().apply {
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 }
189
190         protected fun addReply(id: String, reply: PostReply) {
191                 replies[id] = reply
192         }
193
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)
202                         }
203                         whenever(this.post).thenReturn(post.asOptional())
204                         whenever(this.postId).thenReturn(postId)
205                 }
206         }
207
208         protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
209                 linkedElements[link] = LinkedElement(link, failed, loading)
210         }
211
212         protected fun addAlbum(album: Album, albumId: String? = null) {
213                 albums[albumId ?: album.id] = album
214         }
215
216         @Test
217         fun `page returns correct path`() {
218                 assertThat(page.path, equalTo(expectedPath))
219         }
220
221         @Test
222         fun `page needs form password`() {
223                 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
224         }
225
226         @Test
227         fun `page requires login`() {
228                 assertThat(page.requiresLogin(), equalTo(requiresLogin))
229         }
230
231 }