🔀 Merge branch 'release/v82'
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / core / SoneParserTest.kt
1 package net.pterodactylus.sone.core
2
3 import com.codahale.metrics.*
4 import com.google.common.base.Optional.*
5 import freenet.crypt.*
6 import freenet.keys.InsertableClientSSK.*
7 import net.pterodactylus.sone.data.*
8 import net.pterodactylus.sone.data.impl.AlbumImpl
9 import net.pterodactylus.sone.database.memory.*
10 import net.pterodactylus.sone.freenet.wot.*
11 import net.pterodactylus.sone.test.*
12 import net.pterodactylus.util.config.*
13 import org.hamcrest.MatcherAssert.*
14 import org.hamcrest.Matchers.*
15 import org.mockito.Mockito.*
16 import java.lang.System.*
17 import java.util.concurrent.TimeUnit.*
18 import kotlin.test.*
19
20 /**
21  * Unit test for [SoneParser].
22  */
23 class SoneParserTest {
24
25         private val database = MemoryDatabase(Configuration(MapConfigurationBackend()))
26         private val metricRegistry = MetricRegistry()
27         private val soneParser = SoneParser(database, metricRegistry)
28         private val sone = mock<Sone>()
29
30         @BeforeTest
31         fun setupSone() {
32                 setupSone(this.sone, Identity::class.java)
33                 database.storeSone(sone)
34         }
35
36         private fun setupSone(sone: Sone, identityClass: Class<out Identity>) {
37                 val identity = mock(identityClass)
38                 val clientSSK = createRandom(DummyRandomSource(), "WoT")
39                 whenever(identity.requestUri).thenReturn(clientSSK.uri.toString())
40                 whenever(identity.id).thenReturn("identity")
41                 whenever(sone.id).thenReturn("identity")
42                 whenever(sone.identity).thenReturn(identity)
43                 whenever(sone.requestUri).thenAnswer { clientSSK.uri.setKeyType("USK").setDocName("Sone") }
44                 whenever(sone.time).thenReturn(currentTimeMillis() - DAYS.toMillis(1))
45                 whenever(sone.rootAlbum).thenReturn(AlbumImpl(sone))
46         }
47
48         @Test
49         fun `parsing a sone fails when document is not xml`() {
50                 val inputStream = javaClass.getResourceAsStream("sone-parser-not-xml.xml")
51                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
52         }
53
54         @Test
55         fun `parsing a sone fails when document has negative protocol version`() {
56                 val inputStream = javaClass.getResourceAsStream("sone-parser-negative-protocol-version.xml")
57                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
58         }
59
60         @Test
61         fun `parsing a sone fails when protocol version is too large`() {
62                 val inputStream = javaClass.getResourceAsStream("sone-parser-too-large-protocol-version.xml")
63                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
64         }
65
66         @Test
67         fun `parsing a sone fails when there is no time`() {
68                 val inputStream = javaClass.getResourceAsStream("sone-parser-no-time.xml")
69                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
70         }
71
72         @Test
73         fun `parsing a sone fails when time is not numeric`() {
74                 val inputStream = javaClass.getResourceAsStream("sone-parser-time-not-numeric.xml")
75                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
76         }
77
78         @Test
79         fun `parsing a sone fails when profile is missing`() {
80                 val inputStream = javaClass.getResourceAsStream("sone-parser-no-profile.xml")
81                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
82         }
83
84         @Test
85         fun `parsing a sone fails when profile field is missing afield name`() {
86                 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-missing-field-name.xml")
87                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
88         }
89
90         @Test
91         fun `parsing a sone fails when profile field name is empty`() {
92                 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-empty-field-name.xml")
93                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
94         }
95
96         @Test
97         fun `parsing a sone fails when profile field name is not unique`() {
98                 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-duplicate-field-name.xml")
99                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
100         }
101
102         @Test
103         fun `parsing a sone succeeds without payload`() {
104                 val inputStream = javaClass.getResourceAsStream("sone-parser-no-payload.xml")
105                 assertThat(soneParser.parseSone(sone, inputStream)!!.time, equalTo(1407197508000L))
106         }
107
108         @Test
109         fun `parsing a local sone succeeds without payload`() {
110                 val inputStream = javaClass.getResourceAsStream("sone-parser-no-payload.xml")
111                 val localSone = mock<Sone>()
112                 setupSone(localSone, OwnIdentity::class.java)
113                 whenever(localSone.isLocal).thenReturn(true)
114                 val parsedSone = soneParser.parseSone(localSone, inputStream)
115                 assertThat(parsedSone!!.time, equalTo(1407197508000L))
116                 assertThat(parsedSone.isLocal, equalTo(true))
117         }
118
119         @Test
120         fun `parsing a sone succeeds without protocol version`() {
121                 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-protocol-version.xml")
122                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
123         }
124
125         @Test
126         fun `parsing a sone fails with missing client name`() {
127                 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-client-name.xml")
128                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
129         }
130
131         @Test
132         fun `parsing a sone fails with missing client version`() {
133                 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-client-version.xml")
134                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
135         }
136
137         @Test
138         fun `parsing a sone succeeds with client info`() {
139                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-client-info.xml")
140                 assertThat(soneParser.parseSone(sone, inputStream)!!.client, equalTo(Client("some-client", "some-version")))
141         }
142
143         @Test
144         fun `parsing a sone succeeds with profile`() {
145                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-profile.xml")
146                 val profile = soneParser.parseSone(sone, inputStream)!!.profile
147                 assertThat(profile.firstName, equalTo("first"))
148                 assertThat(profile.middleName, equalTo("middle"))
149                 assertThat(profile.lastName, equalTo("last"))
150                 assertThat(profile.birthDay, equalTo(18))
151                 assertThat(profile.birthMonth, equalTo(12))
152                 assertThat(profile.birthYear, equalTo(1976))
153         }
154
155         @Test
156         fun `parsing a sone succeeds without profile fields`() {
157                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-fields.xml")
158                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
159         }
160
161         @Test
162         fun `parsing a sone fails without post id`() {
163                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-id.xml")
164                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
165         }
166
167         @Test
168         fun `parsing a sone fails without post time`() {
169                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-time.xml")
170                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
171         }
172
173         @Test
174         fun `parsing a sone fails without post text`() {
175                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-text.xml")
176                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
177         }
178
179         @Test
180         fun `parsing a sone fails with invalid post time`() {
181                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-post-time.xml")
182                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
183         }
184
185         @Test
186         fun `parsing a sone succeeds with valid post time`() {
187                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-valid-post-time.xml")
188                 val posts = soneParser.parseSone(sone, inputStream)!!.posts
189                 assertThat(posts, hasSize(1))
190                 assertThat(posts[0].sone.id, equalTo(sone.id))
191                 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
192                 assertThat(posts[0].time, equalTo(1407197508000L))
193                 assertThat(posts[0].recipientId, equalTo(absent()))
194                 assertThat(posts[0].text, equalTo("text"))
195         }
196
197         @Test
198         fun `parsing a sone succeeds with recipient`() {
199                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-recipient.xml")
200                 val posts = soneParser.parseSone(sone, inputStream)!!.posts
201                 assertThat(posts, hasSize(1))
202                 assertThat(posts[0].sone.id, equalTo(sone.id))
203                 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
204                 assertThat(posts[0].time, equalTo(1407197508000L))
205                 assertThat(posts[0].recipientId, equalTo(of("1234567890123456789012345678901234567890123")))
206                 assertThat(posts[0].text, equalTo("text"))
207         }
208
209         @Test
210         fun `parsing a sone succeeds with invalid recipient`() {
211                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-recipient.xml")
212                 val posts = soneParser.parseSone(sone, inputStream)!!.posts
213                 assertThat(posts, hasSize(1))
214                 assertThat(posts[0].sone.id, equalTo(sone.id))
215                 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
216                 assertThat(posts[0].time, equalTo(1407197508000L))
217                 assertThat(posts[0].recipientId, equalTo(absent()))
218                 assertThat(posts[0].text, equalTo("text"))
219         }
220
221         @Test
222         fun `parsing a sone fails without post reply id`() {
223                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-id.xml")
224                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
225         }
226
227         @Test
228         fun `parsing a sone fails without post reply post id`() {
229                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-post-id.xml")
230                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
231         }
232
233         @Test
234         fun `parsing a sone fails without post reply time`() {
235                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-time.xml")
236                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
237         }
238
239         @Test
240         fun `parsing a sone fails without post reply text`() {
241                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-text.xml")
242                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
243         }
244
245         @Test
246         fun `parsing a sone fails with invalid post reply time`() {
247                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml")
248                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
249         }
250
251         @Test
252         fun `parsing a sone succeeds with valid post reply time`() {
253                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-valid-post-reply-time.xml")
254                 val postReplies = soneParser.parseSone(sone, inputStream)!!.replies
255                 assertThat(postReplies, hasSize(1))
256                 val postReply = postReplies.first()
257                 assertThat(postReply.id, equalTo("5ccba7f4-aff0-11e9-b176-a7b9db60ce98"))
258                 assertThat(postReply.postId, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
259                 assertThat(postReply.sone.id, equalTo("identity"))
260                 assertThat(postReply.time, equalTo(1407197508000L))
261                 assertThat(postReply.text, equalTo("reply-text"))
262         }
263
264         @Test
265         fun `parsing a sone succeeds without liked post ids`() {
266                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-liked-post-ids.xml")
267                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
268         }
269
270         @Test
271         fun `parsing a sone succeeds with liked post ids`() {
272                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-liked-post-ids.xml")
273                 assertThat(soneParser.parseSone(sone, inputStream)!!.likedPostIds, equalTo(setOf("liked-post-id")))
274         }
275
276         @Test
277         fun `parsing a sone succeeds without liked post reply ids`() {
278                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml")
279                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
280         }
281
282         @Test
283         fun `parsing a sone succeeds with liked post reply ids`() {
284                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml")
285                 assertThat(soneParser.parseSone(sone, inputStream)!!.likedReplyIds, equalTo(setOf("liked-post-reply-id")))
286         }
287
288         @Test
289         fun `parsing a sone succeeds without albums`() {
290                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-albums.xml")
291                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
292         }
293
294         @Test
295         fun `parsing a sone fails without album id`() {
296                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-album-id.xml")
297                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
298         }
299
300         @Test
301         fun `parsing a sone fails without album title`() {
302                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-album-title.xml")
303                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
304         }
305
306         @Test
307         fun `parsing a sone succeeds with nested albums`() {
308                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-multiple-albums.xml")
309                 val parsedSone = soneParser.parseSone(sone, inputStream)
310                 assertThat(parsedSone, notNullValue())
311                 assertThat(parsedSone!!.rootAlbum.albums, hasSize(1))
312                 val album = parsedSone.rootAlbum.albums[0]
313                 assertThat(album.id, equalTo("album-id-1"))
314                 assertThat(album.title, equalTo("album-title"))
315                 assertThat(album.description, equalTo("album-description"))
316                 assertThat(album.albums, hasSize(1))
317                 val nestedAlbum = album.albums[0]
318                 assertThat(nestedAlbum.id, equalTo("album-id-2"))
319                 assertThat(nestedAlbum.title, equalTo("album-title-2"))
320                 assertThat(nestedAlbum.description, equalTo("album-description-2"))
321                 assertThat(nestedAlbum.albums, hasSize(0))
322         }
323
324         @Test
325         fun `parsing a sone fails with invalid parent album id`() {
326                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml")
327                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
328         }
329
330         @Test
331         fun `parsing a sone succeeds without images`() {
332                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-images.xml")
333                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
334         }
335
336         @Test
337         fun `parsing a sone fails without image id`() {
338                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-id.xml")
339                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
340         }
341
342         @Test
343         fun `parsing a sone fails without image time`() {
344                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-time.xml")
345                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
346         }
347
348         @Test
349         fun `parsing a sone fails without image key`() {
350                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-key.xml")
351                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
352         }
353
354         @Test
355         fun `parsing a sone fails without image title`() {
356                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-title.xml")
357                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
358         }
359
360         @Test
361         fun `parsing a sone fails without image width`() {
362                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-width.xml")
363                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
364         }
365
366         @Test
367         fun `parsing a sone fails without image height`() {
368                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-height.xml")
369                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
370         }
371
372         @Test
373         fun `parsing a sone fails with invalid image width`() {
374                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-width.xml")
375                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
376         }
377
378         @Test
379         fun `parsing a sone fails with invalid image height`() {
380                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-height.xml")
381                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
382         }
383
384         @Test
385         fun `parsing a sone succeeds with image`() {
386                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-image.xml")
387                 val sone = soneParser.parseSone(this.sone, inputStream)
388                 assertThat(sone, notNullValue())
389                 assertThat(sone!!.rootAlbum.albums, hasSize(1))
390                 assertThat(sone.rootAlbum.albums[0].images, hasSize(1))
391                 val image = sone.rootAlbum.albums[0].images[0]
392                 assertThat(image.id, equalTo("image-id"))
393                 assertThat(image.creationTime, equalTo(1407197508000L))
394                 assertThat(image.key, equalTo("KSK@GPLv3.txt"))
395                 assertThat(image.title, equalTo("image-title"))
396                 assertThat(image.description, equalTo("image-description"))
397                 assertThat(image.width, equalTo(1920))
398                 assertThat(image.height, equalTo(1080))
399                 assertThat(sone.profile.avatar, equalTo("image-id"))
400         }
401
402         @Test
403         fun `unsuccessful parsing does not add a histogram entry`() {
404                 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-height.xml")
405                 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
406                 val histogram = metricRegistry.histogram("sone.parse.duration")
407                 assertThat(histogram.count, equalTo(0L))
408         }
409
410         @Test
411         fun `successful parsing adds histogram entry`() {
412                 val inputStream = javaClass.getResourceAsStream("sone-parser-without-images.xml")
413                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
414                 val histogram = metricRegistry.histogram("sone.parse.duration")
415                 assertThat(histogram.count, equalTo(1L))
416         }
417
418 }