1 package net.pterodactylus.sone.core
3 import com.codahale.metrics.*
4 import com.google.common.base.Optional.*
6 import freenet.keys.InsertableClientSSK.*
7 import net.pterodactylus.sone.data.*
8 import net.pterodactylus.sone.database.memory.*
9 import net.pterodactylus.sone.freenet.wot.*
10 import net.pterodactylus.sone.test.*
11 import net.pterodactylus.util.config.*
12 import org.hamcrest.MatcherAssert.*
13 import org.hamcrest.Matchers.*
14 import org.mockito.Mockito.*
15 import java.lang.System.*
16 import java.util.concurrent.TimeUnit.*
20 * Unit test for [SoneParser].
22 class SoneParserTest {
24 private val database = MemoryDatabase(Configuration(MapConfigurationBackend()))
25 private val metricRegistry = MetricRegistry()
26 private val soneParser = SoneParser(database, metricRegistry)
27 private val sone = mock<Sone>()
31 setupSone(this.sone, Identity::class.java)
32 database.storeSone(sone)
35 private fun setupSone(sone: Sone, identityClass: Class<out Identity>) {
36 val identity = mock(identityClass)
37 val clientSSK = createRandom(DummyRandomSource(), "WoT")
38 whenever(identity.requestUri).thenReturn(clientSSK.uri.toString())
39 whenever(identity.id).thenReturn("identity")
40 whenever(sone.id).thenReturn("identity")
41 whenever(sone.identity).thenReturn(identity)
42 whenever(sone.requestUri).thenAnswer { clientSSK.uri.setKeyType("USK").setDocName("Sone") }
43 whenever(sone.time).thenReturn(currentTimeMillis() - DAYS.toMillis(1))
47 fun `parsing a sone fails when document is not xml`() {
48 val inputStream = javaClass.getResourceAsStream("sone-parser-not-xml.xml")
49 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
53 fun `parsing a sone fails when document has negative protocol version`() {
54 val inputStream = javaClass.getResourceAsStream("sone-parser-negative-protocol-version.xml")
55 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
59 fun `parsing a sone fails when protocol version is too large`() {
60 val inputStream = javaClass.getResourceAsStream("sone-parser-too-large-protocol-version.xml")
61 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
65 fun `parsing a sone fails when there is no time`() {
66 val inputStream = javaClass.getResourceAsStream("sone-parser-no-time.xml")
67 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
71 fun `parsing a sone fails when time is not numeric`() {
72 val inputStream = javaClass.getResourceAsStream("sone-parser-time-not-numeric.xml")
73 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
77 fun `parsing a sone fails when profile is missing`() {
78 val inputStream = javaClass.getResourceAsStream("sone-parser-no-profile.xml")
79 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
83 fun `parsing a sone fails when profile field is missing afield name`() {
84 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-missing-field-name.xml")
85 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
89 fun `parsing a sone fails when profile field name is empty`() {
90 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-empty-field-name.xml")
91 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
95 fun `parsing a sone fails when profile field name is not unique`() {
96 val inputStream = javaClass.getResourceAsStream("sone-parser-profile-duplicate-field-name.xml")
97 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
101 fun `parsing a sone succeeds without payload`() {
102 val inputStream = javaClass.getResourceAsStream("sone-parser-no-payload.xml")
103 assertThat(soneParser.parseSone(sone, inputStream)!!.time, equalTo(1407197508000L))
107 fun `parsing a local sone succeeds without payload`() {
108 val inputStream = javaClass.getResourceAsStream("sone-parser-no-payload.xml")
109 val localSone = mock<Sone>()
110 setupSone(localSone, OwnIdentity::class.java)
111 whenever(localSone.isLocal).thenReturn(true)
112 val parsedSone = soneParser.parseSone(localSone, inputStream)
113 assertThat(parsedSone!!.time, equalTo(1407197508000L))
114 assertThat(parsedSone.isLocal, equalTo(true))
118 fun `parsing a sone succeeds without protocol version`() {
119 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-protocol-version.xml")
120 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
124 fun `parsing a sone fails with missing client name`() {
125 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-client-name.xml")
126 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
130 fun `parsing a sone fails with missing client version`() {
131 val inputStream = javaClass.getResourceAsStream("sone-parser-missing-client-version.xml")
132 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
136 fun `parsing a sone succeeds with client info`() {
137 val inputStream = javaClass.getResourceAsStream("sone-parser-with-client-info.xml")
138 assertThat(soneParser.parseSone(sone, inputStream)!!.client, equalTo(Client("some-client", "some-version")))
142 fun `parsing a sone succeeds with profile`() {
143 val inputStream = javaClass.getResourceAsStream("sone-parser-with-profile.xml")
144 val profile = soneParser.parseSone(sone, inputStream)!!.profile
145 assertThat(profile.firstName, equalTo("first"))
146 assertThat(profile.middleName, equalTo("middle"))
147 assertThat(profile.lastName, equalTo("last"))
148 assertThat(profile.birthDay, equalTo(18))
149 assertThat(profile.birthMonth, equalTo(12))
150 assertThat(profile.birthYear, equalTo(1976))
154 fun `parsing a sone succeeds without profile fields`() {
155 val inputStream = javaClass.getResourceAsStream("sone-parser-without-fields.xml")
156 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
160 fun `parsing a sone fails without post id`() {
161 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-id.xml")
162 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
166 fun `parsing a sone fails without post time`() {
167 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-time.xml")
168 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
172 fun `parsing a sone fails without post text`() {
173 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-text.xml")
174 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
178 fun `parsing a sone fails with invalid post time`() {
179 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-post-time.xml")
180 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
184 fun `parsing a sone succeeds with valid post time`() {
185 val inputStream = javaClass.getResourceAsStream("sone-parser-with-valid-post-time.xml")
186 val posts = soneParser.parseSone(sone, inputStream)!!.posts
187 assertThat(posts, hasSize(1))
188 assertThat(posts[0].sone.id, equalTo(sone.id))
189 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
190 assertThat(posts[0].time, equalTo(1407197508000L))
191 assertThat(posts[0].recipientId, equalTo(absent()))
192 assertThat(posts[0].text, equalTo("text"))
196 fun `parsing a sone succeeds with recipient`() {
197 val inputStream = javaClass.getResourceAsStream("sone-parser-with-recipient.xml")
198 val posts = soneParser.parseSone(sone, inputStream)!!.posts
199 assertThat(posts, hasSize(1))
200 assertThat(posts[0].sone.id, equalTo(sone.id))
201 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
202 assertThat(posts[0].time, equalTo(1407197508000L))
203 assertThat(posts[0].recipientId, equalTo(of("1234567890123456789012345678901234567890123")))
204 assertThat(posts[0].text, equalTo("text"))
208 fun `parsing a sone succeeds with invalid recipient`() {
209 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-recipient.xml")
210 val posts = soneParser.parseSone(sone, inputStream)!!.posts
211 assertThat(posts, hasSize(1))
212 assertThat(posts[0].sone.id, equalTo(sone.id))
213 assertThat(posts[0].id, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
214 assertThat(posts[0].time, equalTo(1407197508000L))
215 assertThat(posts[0].recipientId, equalTo(absent()))
216 assertThat(posts[0].text, equalTo("text"))
220 fun `parsing a sone fails without post reply id`() {
221 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-id.xml")
222 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
226 fun `parsing a sone fails without post reply post id`() {
227 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-post-id.xml")
228 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
232 fun `parsing a sone fails without post reply time`() {
233 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-time.xml")
234 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
238 fun `parsing a sone fails without post reply text`() {
239 val inputStream = javaClass.getResourceAsStream("sone-parser-without-post-reply-text.xml")
240 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
244 fun `parsing a sone fails with invalid post reply time`() {
245 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml")
246 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
250 fun `parsing a sone succeeds with valid post reply time`() {
251 val inputStream = javaClass.getResourceAsStream("sone-parser-with-valid-post-reply-time.xml")
252 val postReplies = soneParser.parseSone(sone, inputStream)!!.replies
253 assertThat(postReplies, hasSize(1))
254 val postReply = postReplies.first()
255 assertThat(postReply.id, equalTo("5ccba7f4-aff0-11e9-b176-a7b9db60ce98"))
256 assertThat(postReply.postId, equalTo("3de12680-afef-11e9-a124-e713cf8912fe"))
257 assertThat(postReply.sone.id, equalTo("identity"))
258 assertThat(postReply.time, equalTo(1407197508000L))
259 assertThat(postReply.text, equalTo("reply-text"))
263 fun `parsing a sone succeeds without liked post ids`() {
264 val inputStream = javaClass.getResourceAsStream("sone-parser-without-liked-post-ids.xml")
265 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
269 fun `parsing a sone succeeds with liked post ids`() {
270 val inputStream = javaClass.getResourceAsStream("sone-parser-with-liked-post-ids.xml")
271 assertThat(soneParser.parseSone(sone, inputStream)!!.likedPostIds, equalTo(setOf("liked-post-id")))
275 fun `parsing a sone succeeds without liked post reply ids`() {
276 val inputStream = javaClass.getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml")
277 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
281 fun `parsing a sone succeeds with liked post reply ids`() {
282 val inputStream = javaClass.getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml")
283 assertThat(soneParser.parseSone(sone, inputStream)!!.likedReplyIds, equalTo(setOf("liked-post-reply-id")))
287 fun `parsing a sone succeeds without albums`() {
288 val inputStream = javaClass.getResourceAsStream("sone-parser-without-albums.xml")
289 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
293 fun `parsing a sone fails without album id`() {
294 val inputStream = javaClass.getResourceAsStream("sone-parser-without-album-id.xml")
295 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
299 fun `parsing a sone fails without album title`() {
300 val inputStream = javaClass.getResourceAsStream("sone-parser-without-album-title.xml")
301 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
305 fun `parsing a sone succeeds with nested albums`() {
306 val inputStream = javaClass.getResourceAsStream("sone-parser-with-multiple-albums.xml")
307 val parsedSone = soneParser.parseSone(sone, inputStream)
308 assertThat(parsedSone, notNullValue())
309 assertThat(parsedSone!!.rootAlbum.albums, hasSize(1))
310 val album = parsedSone.rootAlbum.albums[0]
311 assertThat(album.id, equalTo("album-id-1"))
312 assertThat(album.title, equalTo("album-title"))
313 assertThat(album.description, equalTo("album-description"))
314 assertThat(album.albums, hasSize(1))
315 val nestedAlbum = album.albums[0]
316 assertThat(nestedAlbum.id, equalTo("album-id-2"))
317 assertThat(nestedAlbum.title, equalTo("album-title-2"))
318 assertThat(nestedAlbum.description, equalTo("album-description-2"))
319 assertThat(nestedAlbum.albums, hasSize(0))
323 fun `parsing a sone fails with invalid parent album id`() {
324 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml")
325 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
329 fun `parsing a sone succeeds without images`() {
330 val inputStream = javaClass.getResourceAsStream("sone-parser-without-images.xml")
331 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
335 fun `parsing a sone fails without image id`() {
336 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-id.xml")
337 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
341 fun `parsing a sone fails without image time`() {
342 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-time.xml")
343 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
347 fun `parsing a sone fails without image key`() {
348 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-key.xml")
349 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
353 fun `parsing a sone fails without image title`() {
354 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-title.xml")
355 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
359 fun `parsing a sone fails without image width`() {
360 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-width.xml")
361 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
365 fun `parsing a sone fails without image height`() {
366 val inputStream = javaClass.getResourceAsStream("sone-parser-without-image-height.xml")
367 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
371 fun `parsing a sone fails with invalid image width`() {
372 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-width.xml")
373 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
377 fun `parsing a sone fails with invalid image height`() {
378 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-height.xml")
379 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
383 fun `parsing a sone succeeds with image`() {
384 val inputStream = javaClass.getResourceAsStream("sone-parser-with-image.xml")
385 val sone = soneParser.parseSone(this.sone, inputStream)
386 assertThat(sone, notNullValue())
387 assertThat(sone!!.rootAlbum.albums, hasSize(1))
388 assertThat(sone.rootAlbum.albums[0].images, hasSize(1))
389 val image = sone.rootAlbum.albums[0].images[0]
390 assertThat(image.id, equalTo("image-id"))
391 assertThat(image.creationTime, equalTo(1407197508000L))
392 assertThat(image.key, equalTo("KSK@GPLv3.txt"))
393 assertThat(image.title, equalTo("image-title"))
394 assertThat(image.description, equalTo("image-description"))
395 assertThat(image.width, equalTo(1920))
396 assertThat(image.height, equalTo(1080))
397 assertThat(sone.profile.avatar, equalTo("image-id"))
401 fun `unsuccessful parsing does not add a histogram entry`() {
402 val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-height.xml")
403 assertThat(soneParser.parseSone(sone, inputStream), nullValue())
404 val histogram = metricRegistry.histogram("sone.parse.duration")
405 assertThat(histogram.count, equalTo(0L))
409 fun `successful parsing adds histogram entry`() {
410 val inputStream = javaClass.getResourceAsStream("sone-parser-without-images.xml")
411 assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
412 val histogram = metricRegistry.histogram("sone.parse.duration")
413 assertThat(histogram.count, equalTo(1L))