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