1 package net.pterodactylus.sone.core;
3 import static com.google.common.base.Optional.of;
4 import static java.util.UUID.randomUUID;
5 import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
6 import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
7 import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
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.ArgumentCaptor.forClass;
15 import static org.mockito.Matchers.any;
16 import static org.mockito.Matchers.anyBoolean;
17 import static org.mockito.Matchers.anyLong;
18 import static org.mockito.Matchers.anyString;
19 import static org.mockito.Matchers.eq;
20 import static org.mockito.Mockito.doAnswer;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.times;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.net.MalformedURLException;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
37 import net.pterodactylus.sone.core.FreenetInterface.Fetched;
38 import net.pterodactylus.sone.data.Album;
39 import net.pterodactylus.sone.data.Album.Modifier;
40 import net.pterodactylus.sone.data.Client;
41 import net.pterodactylus.sone.data.Image;
42 import net.pterodactylus.sone.data.ImageImpl;
43 import net.pterodactylus.sone.data.Post;
44 import net.pterodactylus.sone.data.PostReply;
45 import net.pterodactylus.sone.data.Profile;
46 import net.pterodactylus.sone.data.Sone;
47 import net.pterodactylus.sone.data.Sone.SoneStatus;
48 import net.pterodactylus.sone.database.AlbumBuilder;
49 import net.pterodactylus.sone.database.PostBuilder;
50 import net.pterodactylus.sone.database.PostReplyBuilder;
51 import net.pterodactylus.sone.freenet.wot.Identity;
53 import freenet.client.ClientMetadata;
54 import freenet.client.FetchResult;
55 import freenet.keys.FreenetURI;
56 import freenet.support.api.Bucket;
58 import com.google.common.base.Optional;
59 import com.google.common.collect.ArrayListMultimap;
60 import com.google.common.collect.ImmutableSet;
61 import com.google.common.collect.ListMultimap;
62 import org.junit.Before;
63 import org.junit.Test;
64 import org.mockito.ArgumentCaptor;
65 import org.mockito.invocation.InvocationOnMock;
66 import org.mockito.stubbing.Answer;
69 * Unit test for {@link SoneDownloaderImpl} and its subclasses.
71 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
73 public class SoneDownloaderTest {
75 private final Core core = mock(Core.class);
76 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
77 private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface);
78 private final FreenetURI requestUri = mock(FreenetURI.class);
79 private final Sone sone = mock(Sone.class);
80 private final PostBuilder postBuilder = mock(PostBuilder.class);
81 private final List<Post> createdPosts = new ArrayList<Post>();
82 private Post post = mock(Post.class);
83 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
84 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
85 private PostReply postReply = mock(PostReply.class);
86 private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
87 private final ListMultimap<Album, Album> nestedAlbums = ArrayListMultimap.create();
88 private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
89 private Album album = mock(Album.class);
90 private final Map<String, Album> albums = new HashMap<String, Album>();
93 public void setupSone() {
94 Identity identity = mock(Identity.class);
95 when(identity.getId()).thenReturn("identity");
96 when(sone.getId()).thenReturn("identity");
97 when(sone.getIdentity()).thenReturn(identity);
98 when(sone.getRequestUri()).thenReturn(requestUri);
102 public void setupRequestUri() {
103 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
104 when(requestUri.sskForUSK()).thenReturn(requestUri);
105 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
106 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
107 when(requestUri.getKeyType()).thenReturn("USK");
111 public void setupPost() {
112 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
116 public void setupPostBuilder() {
117 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
119 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
120 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
124 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
126 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
127 final Sone sone = mock(Sone.class);
128 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
129 when(post.getSone()).thenReturn(sone);
133 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
135 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
136 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
140 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
142 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
143 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
147 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
149 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
150 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
154 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
156 public Post answer(InvocationOnMock invocation) throws Throwable {
157 Post post = SoneDownloaderTest.this.post;
158 SoneDownloaderTest.this.post = mock(Post.class);
160 createdPosts.add(post);
164 when(core.postBuilder()).thenReturn(postBuilder);
168 public void setupPostReplyBuilder() {
169 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
171 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
172 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
173 return postReplyBuilder;
176 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
178 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
179 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
180 when(postReply.getSone()).thenReturn(sone);
181 return postReplyBuilder;
184 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
186 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
187 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
188 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
189 when(postReply.getPost()).thenReturn(of(post));
190 return postReplyBuilder;
193 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
195 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
196 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
197 return postReplyBuilder;
200 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
202 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
203 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
204 return postReplyBuilder;
207 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
209 public PostReply answer(InvocationOnMock invocation) throws Throwable {
210 PostReply postReply = SoneDownloaderTest.this.postReply;
211 createdPostReplies.add(postReply);
212 SoneDownloaderTest.this.postReply = mock(PostReply.class);
216 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
220 public void setupAlbum() {
224 private void setupAlbum(final Album album) {
225 when(album.getAlbumImage()).thenReturn(mock(Image.class));
226 doAnswer(new Answer<Void>() {
228 public Void answer(InvocationOnMock invocation) {
229 nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
232 }).when(album).addAlbum(any(Album.class));
233 doAnswer(new Answer<Void>() {
235 public Void answer(InvocationOnMock invocation) {
236 albumImages.put(album, (Image) invocation.getArguments()[0]);
239 }).when(album).addImage(any(Image.class));
240 when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
242 public List<Album> answer(InvocationOnMock invocation) {
243 return nestedAlbums.get(album);
246 when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
248 public List<Image> answer(InvocationOnMock invocation) {
249 return albumImages.get(album);
252 final Modifier albumModifier = new Modifier() {
253 private String title = album.getTitle();
254 private String description = album.getDescription();
255 private String imageId = album.getAlbumImage().getId();
258 public Modifier setTitle(String title) {
264 public Modifier setDescription(String description) {
265 this.description = description;
270 public Modifier setAlbumImage(String imageId) {
271 this.imageId = imageId;
276 public Album update() throws IllegalStateException {
277 when(album.getTitle()).thenReturn(title);
278 when(album.getDescription()).thenReturn(description);
279 Image image = mock(Image.class);
280 when(image.getId()).thenReturn(imageId);
281 when(album.getAlbumImage()).thenReturn(image);
285 when(album.modify()).thenReturn(albumModifier);
289 public void setupAlbumBuilder() {
290 when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
292 public AlbumBuilder answer(InvocationOnMock invocation) {
293 when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
297 when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
299 public AlbumBuilder answer(InvocationOnMock invocation) {
300 when(album.getId()).thenReturn(randomUUID().toString());
304 when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
306 public AlbumBuilder answer(InvocationOnMock invocation) {
307 when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
311 when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
313 public Album answer(InvocationOnMock invocation) {
314 Album album = SoneDownloaderTest.this.album;
315 albums.put(album.getId(), album);
316 SoneDownloaderTest.this.album = mock(Album.class);
317 setupAlbum(SoneDownloaderTest.this.album);
321 when(core.albumBuilder()).thenReturn(albumBuilder);
325 public void setupAlbums() {
326 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
328 public Album answer(InvocationOnMock invocation) throws Throwable {
329 return albums.get(invocation.getArguments()[0]);
335 public void setupImages() {
336 Image image = new ImageImpl("image-id");
337 when(core.getImage("image-id")).thenReturn(image);
338 when(core.getImage(eq("image-id"), anyBoolean())).thenReturn(image);
342 public void addingASoneWillRegisterItsKey() {
343 soneDownloader.addSone(sone);
344 verify(freenetInterface).registerUsk(sone, soneDownloader);
345 verify(freenetInterface, never()).unregisterUsk(sone);
349 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
350 soneDownloader.addSone(sone);
351 soneDownloader.addSone(sone);
352 verify(freenetInterface, times(2)).registerUsk(sone, soneDownloader);
353 verify(freenetInterface).unregisterUsk(sone);
357 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
358 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
359 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
363 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
364 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
365 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
369 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
370 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
371 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
375 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
376 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
377 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
381 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
382 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
383 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
387 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
388 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
389 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
393 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
394 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
395 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
399 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
400 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
401 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
405 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
406 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
407 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
411 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
412 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
413 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
417 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
418 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
419 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
423 public void parsingASoneFailsWithMissingClientName() throws SoneException {
424 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
425 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
429 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
430 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
431 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
435 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
436 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
437 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
441 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
442 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
443 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
447 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
448 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
449 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
453 public void soneInsertUriIsCopiedToNewSone() throws SoneException {
454 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
455 FreenetURI insertUri = mock(FreenetURI.class);
456 when(insertUri.setKeyType(anyString())).thenReturn(insertUri);
457 when(insertUri.setDocName(anyString())).thenReturn(insertUri);
458 when(insertUri.setMetaString(any(String[].class))).thenReturn(insertUri);
459 when(insertUri.setSuggestedEdition(anyLong())).thenReturn(insertUri);
460 when(sone.getInsertUri()).thenReturn(insertUri);
461 assertThat(soneDownloader.parseSone(sone, inputStream).getInsertUri(), is(insertUri));
465 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
466 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
467 final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
468 assertThat(profile.getFirstName(), is("first"));
469 assertThat(profile.getMiddleName(), is("middle"));
470 assertThat(profile.getLastName(), is("last"));
471 assertThat(profile.getBirthDay(), is(18));
472 assertThat(profile.getBirthMonth(), is(12));
473 assertThat(profile.getBirthYear(), is(1976));
477 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
478 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
479 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
483 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
484 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
485 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
489 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
490 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
491 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
495 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
496 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
497 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
501 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
502 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
503 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
507 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
508 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
509 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
510 assertThat(posts, is(createdPosts));
511 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
512 assertThat(posts.get(0).getId(), is("post-id"));
513 assertThat(posts.get(0).getTime(), is(1407197508000L));
514 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
515 assertThat(posts.get(0).getText(), is("text"));
519 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
520 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
521 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
522 assertThat(posts, is(createdPosts));
523 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
524 assertThat(posts.get(0).getId(), is("post-id"));
525 assertThat(posts.get(0).getTime(), is(1407197508000L));
526 assertThat(posts.get(0).getRecipientId(), is(of("1234567890123456789012345678901234567890123")));
527 assertThat(posts.get(0).getText(), is("text"));
531 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
532 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
533 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
534 assertThat(posts, is(createdPosts));
535 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
536 assertThat(posts.get(0).getId(), is("post-id"));
537 assertThat(posts.get(0).getTime(), is(1407197508000L));
538 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
539 assertThat(posts.get(0).getText(), is("text"));
543 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
544 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
545 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
549 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
550 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
551 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
555 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
556 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
557 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
561 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
562 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
563 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
567 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
568 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
569 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
573 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
574 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
575 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
576 assertThat(postReplies, is(createdPostReplies));
577 PostReply postReply = createdPostReplies.iterator().next();
578 assertThat(postReply.getId(), is("reply-id"));
579 assertThat(postReply.getPostId(), is("post-id"));
580 assertThat(postReply.getPost().get().getId(), is("post-id"));
581 assertThat(postReply.getSone().getId(), is("identity"));
582 assertThat(postReply.getTime(), is(1407197508000L));
583 assertThat(postReply.getText(), is("reply-text"));
587 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
588 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
589 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
593 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
594 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
595 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
599 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
600 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
601 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
605 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
606 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
607 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
611 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
612 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
613 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
617 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
618 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
619 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
623 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
624 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
625 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
629 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
630 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
631 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
632 assertThat(parsedSone, not(nullValue()));
633 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
634 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
635 assertThat(album.getId(), is("album-id-1"));
636 assertThat(album.getTitle(), is("album-title"));
637 assertThat(album.getDescription(), is("album-description"));
638 assertThat(album.getAlbums(), hasSize(1));
639 Album nestedAlbum = album.getAlbums().get(0);
640 assertThat(nestedAlbum.getId(), is("album-id-2"));
641 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
642 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
643 assertThat(nestedAlbum.getAlbums(), hasSize(0));
647 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
648 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
649 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
653 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
654 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
655 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
659 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
660 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
661 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
665 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
666 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
667 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
671 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
672 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
673 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
677 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
678 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
679 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
683 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
684 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
685 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
689 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
690 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
691 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
695 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
696 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
697 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
701 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
702 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
703 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
707 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
708 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
709 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
710 assertThat(sone, not(nullValue()));
711 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
712 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
713 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
714 assertThat(image.getId(), is("image-id"));
715 assertThat(image.getCreationTime(), is(1407197508000L));
716 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
717 assertThat(image.getTitle(), is("image-title"));
718 assertThat(image.getDescription(), is("image-description"));
719 assertThat(image.getWidth(), is(1920));
720 assertThat(image.getHeight(), is(1080));
721 assertThat(sone.getProfile().getAvatar(), is("image-id"));
725 public void stoppingTheSoneDownloaderUnregistersTheSone() {
726 soneDownloader.addSone(sone);
727 soneDownloader.stop();
728 verify(freenetInterface).unregisterUsk(sone);
732 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
733 soneDownloader.fetchSone(sone);
734 verify(freenetInterface).fetchUri(requestUri);
735 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
736 verify(core, never()).updateSone(any(Sone.class));
739 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
740 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
741 verify(sone, times(2)).setStatus(soneStatuses.capture());
742 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
743 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
747 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
748 when(sone.getTime()).thenReturn(1000L);
749 soneDownloader.fetchSone(sone);
750 verify(freenetInterface).fetchUri(requestUri);
751 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
752 verify(core, never()).updateSone(any(Sone.class));
755 @Test(expected = NullPointerException.class)
756 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
757 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
759 soneDownloader.fetchSone(sone);
761 verify(freenetInterface).fetchUri(requestUri);
762 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
763 verify(core, never()).updateSone(any(Sone.class));
767 @Test(expected = NullPointerException.class)
768 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
769 when(sone.getTime()).thenReturn(1000L);
770 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
772 soneDownloader.fetchSone(sone);
774 verify(freenetInterface).fetchUri(requestUri);
775 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
776 verify(core, never()).updateSone(any(Sone.class));
781 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
782 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
783 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
784 soneDownloader.fetchSone(sone);
785 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
788 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
789 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
790 verify(core).updateSone(soneCaptor.capture());
791 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
795 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
796 when(requestUri.getKeyType()).thenReturn("SSK");
797 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
798 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
799 soneDownloader.fetchSone(sone);
800 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
804 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
805 when(requestUri.getKeyType()).thenReturn("SSK");
806 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
807 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
808 soneDownloader.fetchSone(sone);
809 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
813 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
814 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
815 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
816 soneDownloader.fetchSone(sone);
817 verify(core, never()).updateSone(any(Sone.class));
821 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
822 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
823 when(sone.getId()).thenThrow(NullPointerException.class);
824 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
825 soneDownloader.fetchSone(sone);
826 verify(core, never()).updateSone(any(Sone.class));
830 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
831 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
832 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
833 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
834 verify(core, never()).updateSone(any(Sone.class));
835 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
838 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
839 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
840 Bucket bucket = mock(Bucket.class);
841 when(bucket.getInputStream()).thenReturn(inputStream);
842 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
843 return new Fetched(uri, fetchResult);