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.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.*
21 * Unit test for [SoneParser].
23 class SoneParserTest {
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>()
32 setupSone(this.sone, Identity::class.java)
33 database.storeSone(sone)
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))
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())
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())
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())
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())
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())
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())
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())
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())
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())
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))
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))
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())
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())
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())
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")))
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))
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())
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())
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())
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())
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())
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"))
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"))
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"))
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())
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())
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())
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())
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())
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"))
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())
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")))
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())
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")))
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())
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())
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())
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))
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())
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())
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())
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())
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())
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())
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())
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())
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())
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())
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"))
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))
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))