1 package net.pterodactylus.sone.core;
3 import static com.google.common.base.Optional.of;
4 import static java.lang.System.currentTimeMillis;
5 import static java.util.UUID.randomUUID;
6 import static java.util.concurrent.TimeUnit.DAYS;
7 import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
8 import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
9 import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
10 import static org.hamcrest.MatcherAssert.assertThat;
11 import static org.hamcrest.Matchers.hasSize;
12 import static org.hamcrest.Matchers.is;
13 import static org.hamcrest.Matchers.not;
14 import static org.hamcrest.Matchers.notNullValue;
15 import static org.hamcrest.Matchers.nullValue;
16 import static org.mockito.ArgumentCaptor.forClass;
17 import static org.mockito.Matchers.any;
18 import static org.mockito.Matchers.anyLong;
19 import static org.mockito.Matchers.anyString;
20 import static org.mockito.Matchers.eq;
21 import static org.mockito.Mockito.doAnswer;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.never;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.MalformedURLException;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
38 import net.pterodactylus.sone.core.FreenetInterface.Fetched;
39 import net.pterodactylus.sone.data.Album;
40 import net.pterodactylus.sone.data.Album.Modifier;
41 import net.pterodactylus.sone.data.Client;
42 import net.pterodactylus.sone.data.Image;
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.ImageBuilder;
50 import net.pterodactylus.sone.database.PostBuilder;
51 import net.pterodactylus.sone.database.PostReplyBuilder;
52 import net.pterodactylus.sone.freenet.wot.Identity;
54 import freenet.client.ClientMetadata;
55 import freenet.client.FetchResult;
56 import freenet.client.async.USKCallback;
57 import freenet.keys.FreenetURI;
58 import freenet.support.api.Bucket;
60 import com.google.common.base.Optional;
61 import com.google.common.collect.ArrayListMultimap;
62 import com.google.common.collect.ImmutableSet;
63 import com.google.common.collect.ListMultimap;
64 import org.junit.Before;
65 import org.junit.Test;
66 import org.mockito.ArgumentCaptor;
67 import org.mockito.invocation.InvocationOnMock;
68 import org.mockito.stubbing.Answer;
71 * Unit test for {@link SoneDownloaderImpl} and its subclasses.
73 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
75 public class SoneDownloaderTest {
77 private final Core core = mock(Core.class);
78 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
79 private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface);
80 private final FreenetURI requestUri = mock(FreenetURI.class);
81 private final Sone sone = mock(Sone.class);
82 private final PostBuilder postBuilder = mock(PostBuilder.class);
83 private final List<Post> createdPosts = new ArrayList<Post>();
84 private Post post = mock(Post.class);
85 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
86 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
87 private PostReply postReply = mock(PostReply.class);
88 private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
89 private final ListMultimap<Album, Album> nestedAlbums = ArrayListMultimap.create();
90 private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
91 private Album album = mock(Album.class);
92 private final Map<String, Album> albums = new HashMap<String, Album>();
93 private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
94 private Image image = mock(Image.class);
95 private final Map<String, Image> images = new HashMap<String, Image>();
98 public void setupSone() {
99 Identity identity = mock(Identity.class);
100 when(identity.getId()).thenReturn("identity");
101 when(sone.getId()).thenReturn("identity");
102 when(sone.getIdentity()).thenReturn(identity);
103 when(sone.getRequestUri()).thenReturn(requestUri);
104 when(sone.getTime()).thenReturn(currentTimeMillis() - DAYS.toMillis(1));
107 private void setupSoneAsUnknown() {
108 when(sone.getTime()).thenReturn(0L);
112 public void setupRequestUri() {
113 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
114 when(requestUri.sskForUSK()).thenReturn(requestUri);
115 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
116 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
117 when(requestUri.getKeyType()).thenReturn("USK");
121 public void setupPost() {
122 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
126 public void setupPostBuilder() {
127 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
129 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
130 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
134 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
136 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
137 final Sone sone = mock(Sone.class);
138 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
139 when(post.getSone()).thenReturn(sone);
143 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
145 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
146 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
150 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
152 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
153 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
157 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
159 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
160 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
164 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
166 public Post answer(InvocationOnMock invocation) throws Throwable {
167 Post post = SoneDownloaderTest.this.post;
168 SoneDownloaderTest.this.post = mock(Post.class);
170 createdPosts.add(post);
174 when(core.postBuilder()).thenReturn(postBuilder);
178 public void setupPostReplyBuilder() {
179 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
181 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
182 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
183 return postReplyBuilder;
186 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
188 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
189 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
190 when(postReply.getSone()).thenReturn(sone);
191 return postReplyBuilder;
194 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
196 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
197 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
198 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
199 when(postReply.getPost()).thenReturn(of(post));
200 return postReplyBuilder;
203 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
205 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
206 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
207 return postReplyBuilder;
210 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
212 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
213 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
214 return postReplyBuilder;
217 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
219 public PostReply answer(InvocationOnMock invocation) throws Throwable {
220 PostReply postReply = SoneDownloaderTest.this.postReply;
221 createdPostReplies.add(postReply);
222 SoneDownloaderTest.this.postReply = mock(PostReply.class);
226 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
230 public void setupAlbum() {
231 final Album album = SoneDownloaderTest.this.album;
232 when(album.getAlbumImage()).thenReturn(mock(Image.class));
233 doAnswer(new Answer<Void>() {
235 public Void answer(InvocationOnMock invocation) {
236 nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
239 }).when(album).addAlbum(any(Album.class));
240 doAnswer(new Answer<Void>() {
242 public Void answer(InvocationOnMock invocation) {
243 albumImages.put(album, (Image) invocation.getArguments()[0]);
246 }).when(album).addImage(any(Image.class));
247 when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
249 public List<Album> answer(InvocationOnMock invocation) {
250 return nestedAlbums.get(album);
253 when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
255 public List<Image> answer(InvocationOnMock invocation) {
256 return albumImages.get(album);
259 final Modifier albumModifier = new Modifier() {
260 private String title = album.getTitle();
261 private String description = album.getDescription();
262 private String imageId = album.getAlbumImage().getId();
265 public Modifier setTitle(String title) {
271 public Modifier setDescription(String description) {
272 this.description = description;
277 public Modifier setAlbumImage(String imageId) {
278 this.imageId = imageId;
283 public Album update() throws IllegalStateException {
284 when(album.getTitle()).thenReturn(title);
285 when(album.getDescription()).thenReturn(description);
286 Image image = mock(Image.class);
287 when(image.getId()).thenReturn(imageId);
288 when(album.getAlbumImage()).thenReturn(image);
292 when(album.modify()).thenReturn(albumModifier);
296 public void setupAlbumBuilder() {
297 when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
299 public AlbumBuilder answer(InvocationOnMock invocation) {
300 when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
304 when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
306 public AlbumBuilder answer(InvocationOnMock invocation) {
307 when(album.getId()).thenReturn(randomUUID().toString());
311 when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
313 public AlbumBuilder answer(InvocationOnMock invocation) {
314 when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
318 when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
320 public Album answer(InvocationOnMock invocation) {
321 Album album = SoneDownloaderTest.this.album;
322 albums.put(album.getId(), album);
323 SoneDownloaderTest.this.album = mock(Album.class);
328 when(core.albumBuilder()).thenReturn(albumBuilder);
332 public void setupAlbums() {
333 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
335 public Album answer(InvocationOnMock invocation) throws Throwable {
336 return albums.get(invocation.getArguments()[0]);
342 public void setupImage() {
343 final Image image = SoneDownloaderTest.this.image;
344 Image.Modifier modifier = new Image.Modifier() {
345 private Sone sone = image.getSone();
346 private long creationTime = image.getCreationTime();
347 private String key = image.getKey();
348 private String title = image.getTitle();
349 private String description = image.getDescription();
350 private int width = image.getWidth();
351 private int height = image.getHeight();
354 public Image.Modifier setSone(Sone sone) {
360 public Image.Modifier setCreationTime(long creationTime) {
361 this.creationTime = creationTime;
366 public Image.Modifier setKey(String key) {
372 public Image.Modifier setTitle(String title) {
378 public Image.Modifier setDescription(String description) {
379 this.description = description;
384 public Image.Modifier setWidth(int width) {
390 public Image.Modifier setHeight(int height) {
391 this.height = height;
396 public Image update() throws IllegalStateException {
397 when(image.getSone()).thenReturn(sone);
398 when(image.getCreationTime()).thenReturn(creationTime);
399 when(image.getKey()).thenReturn(key);
400 when(image.getTitle()).thenReturn(title);
401 when(image.getDescription()).thenReturn(description);
402 when(image.getWidth()).thenReturn(width);
403 when(image.getHeight()).thenReturn(height);
407 when(image.getSone()).thenReturn(sone);
408 when(image.modify()).thenReturn(modifier);
412 public void setupImageBuilder() {
413 when(imageBuilder.randomId()).thenAnswer(new Answer<ImageBuilder>() {
415 public ImageBuilder answer(InvocationOnMock invocation) {
416 when(image.getId()).thenReturn(randomUUID().toString());
420 when(imageBuilder.withId(anyString())).thenAnswer(new Answer<ImageBuilder>() {
422 public ImageBuilder answer(InvocationOnMock invocation) {
423 when(image.getId()).thenReturn(
424 (String) invocation.getArguments()[0]);
428 when(imageBuilder.build()).thenAnswer(new Answer<Image>() {
430 public Image answer(InvocationOnMock invocation) {
431 Image image = SoneDownloaderTest.this.image;
432 images.put(image.getId(), image);
433 SoneDownloaderTest.this.image = mock(Image.class);
438 when(core.imageBuilder()).thenReturn(imageBuilder);
442 public void setupImages() {
443 when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
445 public Image answer(InvocationOnMock invocation)
447 return images.get(invocation.getArguments()[0]);
453 public void addingASoneWillRegisterItsKey() {
454 soneDownloader.addSone(sone);
455 verify(freenetInterface).registerActiveUsk(eq(sone.getRequestUri()), any(
457 verify(freenetInterface, never()).unregisterUsk(sone);
461 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
462 soneDownloader.addSone(sone);
463 soneDownloader.addSone(sone);
464 verify(freenetInterface, times(2)).registerActiveUsk(eq(
465 sone.getRequestUri()), any(USKCallback.class));
466 verify(freenetInterface).unregisterUsk(sone);
470 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
471 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
472 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
476 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
477 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
478 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
482 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
483 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
484 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
488 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
489 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
490 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
494 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
495 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
496 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
500 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
501 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
502 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
506 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
507 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
508 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
512 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
513 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
514 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
518 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
519 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
520 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
524 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
525 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
526 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
530 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
531 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
532 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
536 public void parsingASoneFailsWithMissingClientName() throws SoneException {
537 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
538 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
542 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
543 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
544 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
548 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
549 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
550 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
554 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
555 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
556 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
560 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
561 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
562 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
566 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
567 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
568 final Profile profile = soneDownloader.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));
578 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
579 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
580 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
584 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
585 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
586 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
590 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
591 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
592 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
596 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
597 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
598 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
602 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
603 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
604 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
608 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
609 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
610 final List<Post> posts = soneDownloader.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"));
620 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
621 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
622 final List<Post> posts = soneDownloader.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("1234567890123456789012345678901234567890123")));
628 assertThat(posts.get(0).getText(), is("text"));
632 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
633 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
634 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
635 assertThat(posts, is(createdPosts));
636 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
637 assertThat(posts.get(0).getId(), is("post-id"));
638 assertThat(posts.get(0).getTime(), is(1407197508000L));
639 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
640 assertThat(posts.get(0).getText(), is("text"));
644 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
645 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
646 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
650 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
651 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
652 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
656 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
657 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
658 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
662 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
663 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
664 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
668 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
669 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
670 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
674 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
675 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
676 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
677 assertThat(postReplies, is(createdPostReplies));
678 PostReply postReply = createdPostReplies.iterator().next();
679 assertThat(postReply.getId(), is("reply-id"));
680 assertThat(postReply.getPostId(), is("post-id"));
681 assertThat(postReply.getPost().get().getId(), is("post-id"));
682 assertThat(postReply.getSone().getId(), is("identity"));
683 assertThat(postReply.getTime(), is(1407197508000L));
684 assertThat(postReply.getText(), is("reply-text"));
688 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
689 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
690 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
694 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
695 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
696 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
700 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
701 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
702 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
706 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
707 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
708 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
712 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
713 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
714 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
718 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
719 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
720 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
724 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
725 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
726 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
730 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
731 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
732 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
733 assertThat(parsedSone, not(nullValue()));
734 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
735 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
736 assertThat(album.getId(), is("album-id-1"));
737 assertThat(album.getTitle(), is("album-title"));
738 assertThat(album.getDescription(), is("album-description"));
739 assertThat(album.getAlbums(), hasSize(1));
740 Album nestedAlbum = album.getAlbums().get(0);
741 assertThat(nestedAlbum.getId(), is("album-id-2"));
742 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
743 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
744 assertThat(nestedAlbum.getAlbums(), hasSize(0));
748 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
749 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
750 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
754 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
755 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
756 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
760 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
761 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
762 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
766 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
767 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
768 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
772 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
773 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
774 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
778 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
779 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
780 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
784 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
785 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
786 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
790 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
791 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
792 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
796 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
797 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
798 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
802 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
803 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
804 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
808 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
809 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
810 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
811 assertThat(sone, not(nullValue()));
812 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
813 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
814 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
815 assertThat(image.getId(), is("image-id"));
816 assertThat(image.getCreationTime(), is(1407197508000L));
817 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
818 assertThat(image.getTitle(), is("image-title"));
819 assertThat(image.getDescription(), is("image-description"));
820 assertThat(image.getWidth(), is(1920));
821 assertThat(image.getHeight(), is(1080));
822 assertThat(sone.getProfile().getAvatar(), is("image-id"));
826 public void stoppingTheSoneDownloaderUnregistersTheSone() {
827 soneDownloader.addSone(sone);
828 soneDownloader.stop();
829 verify(freenetInterface).unregisterUsk(sone);
833 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
834 setupSoneAsUnknown();
835 soneDownloader.fetchSoneAction(sone).run();
836 verify(freenetInterface).fetchUri(requestUri);
837 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
838 verify(core, never()).updateSone(any(Sone.class));
841 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
842 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
843 verify(sone, times(2)).setStatus(soneStatuses.capture());
844 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
845 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
849 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
850 soneDownloader.fetchSoneAction(sone).run();
851 verify(freenetInterface).fetchUri(requestUri);
852 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
853 verify(core, never()).updateSone(any(Sone.class));
856 @Test(expected = NullPointerException.class)
857 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
858 setupSoneAsUnknown();
859 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
861 soneDownloader.fetchSoneAction(sone).run();
863 verify(freenetInterface).fetchUri(requestUri);
864 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
865 verify(core, never()).updateSone(any(Sone.class));
869 @Test(expected = NullPointerException.class)
870 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
871 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
873 soneDownloader.fetchSoneAction(sone).run();
875 verify(freenetInterface).fetchUri(requestUri);
876 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
877 verify(core, never()).updateSone(any(Sone.class));
882 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
883 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
884 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
885 soneDownloader.fetchSoneAction(sone).run();
886 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
889 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
890 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
891 verify(core).updateSone(soneCaptor.capture());
892 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
896 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
897 when(requestUri.getKeyType()).thenReturn("SSK");
898 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
899 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
900 soneDownloader.fetchSoneAction(sone).run();
901 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
905 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
906 when(requestUri.getKeyType()).thenReturn("SSK");
907 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
908 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
909 soneDownloader.fetchSoneAction(sone).run();
910 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
914 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
915 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
916 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
917 soneDownloader.fetchSoneAction(sone).run();
918 verify(core, never()).updateSone(any(Sone.class));
922 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
923 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
924 when(sone.getId()).thenThrow(NullPointerException.class);
925 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
926 soneDownloader.fetchSoneAction(sone).run();
927 verify(core, never()).updateSone(any(Sone.class));
931 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
932 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
933 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
934 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
935 verify(core, never()).updateSone(any(Sone.class));
936 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
939 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
940 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
941 Bucket bucket = mock(Bucket.class);
942 when(bucket.getInputStream()).thenReturn(inputStream);
943 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
944 return new Fetched(uri, fetchResult);