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 SoneUpdater soneUpdater = mock(SoneUpdater.class);
79 private final FreenetURI requestUri = mock(FreenetURI.class);
80 private final Sone sone = mock(Sone.class);
81 private final PostBuilder postBuilder = mock(PostBuilder.class);
82 private final List<Post> createdPosts = new ArrayList<Post>();
83 private Post post = mock(Post.class);
84 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
85 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
86 private PostReply postReply = mock(PostReply.class);
87 private final AlbumBuilder albumBuilder = mock(AlbumBuilder.class);
88 private final ListMultimap<Album, Album> nestedAlbums = ArrayListMultimap.create();
89 private final ListMultimap<Album, Image> albumImages = ArrayListMultimap.create();
90 private Album album = mock(Album.class);
91 private final Map<String, Album> albums = new HashMap<String, Album>();
94 public void setupSone() {
95 Identity identity = mock(Identity.class);
96 when(identity.getId()).thenReturn("identity");
97 when(sone.getId()).thenReturn("identity");
98 when(sone.getIdentity()).thenReturn(identity);
99 when(sone.getRequestUri()).thenReturn(requestUri);
103 public void setupRequestUri() {
104 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
105 when(requestUri.sskForUSK()).thenReturn(requestUri);
106 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
107 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
108 when(requestUri.getKeyType()).thenReturn("USK");
112 public void setupPost() {
113 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
117 public void setupPostBuilder() {
118 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
120 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
121 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
125 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
127 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
128 final Sone sone = mock(Sone.class);
129 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
130 when(post.getSone()).thenReturn(sone);
134 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
136 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
137 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
141 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
143 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
144 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
148 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
150 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
151 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
155 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
157 public Post answer(InvocationOnMock invocation) throws Throwable {
158 Post post = SoneDownloaderTest.this.post;
159 SoneDownloaderTest.this.post = mock(Post.class);
161 createdPosts.add(post);
165 when(core.postBuilder()).thenReturn(postBuilder);
169 public void setupPostReplyBuilder() {
170 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
172 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
173 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
174 return postReplyBuilder;
177 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
179 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
180 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
181 when(postReply.getSone()).thenReturn(sone);
182 return postReplyBuilder;
185 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
187 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
188 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
189 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
190 when(postReply.getPost()).thenReturn(of(post));
191 return postReplyBuilder;
194 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
196 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
197 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
198 return postReplyBuilder;
201 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
203 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
204 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
205 return postReplyBuilder;
208 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
210 public PostReply answer(InvocationOnMock invocation) throws Throwable {
211 PostReply postReply = SoneDownloaderTest.this.postReply;
212 createdPostReplies.add(postReply);
213 SoneDownloaderTest.this.postReply = mock(PostReply.class);
217 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
221 public void setupAlbum() {
225 private void setupAlbum(final Album album) {
226 when(album.getAlbumImage()).thenReturn(mock(Image.class));
227 doAnswer(new Answer<Void>() {
229 public Void answer(InvocationOnMock invocation) {
230 nestedAlbums.put(album, (Album) invocation.getArguments()[0]);
233 }).when(album).addAlbum(any(Album.class));
234 doAnswer(new Answer<Void>() {
236 public Void answer(InvocationOnMock invocation) {
237 albumImages.put(album, (Image) invocation.getArguments()[0]);
240 }).when(album).addImage(any(Image.class));
241 when(album.getAlbums()).thenAnswer(new Answer<List<Album>>() {
243 public List<Album> answer(InvocationOnMock invocation) {
244 return nestedAlbums.get(album);
247 when(album.getImages()).thenAnswer(new Answer<List<Image>>() {
249 public List<Image> answer(InvocationOnMock invocation) {
250 return albumImages.get(album);
253 final Modifier albumModifier = new Modifier() {
254 private String title = album.getTitle();
255 private String description = album.getDescription();
256 private String imageId = album.getAlbumImage().getId();
259 public Modifier setTitle(String title) {
265 public Modifier setDescription(String description) {
266 this.description = description;
271 public Modifier setAlbumImage(String imageId) {
272 this.imageId = imageId;
277 public Album update() throws IllegalStateException {
278 when(album.getTitle()).thenReturn(title);
279 when(album.getDescription()).thenReturn(description);
280 Image image = mock(Image.class);
281 when(image.getId()).thenReturn(imageId);
282 when(album.getAlbumImage()).thenReturn(image);
286 when(album.modify()).thenReturn(albumModifier);
290 public void setupAlbumBuilder() {
291 when(albumBuilder.withId(anyString())).thenAnswer(new Answer<AlbumBuilder>() {
293 public AlbumBuilder answer(InvocationOnMock invocation) {
294 when(album.getId()).thenReturn((String) invocation.getArguments()[0]);
298 when(albumBuilder.randomId()).thenAnswer(new Answer<AlbumBuilder>() {
300 public AlbumBuilder answer(InvocationOnMock invocation) {
301 when(album.getId()).thenReturn(randomUUID().toString());
305 when(albumBuilder.by(any(Sone.class))).thenAnswer(new Answer<AlbumBuilder>() {
307 public AlbumBuilder answer(InvocationOnMock invocation) {
308 when(album.getSone()).thenReturn((Sone) invocation.getArguments()[0]);
312 when(albumBuilder.build()).thenAnswer(new Answer<Album>() {
314 public Album answer(InvocationOnMock invocation) {
315 Album album = SoneDownloaderTest.this.album;
316 albums.put(album.getId(), album);
317 SoneDownloaderTest.this.album = mock(Album.class);
318 setupAlbum(SoneDownloaderTest.this.album);
322 when(core.albumBuilder()).thenReturn(albumBuilder);
326 public void setupAlbums() {
327 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
329 public Album answer(InvocationOnMock invocation) throws Throwable {
330 return albums.get(invocation.getArguments()[0]);
336 public void setupImages() {
337 Image image = new ImageImpl("image-id");
338 when(core.getImage("image-id")).thenReturn(image);
339 when(core.getImage(eq("image-id"), anyBoolean())).thenReturn(image);
343 public void addingASoneWillRegisterItsKey() {
344 soneDownloader.addSone(sone);
345 verify(freenetInterface).registerUsk(eq(sone), any(SoneUpdater.class));
346 verify(freenetInterface, never()).unregisterUsk(sone);
350 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
351 soneDownloader.addSone(sone);
352 soneDownloader.addSone(sone);
353 verify(freenetInterface, times(2)).registerUsk(eq(sone), any(SoneUpdater.class));
354 verify(freenetInterface).unregisterUsk(sone);
358 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
359 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
360 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
364 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
365 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
366 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
370 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
371 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
372 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
376 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
377 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
378 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
382 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
383 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
384 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
388 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
389 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
390 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
394 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
395 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
396 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
400 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
401 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
402 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
406 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
407 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
408 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
412 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
413 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
414 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
418 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
419 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
420 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
424 public void parsingASoneFailsWithMissingClientName() throws SoneException {
425 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
426 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
430 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
431 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
432 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
436 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
437 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
438 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
442 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
443 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
444 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
448 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
449 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
450 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
454 public void soneInsertUriIsCopiedToNewSone() throws SoneException {
455 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
456 FreenetURI insertUri = mock(FreenetURI.class);
457 when(insertUri.setKeyType(anyString())).thenReturn(insertUri);
458 when(insertUri.setDocName(anyString())).thenReturn(insertUri);
459 when(insertUri.setMetaString(any(String[].class))).thenReturn(insertUri);
460 when(insertUri.setSuggestedEdition(anyLong())).thenReturn(insertUri);
461 when(sone.getInsertUri()).thenReturn(insertUri);
462 assertThat(soneDownloader.parseSone(sone, inputStream).getInsertUri(), is(insertUri));
466 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
467 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
468 final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
469 assertThat(profile.getFirstName(), is("first"));
470 assertThat(profile.getMiddleName(), is("middle"));
471 assertThat(profile.getLastName(), is("last"));
472 assertThat(profile.getBirthDay(), is(18));
473 assertThat(profile.getBirthMonth(), is(12));
474 assertThat(profile.getBirthYear(), is(1976));
478 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
479 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
480 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
484 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
485 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
486 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
490 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
491 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
492 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
496 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
497 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
498 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
502 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
503 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
504 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
508 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
509 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
510 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
511 assertThat(posts, is(createdPosts));
512 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
513 assertThat(posts.get(0).getId(), is("post-id"));
514 assertThat(posts.get(0).getTime(), is(1407197508000L));
515 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
516 assertThat(posts.get(0).getText(), is("text"));
520 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
521 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
522 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
523 assertThat(posts, is(createdPosts));
524 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
525 assertThat(posts.get(0).getId(), is("post-id"));
526 assertThat(posts.get(0).getTime(), is(1407197508000L));
527 assertThat(posts.get(0).getRecipientId(), is(of("1234567890123456789012345678901234567890123")));
528 assertThat(posts.get(0).getText(), is("text"));
532 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
533 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
534 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
535 assertThat(posts, is(createdPosts));
536 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
537 assertThat(posts.get(0).getId(), is("post-id"));
538 assertThat(posts.get(0).getTime(), is(1407197508000L));
539 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
540 assertThat(posts.get(0).getText(), is("text"));
544 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
545 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
546 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
550 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
551 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
552 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
556 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
557 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
558 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
562 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
563 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
564 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
568 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
569 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
570 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
574 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
575 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
576 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
577 assertThat(postReplies, is(createdPostReplies));
578 PostReply postReply = createdPostReplies.iterator().next();
579 assertThat(postReply.getId(), is("reply-id"));
580 assertThat(postReply.getPostId(), is("post-id"));
581 assertThat(postReply.getPost().get().getId(), is("post-id"));
582 assertThat(postReply.getSone().getId(), is("identity"));
583 assertThat(postReply.getTime(), is(1407197508000L));
584 assertThat(postReply.getText(), is("reply-text"));
588 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
589 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
590 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
594 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
595 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
596 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
600 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
601 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
602 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
606 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
607 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
608 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
612 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
613 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
614 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
618 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
619 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
620 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
624 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
625 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
626 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
630 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
631 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
632 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
633 assertThat(parsedSone, not(nullValue()));
634 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
635 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
636 assertThat(album.getId(), is("album-id-1"));
637 assertThat(album.getTitle(), is("album-title"));
638 assertThat(album.getDescription(), is("album-description"));
639 assertThat(album.getAlbums(), hasSize(1));
640 Album nestedAlbum = album.getAlbums().get(0);
641 assertThat(nestedAlbum.getId(), is("album-id-2"));
642 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
643 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
644 assertThat(nestedAlbum.getAlbums(), hasSize(0));
648 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
649 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
650 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
654 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
655 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
656 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
660 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
661 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
662 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
666 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
667 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
668 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
672 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
673 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
674 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
678 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
679 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
680 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
684 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
685 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
686 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
690 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
691 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
692 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
696 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
697 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
698 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
702 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
703 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
704 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
708 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
709 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
710 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
711 assertThat(sone, not(nullValue()));
712 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
713 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
714 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
715 assertThat(image.getId(), is("image-id"));
716 assertThat(image.getCreationTime(), is(1407197508000L));
717 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
718 assertThat(image.getTitle(), is("image-title"));
719 assertThat(image.getDescription(), is("image-description"));
720 assertThat(image.getWidth(), is(1920));
721 assertThat(image.getHeight(), is(1080));
722 assertThat(sone.getProfile().getAvatar(), is("image-id"));
726 public void stoppingTheSoneDownloaderUnregistersTheSone() {
727 soneDownloader.addSone(sone);
728 soneDownloader.stop();
729 verify(freenetInterface).unregisterUsk(sone);
733 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
734 soneDownloader.fetchSoneAction(sone).run();
735 verify(freenetInterface).fetchUri(requestUri);
736 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
737 verify(core, never()).updateSone(any(Sone.class));
740 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
741 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
742 verify(sone, times(2)).setStatus(soneStatuses.capture());
743 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
744 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
748 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
749 when(sone.getTime()).thenReturn(1000L);
750 soneDownloader.fetchSoneAction(sone).run();
751 verify(freenetInterface).fetchUri(requestUri);
752 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
753 verify(core, never()).updateSone(any(Sone.class));
756 @Test(expected = NullPointerException.class)
757 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
758 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
760 soneDownloader.fetchSoneAction(sone).run();
762 verify(freenetInterface).fetchUri(requestUri);
763 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
764 verify(core, never()).updateSone(any(Sone.class));
768 @Test(expected = NullPointerException.class)
769 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
770 when(sone.getTime()).thenReturn(1000L);
771 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
773 soneDownloader.fetchSoneAction(sone).run();
775 verify(freenetInterface).fetchUri(requestUri);
776 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
777 verify(core, never()).updateSone(any(Sone.class));
782 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
783 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
784 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
785 soneDownloader.fetchSoneAction(sone).run();
786 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
789 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
790 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
791 verify(core).updateSone(soneCaptor.capture());
792 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
796 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
797 when(requestUri.getKeyType()).thenReturn("SSK");
798 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
799 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
800 soneDownloader.fetchSoneAction(sone).run();
801 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
805 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
806 when(requestUri.getKeyType()).thenReturn("SSK");
807 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
808 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
809 soneDownloader.fetchSoneAction(sone).run();
810 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
814 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
815 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
816 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
817 soneDownloader.fetchSoneAction(sone).run();
818 verify(core, never()).updateSone(any(Sone.class));
822 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
823 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
824 when(sone.getId()).thenThrow(NullPointerException.class);
825 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
826 soneDownloader.fetchSoneAction(sone).run();
827 verify(core, never()).updateSone(any(Sone.class));
831 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
832 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
833 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
834 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
835 verify(core, never()).updateSone(any(Sone.class));
836 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
839 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
840 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
841 Bucket bucket = mock(Bucket.class);
842 when(bucket.getInputStream()).thenReturn(inputStream);
843 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
844 return new Fetched(uri, fetchResult);