1 package net.pterodactylus.sone.core;
3 import static com.google.common.base.Optional.of;
4 import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
5 import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
6 import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
7 import static org.hamcrest.MatcherAssert.assertThat;
8 import static org.hamcrest.Matchers.hasSize;
9 import static org.hamcrest.Matchers.is;
10 import static org.hamcrest.Matchers.not;
11 import static org.hamcrest.Matchers.notNullValue;
12 import static org.hamcrest.Matchers.nullValue;
13 import static org.mockito.ArgumentCaptor.forClass;
14 import static org.mockito.Matchers.any;
15 import static org.mockito.Matchers.anyBoolean;
16 import static org.mockito.Matchers.anyLong;
17 import static org.mockito.Matchers.anyString;
18 import static org.mockito.Matchers.eq;
19 import static org.mockito.Mockito.mock;
20 import static org.mockito.Mockito.never;
21 import static org.mockito.Mockito.times;
22 import static org.mockito.Mockito.verify;
23 import static org.mockito.Mockito.when;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.net.MalformedURLException;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
35 import net.pterodactylus.sone.core.FreenetInterface.Fetched;
36 import net.pterodactylus.sone.core.SoneDownloader.FetchSone;
37 import net.pterodactylus.sone.core.SoneDownloader.FetchSoneWithUri;
38 import net.pterodactylus.sone.data.Album;
39 import net.pterodactylus.sone.data.AlbumImpl;
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.PostBuilder;
49 import net.pterodactylus.sone.database.PostReplyBuilder;
50 import net.pterodactylus.sone.freenet.wot.Identity;
52 import freenet.client.ClientMetadata;
53 import freenet.client.FetchResult;
54 import freenet.keys.FreenetURI;
55 import freenet.support.api.Bucket;
57 import com.google.common.base.Optional;
58 import com.google.common.collect.ImmutableSet;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.mockito.ArgumentCaptor;
62 import org.mockito.invocation.InvocationOnMock;
63 import org.mockito.stubbing.Answer;
66 * Unit test for {@link SoneDownloader} and its subclasses.
68 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
70 public class SoneDownloaderTest {
72 private final Core core = mock(Core.class);
73 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
74 private final SoneDownloader soneDownloader = new SoneDownloader(core, freenetInterface);
75 private final FreenetURI requestUri = mock(FreenetURI.class);
76 private final Sone sone = mock(Sone.class);
77 private final PostBuilder postBuilder = mock(PostBuilder.class);
78 private final List<Post> createdPosts = new ArrayList<Post>();
79 private Post post = mock(Post.class);
80 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
81 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
82 private PostReply postReply = mock(PostReply.class);
83 private final Map<String, Album> albums = new HashMap<String, Album>();
86 public void setupSone() {
87 Identity identity = mock(Identity.class);
88 when(identity.getId()).thenReturn("identity");
89 when(sone.getId()).thenReturn("identity");
90 when(sone.getIdentity()).thenReturn(identity);
91 when(sone.getRequestUri()).thenReturn(requestUri);
95 public void setupRequestUri() {
96 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
97 when(requestUri.sskForUSK()).thenReturn(requestUri);
98 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
99 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
100 when(requestUri.getKeyType()).thenReturn("USK");
104 public void setupPost() {
105 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
109 public void setupPostBuilder() {
110 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
112 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
113 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
117 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
119 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
120 final Sone sone = mock(Sone.class);
121 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
122 when(post.getSone()).thenReturn(sone);
126 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
128 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
129 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
133 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
135 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
136 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
140 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
142 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
143 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
147 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
149 public Post answer(InvocationOnMock invocation) throws Throwable {
150 Post post = SoneDownloaderTest.this.post;
151 SoneDownloaderTest.this.post = mock(Post.class);
153 createdPosts.add(post);
157 when(core.postBuilder()).thenReturn(postBuilder);
161 public void setupPostReplyBuilder() {
162 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
164 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
165 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
166 return postReplyBuilder;
169 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
171 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
172 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
173 when(postReply.getSone()).thenReturn(sone);
174 return postReplyBuilder;
177 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
179 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
180 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
181 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
182 when(postReply.getPost()).thenReturn(of(post));
183 return postReplyBuilder;
186 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
188 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
189 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
190 return postReplyBuilder;
193 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
195 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
196 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
197 return postReplyBuilder;
200 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
202 public PostReply answer(InvocationOnMock invocation) throws Throwable {
203 PostReply postReply = SoneDownloaderTest.this.postReply;
204 createdPostReplies.add(postReply);
205 SoneDownloaderTest.this.postReply = mock(PostReply.class);
209 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
213 public void setupAlbums() {
214 albums.put("album-id-1", new AlbumImpl("album-id-1"));
215 albums.put("album-id-2", new AlbumImpl("album-id-2"));
216 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
218 public Album answer(InvocationOnMock invocation) throws Throwable {
219 return albums.get(invocation.getArguments()[0]);
222 when(core.getAlbum(anyString(), anyBoolean())).thenAnswer(new Answer<Album>() {
224 public Album answer(InvocationOnMock invocation) throws Throwable {
225 return albums.get(invocation.getArguments()[0]);
231 public void setupImages() {
232 Image image = new ImageImpl("image-id");
233 when(core.getImage("image-id")).thenReturn(image);
234 when(core.getImage(eq("image-id"), anyBoolean())).thenReturn(image);
238 public void addingASoneWillRegisterItsKey() {
239 soneDownloader.addSone(sone);
240 verify(freenetInterface).registerUsk(sone, soneDownloader);
241 verify(freenetInterface, never()).unregisterUsk(sone);
245 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
246 soneDownloader.addSone(sone);
247 soneDownloader.addSone(sone);
248 verify(freenetInterface, times(2)).registerUsk(sone, soneDownloader);
249 verify(freenetInterface).unregisterUsk(sone);
253 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
254 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
255 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
259 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
260 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
261 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
265 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
266 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
267 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
271 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
272 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
273 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
277 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
278 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
279 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
283 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
284 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
285 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
289 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
290 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
291 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
295 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
296 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
297 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
301 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
302 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
303 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
307 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
308 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
309 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
313 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
314 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
315 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
319 public void parsingASoneFailsWithMissingClientName() throws SoneException {
320 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
321 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
325 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
326 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
327 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
331 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
332 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
333 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
337 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
338 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
339 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
343 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
344 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
345 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
349 public void soneInsertUriIsCopiedToNewSone() throws SoneException {
350 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
351 FreenetURI insertUri = mock(FreenetURI.class);
352 when(insertUri.setKeyType(anyString())).thenReturn(insertUri);
353 when(insertUri.setDocName(anyString())).thenReturn(insertUri);
354 when(insertUri.setMetaString(any(String[].class))).thenReturn(insertUri);
355 when(insertUri.setSuggestedEdition(anyLong())).thenReturn(insertUri);
356 when(sone.getInsertUri()).thenReturn(insertUri);
357 assertThat(soneDownloader.parseSone(sone, inputStream).getInsertUri(), is(insertUri));
361 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
362 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
363 final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
364 assertThat(profile.getFirstName(), is("first"));
365 assertThat(profile.getMiddleName(), is("middle"));
366 assertThat(profile.getLastName(), is("last"));
367 assertThat(profile.getBirthDay(), is(18));
368 assertThat(profile.getBirthMonth(), is(12));
369 assertThat(profile.getBirthYear(), is(1976));
373 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
374 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
375 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
379 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
380 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
381 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
385 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
386 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
387 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
391 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
392 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
393 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
397 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
398 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
399 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
403 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
404 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
405 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
406 assertThat(posts, is(createdPosts));
407 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
408 assertThat(posts.get(0).getId(), is("post-id"));
409 assertThat(posts.get(0).getTime(), is(1407197508000L));
410 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
411 assertThat(posts.get(0).getText(), is("text"));
415 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
416 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
417 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
418 assertThat(posts, is(createdPosts));
419 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
420 assertThat(posts.get(0).getId(), is("post-id"));
421 assertThat(posts.get(0).getTime(), is(1407197508000L));
422 assertThat(posts.get(0).getRecipientId(), is(of("1234567890123456789012345678901234567890123")));
423 assertThat(posts.get(0).getText(), is("text"));
427 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
428 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
429 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
430 assertThat(posts, is(createdPosts));
431 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
432 assertThat(posts.get(0).getId(), is("post-id"));
433 assertThat(posts.get(0).getTime(), is(1407197508000L));
434 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
435 assertThat(posts.get(0).getText(), is("text"));
439 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
440 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
441 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
445 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
446 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
447 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
451 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
452 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
453 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
457 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
458 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
459 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
463 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
464 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
465 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
469 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
470 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
471 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
472 assertThat(postReplies, is(createdPostReplies));
473 PostReply postReply = createdPostReplies.iterator().next();
474 assertThat(postReply.getId(), is("reply-id"));
475 assertThat(postReply.getPostId(), is("post-id"));
476 assertThat(postReply.getPost().get().getId(), is("post-id"));
477 assertThat(postReply.getSone().getId(), is("identity"));
478 assertThat(postReply.getTime(), is(1407197508000L));
479 assertThat(postReply.getText(), is("reply-text"));
483 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
484 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
485 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
489 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
490 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
491 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
495 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
496 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
497 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
501 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
502 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
503 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
507 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
508 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
509 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
513 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
514 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
515 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
519 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
520 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
521 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
525 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
526 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
527 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
528 assertThat(parsedSone, not(nullValue()));
529 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
530 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
531 assertThat(album.getId(), is("album-id-1"));
532 assertThat(album.getTitle(), is("album-title"));
533 assertThat(album.getDescription(), is("album-description"));
534 assertThat(album.getAlbums(), hasSize(1));
535 Album nestedAlbum = album.getAlbums().get(0);
536 assertThat(nestedAlbum.getId(), is("album-id-2"));
537 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
538 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
539 assertThat(nestedAlbum.getAlbums(), hasSize(0));
543 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
544 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
545 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
549 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
550 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
551 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
555 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
556 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
557 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
561 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
562 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
563 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
567 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
568 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
569 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
573 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
574 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
575 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
579 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
580 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
581 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
585 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
586 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
587 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
591 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
592 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
593 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
597 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
598 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
599 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
603 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
604 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
605 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
606 assertThat(sone, not(nullValue()));
607 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
608 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
609 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
610 assertThat(image.getId(), is("image-id"));
611 assertThat(image.getCreationTime(), is(1407197508000L));
612 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
613 assertThat(image.getTitle(), is("image-title"));
614 assertThat(image.getDescription(), is("image-description"));
615 assertThat(image.getWidth(), is(1920));
616 assertThat(image.getHeight(), is(1080));
617 assertThat(sone.getProfile().getAvatar(), is("image-id"));
621 public void stoppingTheSoneDownloaderUnregistersTheSone() {
622 soneDownloader.addSone(sone);
623 soneDownloader.stop();
624 verify(freenetInterface).unregisterUsk(sone);
628 public void fetchSoneWithUriDownloadsSoneWithUri() {
629 SoneDownloader soneDownloader = mock(SoneDownloader.class);
630 Sone sone = mock(Sone.class);
631 FreenetURI soneUri = mock(FreenetURI.class);
632 when(sone.getRequestUri()).thenReturn(soneUri);
633 FetchSoneWithUri fetchSoneWithUri = soneDownloader.new FetchSoneWithUri(sone);
634 fetchSoneWithUri.run();
635 verify(soneDownloader).fetchSone(eq(sone), eq(soneUri));
639 public void fetchSoneDownloadsSone() {
640 SoneDownloader soneDownloader = mock(SoneDownloader.class);
641 Sone sone = mock(Sone.class);
642 FetchSone fetchSone = soneDownloader.new FetchSone(sone);
644 verify(soneDownloader).fetchSone(eq(sone));
648 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
649 soneDownloader.fetchSone(sone);
650 verify(freenetInterface).fetchUri(requestUri);
651 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
652 verify(core, never()).updateSone(any(Sone.class));
655 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
656 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
657 verify(sone, times(2)).setStatus(soneStatuses.capture());
658 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
659 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
663 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
664 when(sone.getTime()).thenReturn(1000L);
665 soneDownloader.fetchSone(sone);
666 verify(freenetInterface).fetchUri(requestUri);
667 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
668 verify(core, never()).updateSone(any(Sone.class));
671 @Test(expected = NullPointerException.class)
672 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
673 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
675 soneDownloader.fetchSone(sone);
677 verify(freenetInterface).fetchUri(requestUri);
678 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
679 verify(core, never()).updateSone(any(Sone.class));
683 @Test(expected = NullPointerException.class)
684 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
685 when(sone.getTime()).thenReturn(1000L);
686 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
688 soneDownloader.fetchSone(sone);
690 verify(freenetInterface).fetchUri(requestUri);
691 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
692 verify(core, never()).updateSone(any(Sone.class));
697 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
698 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
699 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
700 soneDownloader.fetchSone(sone);
701 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
704 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
705 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
706 verify(core).updateSone(soneCaptor.capture());
707 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
711 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
712 when(requestUri.getKeyType()).thenReturn("SSK");
713 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
714 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
715 soneDownloader.fetchSone(sone);
716 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
720 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
721 when(requestUri.getKeyType()).thenReturn("SSK");
722 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
723 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
724 soneDownloader.fetchSone(sone);
725 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
729 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
730 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
731 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
732 soneDownloader.fetchSone(sone);
733 verify(core, never()).updateSone(any(Sone.class));
737 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
738 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
739 when(sone.getId()).thenThrow(NullPointerException.class);
740 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
741 soneDownloader.fetchSone(sone);
742 verify(core, never()).updateSone(any(Sone.class));
746 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
747 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
748 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
749 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
750 verify(core, never()).updateSone(any(Sone.class));
751 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
754 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
755 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
756 Bucket bucket = mock(Bucket.class);
757 when(bucket.getInputStream()).thenReturn(inputStream);
758 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
759 return new Fetched(uri, fetchResult);