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