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