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