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.database.SoneBuilder;
53 import net.pterodactylus.sone.database.memory.MemorySoneBuilder;
54 import net.pterodactylus.sone.freenet.wot.Identity;
56 import freenet.client.ClientMetadata;
57 import freenet.client.FetchResult;
58 import freenet.client.async.USKCallback;
59 import freenet.keys.FreenetURI;
60 import freenet.support.api.Bucket;
62 import com.google.common.base.Optional;
63 import com.google.common.collect.ArrayListMultimap;
64 import com.google.common.collect.ImmutableSet;
65 import com.google.common.collect.ListMultimap;
66 import org.junit.Before;
67 import org.junit.Test;
68 import org.mockito.ArgumentCaptor;
69 import org.mockito.invocation.InvocationOnMock;
70 import org.mockito.stubbing.Answer;
73 * Unit test for {@link SoneDownloaderImpl} and its subclasses.
75 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
77 public class SoneDownloaderTest {
79 private final Core core = mock(Core.class);
80 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
81 private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface);
82 private final FreenetURI requestUri = mock(FreenetURI.class);
83 private Sone sone = mock(Sone.class);
84 private final PostBuilder postBuilder = mock(PostBuilder.class);
85 private final List<Post> createdPosts = new ArrayList<Post>();
86 private Post post = mock(Post.class);
87 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
88 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
89 private PostReply postReply = mock(PostReply.class);
90 private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
91 private final ListMultimap<Album, Album> nestedAlbums = ArrayListMultimap.create();
92 private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
93 private Album album = mock(Album.class);
94 private final Map<String, Album> albums = new HashMap<String, Album>();
95 private final ImageBuilder imageBuilder = mock(ImageBuilder.class);
96 private Image image = mock(Image.class);
97 private final Map<String, Image> images = new HashMap<String, Image>();
100 public void setupSone() {
101 Sone sone = SoneDownloaderTest.this.sone;
102 Identity identity = mock(Identity.class);
103 when(identity.getId()).thenReturn("identity");
104 when(sone.getId()).thenReturn("identity");
105 when(sone.getIdentity()).thenReturn(identity);
106 when(sone.getRequestUri()).thenReturn(requestUri);
107 when(sone.getTime()).thenReturn(currentTimeMillis() - DAYS.toMillis(1));
110 private void setupSoneAsUnknown() {
111 when(sone.getTime()).thenReturn(0L);
115 public void setupRequestUri() {
116 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
117 when(requestUri.sskForUSK()).thenReturn(requestUri);
118 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
119 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
120 when(requestUri.getKeyType()).thenReturn("USK");
124 public void setupSoneBuilder() {
125 when(core.soneBuilder()).thenAnswer(new Answer<SoneBuilder>() {
127 public SoneBuilder answer(InvocationOnMock invocation) {
128 return new MemorySoneBuilder();
134 public void setupPost() {
135 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
139 public void setupPostBuilder() {
140 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
142 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
143 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
147 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
149 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
150 final Sone sone = mock(Sone.class);
151 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
152 when(post.getSone()).thenReturn(sone);
156 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
158 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
159 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
163 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
165 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
166 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
170 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
172 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
173 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
177 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
179 public Post answer(InvocationOnMock invocation) throws Throwable {
180 Post post = SoneDownloaderTest.this.post;
181 SoneDownloaderTest.this.post = mock(Post.class);
183 createdPosts.add(post);
187 when(core.postBuilder()).thenReturn(postBuilder);
191 public void setupPostReplyBuilder() {
192 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
194 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
195 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
196 return postReplyBuilder;
199 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
201 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
202 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
203 when(postReply.getSone()).thenReturn(sone);
204 return postReplyBuilder;
207 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
209 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
210 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
211 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
212 when(postReply.getPost()).thenReturn(of(post));
213 return postReplyBuilder;
216 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
218 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
219 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
220 return postReplyBuilder;
223 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
225 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
226 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
227 return postReplyBuilder;
230 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
232 public PostReply answer(InvocationOnMock invocation) throws Throwable {
233 PostReply postReply = SoneDownloaderTest.this.postReply;
234 createdPostReplies.add(postReply);
235 SoneDownloaderTest.this.postReply = mock(PostReply.class);
239 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
243 public void setupAlbum() {
244 final Album album = SoneDownloaderTest.this.album;
245 when(album.getAlbumImage()).thenReturn(mock(Image.class));
246 doAnswer(new Answer<Void>() {
248 public Void answer(InvocationOnMock invocation) {
249 nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
252 }).when(album).addAlbum(any(Album.class));
253 doAnswer(new Answer<Void>() {
255 public Void answer(InvocationOnMock invocation) {
256 albumImages.put(album, (Image) invocation.getArguments()[0]);
259 }).when(album).addImage(any(Image.class));
260 when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
262 public List<Album> answer(InvocationOnMock invocation) {
263 return nestedAlbums.get(album);
266 when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
268 public List<Image> answer(InvocationOnMock invocation) {
269 return albumImages.get(album);
272 final Modifier albumModifier = new Modifier() {
273 private String title = album.getTitle();
274 private String description = album.getDescription();
275 private String imageId = album.getAlbumImage().getId();
278 public Modifier setTitle(String title) {
284 public Modifier setDescription(String description) {
285 this.description = description;
290 public Modifier setAlbumImage(String imageId) {
291 this.imageId = imageId;
296 public Album update() throws IllegalStateException {
297 when(album.getTitle()).thenReturn(title);
298 when(album.getDescription()).thenReturn(description);
299 Image image = mock(Image.class);
300 when(image.getId()).thenReturn(imageId);
301 when(album.getAlbumImage()).thenReturn(image);
305 when(album.modify()).thenReturn(albumModifier);
309 public void setupAlbumBuilder() {
310 when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
312 public AlbumBuilder answer(InvocationOnMock invocation) {
313 when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
317 when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
319 public AlbumBuilder answer(InvocationOnMock invocation) {
320 when(album.getId()).thenReturn(randomUUID().toString());
324 when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
326 public AlbumBuilder answer(InvocationOnMock invocation) {
327 when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
331 when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
333 public Album answer(InvocationOnMock invocation) {
334 Album album = SoneDownloaderTest.this.album;
335 albums.put(album.getId(), album);
336 SoneDownloaderTest.this.album = mock(Album.class);
341 when(core.albumBuilder()).thenReturn(albumBuilder);
345 public void setupAlbums() {
346 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
348 public Album answer(InvocationOnMock invocation) throws Throwable {
349 return albums.get(invocation.getArguments()[0]);
355 public void setupImage() {
356 final Image image = SoneDownloaderTest.this.image;
357 Image.Modifier modifier = new Image.Modifier() {
358 private Sone sone = image.getSone();
359 private long creationTime = image.getCreationTime();
360 private String key = image.getKey();
361 private String title = image.getTitle();
362 private String description = image.getDescription();
363 private int width = image.getWidth();
364 private int height = image.getHeight();
367 public Image.Modifier setSone(Sone sone) {
373 public Image.Modifier setCreationTime(long creationTime) {
374 this.creationTime = creationTime;
379 public Image.Modifier setKey(String key) {
385 public Image.Modifier setTitle(String title) {
391 public Image.Modifier setDescription(String description) {
392 this.description = description;
397 public Image.Modifier setWidth(int width) {
403 public Image.Modifier setHeight(int height) {
404 this.height = height;
409 public Image update() throws IllegalStateException {
410 when(image.getSone()).thenReturn(sone);
411 when(image.getCreationTime()).thenReturn(creationTime);
412 when(image.getKey()).thenReturn(key);
413 when(image.getTitle()).thenReturn(title);
414 when(image.getDescription()).thenReturn(description);
415 when(image.getWidth()).thenReturn(width);
416 when(image.getHeight()).thenReturn(height);
420 when(image.getSone()).thenReturn(sone);
421 when(image.modify()).thenReturn(modifier);
425 public void setupImageBuilder() {
426 when(imageBuilder.randomId()).thenAnswer(new Answer<ImageBuilder>() {
428 public ImageBuilder answer(InvocationOnMock invocation) {
429 when(image.getId()).thenReturn(randomUUID().toString());
433 when(imageBuilder.withId(anyString())).thenAnswer(new Answer<ImageBuilder>() {
435 public ImageBuilder answer(InvocationOnMock invocation) {
436 when(image.getId()).thenReturn(
437 (String) invocation.getArguments()[0]);
441 when(imageBuilder.build()).thenAnswer(new Answer<Image>() {
443 public Image answer(InvocationOnMock invocation) {
444 Image image = SoneDownloaderTest.this.image;
445 images.put(image.getId(), image);
446 SoneDownloaderTest.this.image = mock(Image.class);
451 when(core.imageBuilder()).thenReturn(imageBuilder);
455 public void setupImages() {
456 when(core.getImage(anyString())).thenAnswer(new Answer<Image>() {
458 public Image answer(InvocationOnMock invocation)
460 return images.get(invocation.getArguments()[0]);
466 public void addingASoneWillRegisterItsKey() {
467 soneDownloader.addSone(sone);
468 verify(freenetInterface).registerActiveUsk(eq(sone.getRequestUri()), any(
470 verify(freenetInterface, never()).unregisterUsk(sone);
474 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
475 soneDownloader.addSone(sone);
476 soneDownloader.addSone(sone);
477 verify(freenetInterface, times(2)).registerActiveUsk(eq(
478 sone.getRequestUri()), any(USKCallback.class));
479 verify(freenetInterface).unregisterUsk(sone);
483 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
484 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
485 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
489 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
490 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
491 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
495 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
496 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
497 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
501 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
502 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
503 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
507 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
508 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
509 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
513 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
514 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
515 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
519 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
520 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
521 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
525 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
526 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
527 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
531 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
532 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
533 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
537 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
538 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
539 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
543 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
544 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
545 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
549 public void parsingASoneFailsWithMissingClientName() throws SoneException {
550 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
551 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
555 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
556 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
557 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
561 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
562 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
563 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
567 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
568 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
569 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
573 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
574 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
575 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
579 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
580 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
581 final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
582 assertThat(profile.getFirstName(), is("first"));
583 assertThat(profile.getMiddleName(), is("middle"));
584 assertThat(profile.getLastName(), is("last"));
585 assertThat(profile.getBirthDay(), is(18));
586 assertThat(profile.getBirthMonth(), is(12));
587 assertThat(profile.getBirthYear(), is(1976));
591 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
592 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
593 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
597 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
598 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
599 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
603 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
604 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
605 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
609 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
610 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
611 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
615 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
616 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
617 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
621 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
622 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
623 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
624 assertThat(posts, is(createdPosts));
625 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
626 assertThat(posts.get(0).getId(), is("post-id"));
627 assertThat(posts.get(0).getTime(), is(1407197508000L));
628 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
629 assertThat(posts.get(0).getText(), is("text"));
633 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
634 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
635 final List<Post> posts = soneDownloader.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(of("1234567890123456789012345678901234567890123")));
641 assertThat(posts.get(0).getText(), is("text"));
645 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
646 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
647 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
648 assertThat(posts, is(createdPosts));
649 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
650 assertThat(posts.get(0).getId(), is("post-id"));
651 assertThat(posts.get(0).getTime(), is(1407197508000L));
652 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
653 assertThat(posts.get(0).getText(), is("text"));
657 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
658 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
659 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
663 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
664 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
665 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
669 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
670 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
671 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
675 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
676 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
677 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
681 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
682 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
683 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
687 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
688 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
689 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
690 assertThat(postReplies, is(createdPostReplies));
691 PostReply postReply = createdPostReplies.iterator().next();
692 assertThat(postReply.getId(), is("reply-id"));
693 assertThat(postReply.getPostId(), is("post-id"));
694 assertThat(postReply.getPost().get().getId(), is("post-id"));
695 assertThat(postReply.getSone().getId(), is("identity"));
696 assertThat(postReply.getTime(), is(1407197508000L));
697 assertThat(postReply.getText(), is("reply-text"));
701 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
702 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
703 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
707 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
708 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
709 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
713 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
714 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
715 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
719 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
720 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
721 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
725 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
726 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
727 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
731 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
732 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
733 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
737 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
738 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
739 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
743 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
744 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
745 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
746 assertThat(parsedSone, not(nullValue()));
747 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
748 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
749 assertThat(album.getId(), is("album-id-1"));
750 assertThat(album.getTitle(), is("album-title"));
751 assertThat(album.getDescription(), is("album-description"));
752 assertThat(album.getAlbums(), hasSize(1));
753 Album nestedAlbum = album.getAlbums().get(0);
754 assertThat(nestedAlbum.getId(), is("album-id-2"));
755 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
756 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
757 assertThat(nestedAlbum.getAlbums(), hasSize(0));
761 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
762 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
763 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
767 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
768 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
769 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
773 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
774 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
775 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
779 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
780 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
781 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
785 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
786 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
787 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
791 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
792 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
793 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
797 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
798 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
799 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
803 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
804 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
805 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
809 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
810 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
811 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
815 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
816 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
817 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
821 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
822 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
823 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
824 assertThat(sone, not(nullValue()));
825 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
826 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
827 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
828 assertThat(image.getId(), is("image-id"));
829 assertThat(image.getCreationTime(), is(1407197508000L));
830 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
831 assertThat(image.getTitle(), is("image-title"));
832 assertThat(image.getDescription(), is("image-description"));
833 assertThat(image.getWidth(), is(1920));
834 assertThat(image.getHeight(), is(1080));
835 assertThat(sone.getProfile().getAvatar(), is("image-id"));
839 public void stoppingTheSoneDownloaderUnregistersTheSone() {
840 soneDownloader.addSone(sone);
841 soneDownloader.stop();
842 verify(freenetInterface).unregisterUsk(sone);
846 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
847 setupSoneAsUnknown();
848 soneDownloader.fetchSoneAction(sone).run();
849 verify(freenetInterface).fetchUri(requestUri);
850 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
851 verify(core, never()).updateSone(any(Sone.class));
854 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
855 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
856 verify(sone, times(2)).setStatus(soneStatuses.capture());
857 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
858 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
862 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
863 soneDownloader.fetchSoneAction(sone).run();
864 verify(freenetInterface).fetchUri(requestUri);
865 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
866 verify(core, never()).updateSone(any(Sone.class));
869 @Test(expected = NullPointerException.class)
870 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
871 setupSoneAsUnknown();
872 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
874 soneDownloader.fetchSoneAction(sone).run();
876 verify(freenetInterface).fetchUri(requestUri);
877 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
878 verify(core, never()).updateSone(any(Sone.class));
882 @Test(expected = NullPointerException.class)
883 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
884 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
886 soneDownloader.fetchSoneAction(sone).run();
888 verify(freenetInterface).fetchUri(requestUri);
889 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
890 verify(core, never()).updateSone(any(Sone.class));
895 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
896 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
897 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
898 soneDownloader.fetchSoneAction(sone).run();
899 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
902 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
903 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
904 verify(core).updateSone(soneCaptor.capture());
905 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
909 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
910 when(requestUri.getKeyType()).thenReturn("SSK");
911 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
912 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
913 soneDownloader.fetchSoneAction(sone).run();
914 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
918 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
919 when(requestUri.getKeyType()).thenReturn("SSK");
920 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
921 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
922 soneDownloader.fetchSoneAction(sone).run();
923 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
927 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
928 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
929 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
930 soneDownloader.fetchSoneAction(sone).run();
931 verify(core, never()).updateSone(any(Sone.class));
935 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
936 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
937 when(core.soneBuilder()).thenReturn(null);
938 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
939 soneDownloader.fetchSoneAction(sone).run();
940 verify(core, never()).updateSone(any(Sone.class));
944 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
945 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
946 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
947 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
948 verify(core, never()).updateSone(any(Sone.class));
949 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
952 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
953 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
954 Bucket bucket = mock(Bucket.class);
955 when(bucket.getInputStream()).thenReturn(inputStream);
956 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
957 return new Fetched(uri, fetchResult);