Let core be an album provider
[Sone.git] / src / test / java / net / pterodactylus / sone / core / SoneParserTest.java
1 package net.pterodactylus.sone.core;
2
3 import static com.google.common.base.Optional.of;
4 import static freenet.keys.InsertableClientSSK.createRandom;
5 import static java.lang.System.currentTimeMillis;
6 import static java.util.UUID.randomUUID;
7 import static java.util.concurrent.TimeUnit.DAYS;
8 import static org.hamcrest.MatcherAssert.assertThat;
9 import static org.hamcrest.Matchers.hasSize;
10 import static org.hamcrest.Matchers.is;
11 import static org.hamcrest.Matchers.not;
12 import static org.hamcrest.Matchers.notNullValue;
13 import static org.hamcrest.Matchers.nullValue;
14 import static org.mockito.Matchers.any;
15 import static org.mockito.Matchers.anyLong;
16 import static org.mockito.Matchers.anyString;
17 import static org.mockito.Mockito.doAnswer;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.when;
20
21 import java.io.InputStream;
22 import java.net.MalformedURLException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import net.pterodactylus.sone.data.Album;
31 import net.pterodactylus.sone.data.Album.Modifier;
32 import net.pterodactylus.sone.data.Client;
33 import net.pterodactylus.sone.data.IdBuilder;
34 import net.pterodactylus.sone.data.Image;
35 import net.pterodactylus.sone.data.Post;
36 import net.pterodactylus.sone.data.PostReply;
37 import net.pterodactylus.sone.data.Profile;
38 import net.pterodactylus.sone.data.Sone;
39 import net.pterodactylus.sone.database.AlbumBuilder;
40 import net.pterodactylus.sone.database.ImageBuilder;
41 import net.pterodactylus.sone.database.PostBuilder;
42 import net.pterodactylus.sone.database.PostReplyBuilder;
43 import net.pterodactylus.sone.database.SoneBuilder;
44 import net.pterodactylus.sone.database.memory.MemorySoneBuilder;
45 import net.pterodactylus.sone.freenet.wot.Identity;
46 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
47
48 import freenet.crypt.DummyRandomSource;
49 import freenet.keys.FreenetURI;
50 import freenet.keys.InsertableClientSSK;
51
52 import com.google.common.base.Optional;
53 import com.google.common.collect.ArrayListMultimap;
54 import com.google.common.collect.ImmutableSet;
55 import com.google.common.collect.ListMultimap;
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.mockito.invocation.InvocationOnMock;
59 import org.mockito.stubbing.Answer;
60
61 /**
62  * Unit test for {@link SoneParser}.
63  *
64  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
65  */
66 public class SoneParserTest {
67
68         private final Core core = mock(Core.class);
69         private final SoneParser soneParser = new SoneParser(core);
70         private final Sone sone = mock(Sone.class);
71         private FreenetURI requestUri = mock(FreenetURI.class);
72         private final PostBuilder postBuilder = mock(PostBuilder.class);
73         private final List<Post> createdPosts = new ArrayList<Post>();
74         private Post post = mock(Post.class);
75         private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
76         private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
77         private PostReply postReply = mock(PostReply.class);
78         private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
79         private final ListMultimap<Album, Album>
80                         nestedAlbums = ArrayListMultimap.create();
81         private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
82         private Album album = mock(Album.class);
83         private final Map<String, Album> albums = new HashMap<String, Album>();
84         private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
85         private Image image = mock(Image.class);
86         private final Map<String, Image> images = new HashMap<String, Image>();
87
88         @Before
89         public void setupSone() {
90                 setupSone(this.sone, Identity.class);
91         }
92
93         private void setupSone(Sone sone, Class<? extends Identity> identityClass) {
94                 Identity identity = mock(identityClass);
95                 InsertableClientSSK clientSSK =
96                                 createRandom(new DummyRandomSource(), "WoT");
97                 when(identity.getRequestUri()).thenReturn(clientSSK.getURI().toString());
98                 when(identity.getId()).thenReturn("identity");
99                 when(sone.getId()).thenReturn("identity");
100                 when(sone.getIdentity()).thenReturn(identity);
101                 requestUri = clientSSK.getURI().setKeyType("USK").setDocName("Sone");
102                 when(sone.getRequestUri()).thenAnswer(new Answer<FreenetURI>() {
103                         @Override
104                         public FreenetURI answer(InvocationOnMock invocation)
105                         throws Throwable {
106                                 return requestUri;
107                         }
108                 });
109                 when(sone.getTime())
110                                 .thenReturn(currentTimeMillis() - DAYS.toMillis(1));
111         }
112
113         @Before
114         public void setupSoneBuilder() {
115                 when(core.soneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
116                         @Override
117                         public SoneBuilder answer(InvocationOnMock invocation) {
118                                 return new MemorySoneBuilder(null);
119                         }
120                 });
121         }
122
123         @Before
124         public void setupPost() {
125                 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
126         }
127
128         @Before
129         public void setupPostBuilder() {
130                 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
131                         @Override
132                         public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
133                                 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
134                                 return postBuilder;
135                         }
136                 });
137                 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
138                         @Override
139                         public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
140                                 final Sone sone = mock(Sone.class);
141                                 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
142                                 when(post.getSone()).thenReturn(sone);
143                                 return postBuilder;
144                         }
145                 });
146                 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
147                         @Override
148                         public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
149                                 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
150                                 return postBuilder;
151                         }
152                 });
153                 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
154                         @Override
155                         public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
156                                 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
157                                 return postBuilder;
158                         }
159                 });
160                 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
161                         @Override
162                         public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
163                                 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
164                                 return postBuilder;
165                         }
166                 });
167                 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
168                         @Override
169                         public Post answer(InvocationOnMock invocation) throws Throwable {
170                                 Post post = SoneParserTest.this.post;
171                                 SoneParserTest.this.post = mock(Post.class);
172                                 setupPost();
173                                 createdPosts.add(post);
174                                 return post;
175                         }
176                 });
177                 when(core.postBuilder()).thenReturn(postBuilder);
178         }
179
180         @Before
181         public void setupPostReplyBuilder() {
182                 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
183                         @Override
184                         public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
185                                 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
186                                 return postReplyBuilder;
187                         }
188                 });
189                 when(postReplyBuilder.from(anyString())).thenAnswer(
190                                 new Answer<PostReplyBuilder>() {
191                                         @Override
192                                         public PostReplyBuilder answer(
193                                                         InvocationOnMock invocation) throws Throwable {
194                                                 Sone sone = when(mock(Sone.class).getId()).thenReturn(
195                                                                 (String) invocation.getArguments()[0])
196                                                                 .getMock();
197                                                 when(postReply.getSone()).thenReturn(sone);
198                                                 return postReplyBuilder;
199                                         }
200                                 });
201                 when(postReplyBuilder.to(anyString())).thenAnswer(
202                                 new Answer<PostReplyBuilder>() {
203                                         @Override
204                                         public PostReplyBuilder answer(
205                                                         InvocationOnMock invocation) throws Throwable {
206                                                 when(postReply.getPostId()).thenReturn(
207                                                                 (String) invocation.getArguments()[0]);
208                                                 Post post = when(mock(Post.class).getId()).thenReturn(
209                                                                 (String) invocation.getArguments()[0])
210                                                                 .getMock();
211                                                 when(postReply.getPost()).thenReturn(of(post));
212                                                 return postReplyBuilder;
213                                         }
214                                 });
215                 when(postReplyBuilder.withTime(anyLong())).thenAnswer(
216                                 new Answer<PostReplyBuilder>() {
217                                         @Override
218                                         public PostReplyBuilder answer(
219                                                         InvocationOnMock invocation) throws Throwable {
220                                                 when(postReply.getTime()).thenReturn(
221                                                                 (Long) invocation.getArguments()[0]);
222                                                 return postReplyBuilder;
223                                         }
224                                 });
225                 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
226                         @Override
227                         public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
228                                 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
229                                 return postReplyBuilder;
230                         }
231                 });
232                 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
233                         @Override
234                         public PostReply answer(InvocationOnMock invocation) throws Throwable {
235                                 PostReply postReply = SoneParserTest.this.postReply;
236                                 createdPostReplies.add(postReply);
237                                 SoneParserTest.this.postReply = mock(PostReply.class);
238                                 return postReply;
239                         }
240                 });
241                 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
242         }
243
244         @Before
245         public void setupAlbum() {
246                 final Album album = SoneParserTest.this.album;
247                 when(album.getAlbumImage()).thenReturn(mock(Image.class));
248                 doAnswer(new Answer<Void>() {
249                         @Override
250                         public Void answer(InvocationOnMock invocation) {
251                                 nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
252                                 return null;
253                         }
254                 }).when(album).addAlbum(any(Album.class));
255                 doAnswer(new Answer<Void>() {
256                         @Override
257                         public Void answer(InvocationOnMock invocation) {
258                                 albumImages.put(album, (Image) invocation.getArguments()[0]);
259                                 return null;
260                         }
261                 }).when(album).addImage(any(Image.class));
262                 when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
263                         @Override
264                         public List<Album> answer(InvocationOnMock invocation) {
265                                 return nestedAlbums.get(album);
266                         }
267                 });
268                 when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
269                         @Override
270                         public List<Image> answer(InvocationOnMock invocation) {
271                                 return albumImages.get(album);
272                         }
273                 });
274                 final Modifier albumModifier = new Modifier() {
275                         private String title = album.getTitle();
276                         private String description = album.getDescription();
277                         private String imageId = album.getAlbumImage().getId();
278
279                         @Override
280                         public Modifier setTitle(String title) {
281                                 this.title = title;
282                                 return this;
283                         }
284
285                         @Override
286                         public Modifier setDescription(String description) {
287                                 this.description = description;
288                                 return this;
289                         }
290
291                         @Override
292                         public Modifier setAlbumImage(String imageId) {
293                                 this.imageId = imageId;
294                                 return this;
295                         }
296
297                         @Override
298                         public Album update() throws IllegalStateException {
299                                 when(album.getTitle()).thenReturn(title);
300                                 when(album.getDescription()).thenReturn(description);
301                                 Image image = mock(Image.class);
302                                 when(image.getId()).thenReturn(imageId);
303                                 when(album.getAlbumImage()).thenReturn(image);
304                                 return album;
305                         }
306                 };
307                 when(album.modify()).thenReturn(albumModifier);
308         }
309
310         @Before
311         public void setupAlbumBuilder() {
312                 when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
313                         @Override
314                         public AlbumBuilder answer(InvocationOnMock invocation) {
315                                 when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
316                                 return albumBuilder;
317                         }
318                 });
319                 when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
320                         @Override
321                         public AlbumBuilder answer(InvocationOnMock invocation) {
322                                 when(album.getId()).thenReturn(randomUUID().toString());
323                                 return albumBuilder;
324                         }
325                 });
326                 when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
327                         @Override
328                         public AlbumBuilder answer(InvocationOnMock invocation) {
329                                 when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
330                                 return albumBuilder;
331                         }
332                 });
333                 when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
334                         @Override
335                         public Album answer(InvocationOnMock invocation) {
336                                 Album album = SoneParserTest.this.album;
337                                 albums.put(album.getId(), album);
338                                 SoneParserTest.this.album = mock(Album.class);
339                                 setupAlbum();
340                                 return album;
341                         }
342                 });
343                 when(core.albumBuilder()).thenReturn(albumBuilder);
344         }
345
346         @Before
347         public void setupAlbums() {
348                 when(core.getAlbum(anyString())).thenAnswer(new Answer<Optional<Album>>() {
349                         @Override
350                         public Optional<Album> answer(InvocationOnMock invocation)
351                         throws Throwable {
352                                 return Optional.fromNullable(albums.get(invocation.getArguments()[0]));
353                         }
354                 });
355         }
356
357         @Before
358         public void setupImage() {
359                 final Image image = SoneParserTest.this.image;
360                 Image.Modifier modifier = new Image.Modifier() {
361                         private Sone sone = image.getSone();
362                         private long creationTime = image.getCreationTime();
363                         private String key = image.getKey();
364                         private String title = image.getTitle();
365                         private String description = image.getDescription();
366                         private int width = image.getWidth();
367                         private int height = image.getHeight();
368
369                         @Override
370                         public Image.Modifier setSone(Sone sone) {
371                                 this.sone = sone;
372                                 return this;
373                         }
374
375                         @Override
376                         public Image.Modifier setCreationTime(long creationTime) {
377                                 this.creationTime = creationTime;
378                                 return this;
379                         }
380
381                         @Override
382                         public Image.Modifier setKey(String key) {
383                                 this.key = key;
384                                 return this;
385                         }
386
387                         @Override
388                         public Image.Modifier setTitle(String title) {
389                                 this.title = title;
390                                 return this;
391                         }
392
393                         @Override
394                         public Image.Modifier setDescription(String description) {
395                                 this.description = description;
396                                 return this;
397                         }
398
399                         @Override
400                         public Image.Modifier setWidth(int width) {
401                                 this.width = width;
402                                 return this;
403                         }
404
405                         @Override
406                         public Image.Modifier setHeight(int height) {
407                                 this.height = height;
408                                 return this;
409                         }
410
411                         @Override
412                         public Image update() throws IllegalStateException {
413                                 when(image.getSone()).thenReturn(sone);
414                                 when(image.getCreationTime()).thenReturn(creationTime);
415                                 when(image.getKey()).thenReturn(key);
416                                 when(image.getTitle()).thenReturn(title);
417                                 when(image.getDescription()).thenReturn(description);
418                                 when(image.getWidth()).thenReturn(width);
419                                 when(image.getHeight()).thenReturn(height);
420                                 return image;
421                         }
422                 };
423                 when(image.getSone()).thenReturn(sone);
424                 when(image.modify()).thenReturn(modifier);
425         }
426
427         @Before
428         public void setupImageBuilder() {
429                 when(imageBuilder.randomId()).thenAnswer(new Answer<ImageBuilder>() {
430                         @Override
431                         public ImageBuilder answer(InvocationOnMock invocation) {
432                                 when(image.getId()).thenReturn(randomUUID().toString());
433                                 return imageBuilder;
434                         }
435                 });
436                 when(imageBuilder.withId(anyString())).thenAnswer(new Answer<ImageBuilder>() {
437                         @Override
438                         public ImageBuilder answer(InvocationOnMock invocation) {
439                                 when(image.getId()).thenReturn(new IdBuilder().buildId("identity", (String) invocation.getArguments()[0]));
440                                 when(image.getInternalId()).thenReturn((String) invocation.getArguments()[0]);
441                                 return imageBuilder;
442                         }
443                 });
444                 when(imageBuilder.build()).thenAnswer(new Answer<Image>() {
445                         @Override
446                         public Image answer(InvocationOnMock invocation) {
447                                 Image image = SoneParserTest.this.image;
448                                 images.put(image.getId(), image);
449                                 SoneParserTest.this.image = mock(Image.class);
450                                 setupImage();
451                                 return image;
452                         }
453                 });
454                 when(core.imageBuilder()).thenReturn(imageBuilder);
455         }
456
457         @Before
458         public void setupImages() {
459                 when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
460                         @Override
461                         public Image answer(InvocationOnMock invocation)
462                         throws Throwable {
463                                 return images.get(invocation.getArguments()[0]);
464                         }
465                 });
466         }
467         @Test
468         public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
469                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
470                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
471         }
472
473         @Test
474         public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
475                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
476                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
477         }
478
479         @Test
480         public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
481                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
482                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
483         }
484
485         @Test
486         public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
487                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
488                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
489         }
490
491         @Test
492         public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
493                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
494                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
495         }
496
497         @Test
498         public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
499                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
500                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
501         }
502
503         @Test
504         public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
505                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
506                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
507         }
508
509         @Test
510         public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
511                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
512                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
513         }
514
515         @Test
516         public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
517                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
518                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
519         }
520
521         @Test
522         public void parsingASoneSucceedsWithoutPayload() throws SoneException {
523                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
524                 assertThat(soneParser.parseSone(sone, inputStream).getTime(), is(
525                                 1407197508000L));
526         }
527
528         @Test
529         public void parsingALocalSoneSucceedsWithoutPayload() throws SoneException {
530                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
531                 Sone localSone = mock(Sone.class);
532                 setupSone(localSone, OwnIdentity.class);
533                 when(localSone.isLocal()).thenReturn(true);
534                 Sone parsedSone = soneParser.parseSone(localSone, inputStream);
535                 assertThat(parsedSone.getTime(), is(1407197508000L));
536                 assertThat(parsedSone.isLocal(), is(true));
537         }
538
539         @Test
540         public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
541                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
542                 assertThat(soneParser.parseSone(sone, inputStream), not(
543                                 nullValue()));
544         }
545
546         @Test
547         public void parsingASoneFailsWithMissingClientName() throws SoneException {
548                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
549                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
550         }
551
552         @Test
553         public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
554                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
555                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
556         }
557
558         @Test
559         public void parsingASoneSucceedsWithClientInfo() throws SoneException {
560                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
561                 assertThat(soneParser.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
562         }
563
564         @Test
565         public void parsingASoneSucceedsWithProfile() throws SoneException,
566         MalformedURLException {
567                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
568                 final Profile profile = soneParser.parseSone(sone, inputStream).getProfile();
569                 assertThat(profile.getFirstName(), is("first"));
570                 assertThat(profile.getMiddleName(), is("middle"));
571                 assertThat(profile.getLastName(), is("last"));
572                 assertThat(profile.getBirthDay(), is(18));
573                 assertThat(profile.getBirthMonth(), is(12));
574                 assertThat(profile.getBirthYear(), is(1976));
575         }
576
577         @Test
578         public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
579                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
580                 assertThat(soneParser.parseSone(sone, inputStream), notNullValue());
581         }
582
583         @Test
584         public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
585                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
586                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
587         }
588
589         @Test
590         public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
591                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
592                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
593         }
594
595         @Test
596         public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
597                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
598                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
599         }
600
601         @Test
602         public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
603                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
604                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
605         }
606
607         @Test
608         public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
609                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
610                 final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
611                 assertThat(posts, is(createdPosts));
612                 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
613                 assertThat(posts.get(0).getId(), is("post-id"));
614                 assertThat(posts.get(0).getTime(), is(1407197508000L));
615                 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
616                 assertThat(posts.get(0).getText(), is("text"));
617         }
618
619         @Test
620         public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
621                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
622                 final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
623                 assertThat(posts, is(createdPosts));
624                 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
625                 assertThat(posts.get(0).getId(), is("post-id"));
626                 assertThat(posts.get(0).getTime(), is(1407197508000L));
627                 assertThat(posts.get(0).getRecipientId(), is(of(
628                                 "1234567890123456789012345678901234567890123")));
629                 assertThat(posts.get(0).getText(), is("text"));
630         }
631
632         @Test
633         public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
634                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
635                 final List<Post> posts = soneParser.parseSone(sone, inputStream).getPosts();
636                 assertThat(posts, is(createdPosts));
637                 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
638                 assertThat(posts.get(0).getId(), is("post-id"));
639                 assertThat(posts.get(0).getTime(), is(1407197508000L));
640                 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
641                 assertThat(posts.get(0).getText(), is("text"));
642         }
643
644         @Test
645         public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
646                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
647                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
648         }
649
650         @Test
651         public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
652                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
653                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
654         }
655
656         @Test
657         public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
658                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
659                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
660         }
661
662         @Test
663         public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
664                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
665                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
666         }
667
668         @Test
669         public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
670                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
671                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
672         }
673
674         @Test
675         public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
676                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
677                 final Set<PostReply> postReplies = soneParser.parseSone(sone, inputStream).getReplies();
678                 assertThat(postReplies, is(createdPostReplies));
679                 PostReply postReply = createdPostReplies.iterator().next();
680                 assertThat(postReply.getId(), is("reply-id"));
681                 assertThat(postReply.getPostId(), is("post-id"));
682                 assertThat(postReply.getPost().get().getId(), is("post-id"));
683                 assertThat(postReply.getSone().getId(), is("identity"));
684                 assertThat(postReply.getTime(), is(1407197508000L));
685                 assertThat(postReply.getText(), is("reply-text"));
686         }
687
688         @Test
689         public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
690                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
691                 assertThat(soneParser.parseSone(sone, inputStream), not(
692                                 nullValue()));
693         }
694
695         @Test
696         public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
697                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
698                 assertThat(soneParser.parseSone(sone, inputStream).getLikedPostIds(), is(
699                                 (Set<String>) ImmutableSet.of("liked-post-id")));
700         }
701
702         @Test
703         public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
704                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
705                 assertThat(soneParser.parseSone(sone, inputStream), not(
706                                 nullValue()));
707         }
708
709         @Test
710         public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
711                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
712                 assertThat(soneParser.parseSone(sone, inputStream).getLikedReplyIds(), is(
713                                 (Set<String>) ImmutableSet.of("liked-post-reply-id")));
714         }
715
716         @Test
717         public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
718                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
719                 assertThat(soneParser.parseSone(sone, inputStream), not(
720                                 nullValue()));
721         }
722
723         @Test
724         public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
725                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
726                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
727         }
728
729         @Test
730         public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
731                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
732                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
733         }
734
735         @Test
736         public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
737                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
738                 final Sone parsedSone = soneParser.parseSone(sone, inputStream);
739                 assertThat(parsedSone, not(nullValue()));
740                 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
741                 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
742                 assertThat(album.getId(), is("album-id-1"));
743                 assertThat(album.getTitle(), is("album-title"));
744                 assertThat(album.getDescription(), is("album-description"));
745                 assertThat(album.getAlbums(), hasSize(1));
746                 Album nestedAlbum = album.getAlbums().get(0);
747                 assertThat(nestedAlbum.getId(), is("album-id-2"));
748                 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
749                 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
750                 assertThat(nestedAlbum.getAlbums(), hasSize(0));
751         }
752
753         @Test
754         public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
755                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
756                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
757         }
758
759         @Test
760         public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
761                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
762                 assertThat(soneParser.parseSone(sone, inputStream), not(
763                                 nullValue()));
764         }
765
766         @Test
767         public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
768                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
769                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
770         }
771
772         @Test
773         public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
774                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
775                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
776         }
777
778         @Test
779         public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
780                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
781                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
782         }
783
784         @Test
785         public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
786                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
787                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
788         }
789
790         @Test
791         public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
792                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
793                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
794         }
795
796         @Test
797         public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
798                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
799                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
800         }
801
802         @Test
803         public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
804                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
805                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
806         }
807
808         @Test
809         public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
810                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
811                 assertThat(soneParser.parseSone(sone, inputStream), nullValue());
812         }
813
814         @Test
815         public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
816                 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
817                 final Sone sone = soneParser.parseSone(this.sone, inputStream);
818                 assertThat(sone, not(nullValue()));
819                 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
820                 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
821                 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
822                 assertThat(image.getId(), is(new IdBuilder().buildId("identity", "image-id")));
823                 assertThat(image.getInternalId(), is("image-id"));
824                 assertThat(image.getCreationTime(), is(1407197508000L));
825                 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
826                 assertThat(image.getTitle(), is("image-title"));
827                 assertThat(image.getDescription(), is("image-description"));
828                 assertThat(image.getWidth(), is(1920));
829                 assertThat(image.getHeight(), is(1080));
830                 assertThat(sone.getProfile().getAvatar(), is("image-id"));
831         }
832
833
834 }