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