c950a3121c5425b85ce47ae7a045e89396a5550d
[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.l10n.BaseL10n
6 import freenet.support.SimpleReadOnlyArrayBucket
7 import freenet.support.api.HTTPRequest
8 import net.pterodactylus.sone.core.Core
9 import net.pterodactylus.sone.core.ElementLoader
10 import net.pterodactylus.sone.core.LinkedElement
11 import net.pterodactylus.sone.core.Preferences
12 import net.pterodactylus.sone.core.UpdateChecker
13 import net.pterodactylus.sone.data.Album
14 import net.pterodactylus.sone.data.Image
15 import net.pterodactylus.sone.data.Post
16 import net.pterodactylus.sone.data.PostReply
17 import net.pterodactylus.sone.data.Profile
18 import net.pterodactylus.sone.data.Sone
19 import net.pterodactylus.sone.data.Sone.SoneStatus
20 import net.pterodactylus.sone.data.Sone.SoneStatus.idle
21 import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions
22 import net.pterodactylus.sone.test.deepMock
23 import net.pterodactylus.sone.test.get
24 import net.pterodactylus.sone.test.mock
25 import net.pterodactylus.sone.test.whenever
26 import net.pterodactylus.sone.utils.asOptional
27 import net.pterodactylus.sone.web.WebInterface
28 import net.pterodactylus.sone.web.page.FreenetRequest
29 import net.pterodactylus.util.notify.Notification
30 import net.pterodactylus.util.template.TemplateContextFactory
31 import net.pterodactylus.util.web.Method.GET
32 import org.hamcrest.MatcherAssert.assertThat
33 import org.hamcrest.Matchers.equalTo
34 import org.junit.Before
35 import org.junit.Test
36 import org.mockito.ArgumentMatchers.any
37 import org.mockito.ArgumentMatchers.anyBoolean
38 import org.mockito.ArgumentMatchers.anyInt
39 import org.mockito.ArgumentMatchers.anyString
40 import org.mockito.ArgumentMatchers.eq
41 import org.mockito.ArgumentMatchers.isNull
42 import java.util.NoSuchElementException
43 import javax.naming.SizeLimitExceededException
44
45 /**
46  * Base class for tests for any [JsonPage] implementations.
47  */
48 abstract class JsonPageTest(
49                 private val expectedPath: String,
50                 private val requiresLogin: Boolean = true,
51                 private val needsFormPassword: Boolean = true,
52                 pageSupplier: (WebInterface) -> JsonPage = { _ -> mock<JsonPage>() }) {
53
54         protected val webInterface = mock<WebInterface>()
55         protected val l10n = mock<BaseL10n>()
56         protected val core = mock<Core>()
57         protected val eventBus = mock<EventBus>()
58         protected val preferences = Preferences(eventBus)
59         protected val updateChecker = mock<UpdateChecker>()
60         protected val elementLoader = mock<ElementLoader>()
61         protected open val page: JsonPage by lazy { pageSupplier(webInterface) }
62         protected val json by lazy { page.createJsonObject(freenetRequest) }
63
64         protected val toadletContext = mock<ToadletContext>()
65         protected val freenetRequest = mock<FreenetRequest>()
66         protected val httpRequest = mock<HTTPRequest>()
67         protected val currentSone = deepMock<Sone>()
68         protected val profile = Profile(currentSone)
69
70         private val requestHeaders = mutableMapOf<String, String>()
71         private val requestParameters = mutableMapOf<String, String>()
72         private val requestParts = mutableMapOf<String, String>()
73         private val localSones = mutableMapOf<String, Sone>()
74         private val remoteSones = mutableMapOf<String, Sone>()
75         private val posts = mutableMapOf<String, Post>()
76         private val postLikes = mutableMapOf<Post, Set<Sone>>()
77         private val newPosts = mutableMapOf<String, Post>()
78         private val replies = mutableMapOf<String, PostReply>()
79         private val replyLikes = mutableMapOf<PostReply, Set<Sone>>()
80         private val newReplies = mutableMapOf<String, PostReply>()
81         private val linkedElements = mutableMapOf<String, LinkedElement>()
82         private val notifications = mutableMapOf<String, Notification>()
83         private val albums = mutableMapOf<String, Album>()
84         private val images = mutableMapOf<String, Image>()
85         private val translations = mutableMapOf<String, String>()
86
87         @Before
88         fun setupWebInterface() {
89                 whenever(webInterface.templateContextFactory).thenReturn(TemplateContextFactory())
90                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone)
91                 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone)
92                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone)
93                 whenever(webInterface.core).thenReturn(core)
94                 whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values }
95                 whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
96                 whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
97                 whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
98                 whenever(webInterface.l10n).thenReturn(l10n)
99         }
100
101         @Before
102         fun setupTranslations() {
103                 whenever(l10n.getString(anyString())).then { translations[it[0]] }
104         }
105
106         @Before
107         fun setupCore() {
108                 whenever(core.preferences).thenReturn(preferences)
109                 whenever(core.updateChecker).thenReturn(updateChecker)
110                 whenever(core.getSone(anyString())).thenAnswer { (localSones + remoteSones)[it.getArgument(0)].asOptional() }
111                 whenever(core.getLocalSone(anyString())).thenAnswer { localSones[it[0]] }
112                 whenever(core.getPost(anyString())).thenAnswer { (posts + newPosts)[it[0]].asOptional() }
113                 whenever(core.getLikes(any<Post>())).then { postLikes[it[0]] ?: emptySet<Sone>() }
114                 whenever(core.getLikes(any<PostReply>())).then { replyLikes[it[0]] ?: emptySet<Sone>() }
115                 whenever(core.getPostReply(anyString())).then { replies[it[0]].asOptional() }
116                 whenever(core.getAlbum(anyString())).then { albums[it[0]] }
117                 whenever(core.getImage(anyString())).then { images[it[0]] }
118                 whenever(core.getImage(anyString(), anyBoolean())).then { images[it[0]] }
119         }
120
121         @Before
122         fun setupElementLoader() {
123                 whenever(elementLoader.loadElement(anyString())).thenAnswer {
124                         linkedElements[it.getArgument(0)] ?: LinkedElement(it.getArgument(0), loading = true)
125                 }
126         }
127
128         @Before
129         fun setupCurrentSone() {
130                 whenever(currentSone.options).thenReturn(DefaultSoneOptions())
131                 currentSone.mock("soneId", "Sone_Id", true, 1000, idle)
132         }
133
134         @Before
135         fun setupFreenetRequest() {
136                 whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
137                 whenever(freenetRequest.method).thenReturn(GET)
138                 whenever(freenetRequest.httpRequest).thenReturn(httpRequest)
139         }
140
141         @Before
142         fun setupHttpRequest() {
143                 whenever(httpRequest.method).thenReturn("GET")
144                 whenever(httpRequest.getHeader(anyString())).thenAnswer { requestHeaders[it.get<String>(0).toLowerCase()] }
145                 whenever(httpRequest.getParam(anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: "" }
146                 whenever(httpRequest.getParam(anyString(), anyString())).thenAnswer { requestParameters[it.getArgument(0)] ?: it.getArgument(1) }
147                 whenever(httpRequest.getParam(anyString(), isNull())).thenAnswer { requestParameters[it.getArgument(0)] }
148                 whenever(httpRequest.getPart(anyString())).thenAnswer { requestParts[it.getArgument(0)]?.let { SimpleReadOnlyArrayBucket(it.toByteArray()) } }
149                 whenever(httpRequest.getPartAsBytesFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.toByteArray()?.copyOf(it.getArgument(1)) ?: ByteArray(0) }
150                 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() }
151                 whenever(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer { requestParts[it.getArgument(0)]?.substring(0, it.getArgument(1)) ?: "" }
152                 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() }
153                 whenever(httpRequest.getIntPart(anyString(), anyInt())).thenAnswer { invocation -> requestParts[invocation.getArgument(0)]?.toIntOrNull() ?: invocation.getArgument(1) }
154                 whenever(httpRequest.isPartSet(anyString())).thenAnswer { it.getArgument(0) in requestParts }
155         }
156
157         @Before
158         fun setupProfile() {
159                 whenever(currentSone.profile).thenReturn(profile)
160         }
161
162         protected val JsonReturnObject.error get() = if (this is JsonErrorReturnObject) this.error else null
163
164         protected fun Sone.mock(id: String, name: String, local: Boolean = false, time: Long, status: SoneStatus = idle) = apply {
165                 whenever(this.id).thenReturn(id)
166                 whenever(this.name).thenReturn(name)
167                 whenever(isLocal).thenReturn(local)
168                 whenever(this.time).thenReturn(time)
169                 whenever(this.status).thenReturn(status)
170         }
171
172         protected fun unsetCurrentSone() {
173                 whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null)
174                 whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null)
175                 whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null)
176         }
177
178         protected fun addRequestHeader(key: String, value: String) {
179                 requestHeaders += key.toLowerCase() to value
180         }
181
182         protected fun addRequestParameter(key: String, value: String) {
183                 requestParameters += key to value
184         }
185
186         protected fun addRequestPart(key: String, value: String) {
187                 requestParts += key to value
188         }
189
190         protected fun addNotification(notification: Notification, notificationId: String? = null) {
191                 notifications[notificationId ?: notification.id] = notification
192         }
193
194         protected fun addSone(sone: Sone, soneId: String? = null) {
195                 remoteSones += (soneId ?: sone.id) to sone
196         }
197
198         protected fun addLocalSone(id: String, sone: Sone) {
199                 localSones += id to sone
200         }
201
202         protected fun addPost(post: Post, id: String? = null) {
203                 posts[id ?: post.id] = post
204         }
205
206         protected fun addLikes(post: Post, vararg sones: Sone) {
207                 postLikes[post] = setOf(*sones)
208         }
209
210         protected fun addLikes(reply: PostReply, vararg sones: Sone) {
211                 replyLikes[reply] = setOf(*sones)
212         }
213
214         protected fun addNewPost(id: String, soneId: String, time: Long, recipientId: String? = null) =
215                         mock<Post>().apply {
216                                 whenever(this.id).thenReturn(id)
217                                 val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
218                                 whenever(this.sone).thenReturn(sone)
219                                 whenever(this.time).thenReturn(time)
220                                 whenever(this.recipientId).thenReturn(recipientId.asOptional())
221                         }.also { newPosts[id] = it }
222
223         protected fun addReply(reply: PostReply, id: String? = null) {
224                 replies[id ?: reply.id] = reply
225         }
226
227         protected fun addNewReply(id: String, soneId: String, postId: String, postSoneId: String) {
228                 newReplies[id] = mock<PostReply>().apply {
229                         whenever(this.id).thenReturn(id)
230                         val sone = mock<Sone>().apply { whenever(this.id).thenReturn(soneId) }
231                         whenever(this.sone).thenReturn(sone)
232                         val postSone = mock<Sone>().apply { whenever(this.id).thenReturn(postSoneId) }
233                         val post = mock<Post>().apply {
234                                 whenever(this.sone).thenReturn(postSone)
235                         }
236                         whenever(this.post).thenReturn(post.asOptional())
237                         whenever(this.postId).thenReturn(postId)
238                 }
239         }
240
241         protected fun addLinkedElement(link: String, loading: Boolean, failed: Boolean) {
242                 linkedElements[link] = LinkedElement(link, failed, loading)
243         }
244
245         protected fun addAlbum(album: Album, albumId: String? = null) {
246                 albums[albumId ?: album.id] = album
247         }
248
249         protected fun addImage(image: Image, imageId: String? = null) {
250                 images[imageId ?: image.id] = image
251         }
252
253         protected fun addTranslation(key: String, value: String) {
254                 translations[key] = value
255         }
256
257         @Test
258         fun `page returns correct path`() {
259                 assertThat(page.path, equalTo(expectedPath))
260         }
261
262         @Test
263         fun `page needs form password`() {
264                 assertThat(page.needsFormPassword(), equalTo(needsFormPassword))
265         }
266
267         @Test
268         fun `page requires login`() {
269                 assertThat(page.requiresLogin(), equalTo(requiresLogin))
270         }
271
272 }