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.data.Album;
37 import net.pterodactylus.sone.data.AlbumImpl;
38 import net.pterodactylus.sone.data.Client;
39 import net.pterodactylus.sone.data.Image;
40 import net.pterodactylus.sone.data.ImageImpl;
41 import net.pterodactylus.sone.data.Post;
42 import net.pterodactylus.sone.data.PostReply;
43 import net.pterodactylus.sone.data.Profile;
44 import net.pterodactylus.sone.data.Sone;
45 import net.pterodactylus.sone.data.Sone.SoneStatus;
46 import net.pterodactylus.sone.database.PostBuilder;
47 import net.pterodactylus.sone.database.PostReplyBuilder;
48 import net.pterodactylus.sone.freenet.wot.Identity;
50 import freenet.client.ClientMetadata;
51 import freenet.client.FetchResult;
52 import freenet.keys.FreenetURI;
53 import freenet.support.api.Bucket;
55 import com.google.common.base.Optional;
56 import com.google.common.collect.ImmutableSet;
57 import org.junit.Before;
58 import org.junit.Test;
59 import org.mockito.ArgumentCaptor;
60 import org.mockito.invocation.InvocationOnMock;
61 import org.mockito.stubbing.Answer;
64 * Unit test for {@link SoneDownloaderImpl} and its subclasses.
66 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
68 public class SoneDownloaderTest {
70 private final Core core = mock(Core.class);
71 private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
72 private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(core, freenetInterface);
73 private final FreenetURI requestUri = mock(FreenetURI.class);
74 private final Sone sone = mock(Sone.class);
75 private final PostBuilder postBuilder = mock(PostBuilder.class);
76 private final List<Post> createdPosts = new ArrayList<Post>();
77 private Post post = mock(Post.class);
78 private final PostReplyBuilder postReplyBuilder = mock(PostReplyBuilder.class);
79 private final Set<PostReply> createdPostReplies = new HashSet<PostReply>();
80 private PostReply postReply = mock(PostReply.class);
81 private final Map<String, Album> albums = new HashMap<String, Album>();
84 public void setupSone() {
85 Identity identity = mock(Identity.class);
86 when(identity.getId()).thenReturn("identity");
87 when(sone.getId()).thenReturn("identity");
88 when(sone.getIdentity()).thenReturn(identity);
89 when(sone.getRequestUri()).thenReturn(requestUri);
93 public void setupRequestUri() {
94 when(requestUri.setKeyType(anyString())).thenReturn(requestUri);
95 when(requestUri.sskForUSK()).thenReturn(requestUri);
96 when(requestUri.setDocName(anyString())).thenReturn(requestUri);
97 when(requestUri.setMetaString(any(String[].class))).thenReturn(requestUri);
98 when(requestUri.getKeyType()).thenReturn("USK");
102 public void setupPost() {
103 when(post.getRecipientId()).thenReturn(Optional.<String>absent());
107 public void setupPostBuilder() {
108 when(postBuilder.withId(anyString())).thenAnswer(new Answer<PostBuilder>() {
110 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
111 when(post.getId()).thenReturn((String) invocation.getArguments()[0]);
115 when(postBuilder.from(anyString())).thenAnswer(new Answer<PostBuilder>() {
117 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
118 final Sone sone = mock(Sone.class);
119 when(sone.getId()).thenReturn((String) invocation.getArguments()[0]);
120 when(post.getSone()).thenReturn(sone);
124 when(postBuilder.withTime(anyLong())).thenAnswer(new Answer<PostBuilder>() {
126 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
127 when(post.getTime()).thenReturn((Long) invocation.getArguments()[0]);
131 when(postBuilder.withText(anyString())).thenAnswer(new Answer<PostBuilder>() {
133 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
134 when(post.getText()).thenReturn((String) invocation.getArguments()[0]);
138 when(postBuilder.to(anyString())).thenAnswer(new Answer<PostBuilder>() {
140 public PostBuilder answer(InvocationOnMock invocation) throws Throwable {
141 when(post.getRecipientId()).thenReturn(of((String) invocation.getArguments()[0]));
145 when(postBuilder.build()).thenAnswer(new Answer<Post>() {
147 public Post answer(InvocationOnMock invocation) throws Throwable {
148 Post post = SoneDownloaderTest.this.post;
149 SoneDownloaderTest.this.post = mock(Post.class);
151 createdPosts.add(post);
155 when(core.postBuilder()).thenReturn(postBuilder);
159 public void setupPostReplyBuilder() {
160 when(postReplyBuilder.withId(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
162 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
163 when(postReply.getId()).thenReturn((String) invocation.getArguments()[0]);
164 return postReplyBuilder;
167 when(postReplyBuilder.from(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
169 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
170 Sone sone = when(mock(Sone.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
171 when(postReply.getSone()).thenReturn(sone);
172 return postReplyBuilder;
175 when(postReplyBuilder.to(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
177 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
178 when(postReply.getPostId()).thenReturn((String) invocation.getArguments()[0]);
179 Post post = when(mock(Post.class).getId()).thenReturn((String) invocation.getArguments()[0]).getMock();
180 when(postReply.getPost()).thenReturn(of(post));
181 return postReplyBuilder;
184 when(postReplyBuilder.withTime(anyLong())).thenAnswer(new Answer<PostReplyBuilder>() {
186 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
187 when(postReply.getTime()).thenReturn((Long) invocation.getArguments()[0]);
188 return postReplyBuilder;
191 when(postReplyBuilder.withText(anyString())).thenAnswer(new Answer<PostReplyBuilder>() {
193 public PostReplyBuilder answer(InvocationOnMock invocation) throws Throwable {
194 when(postReply.getText()).thenReturn((String) invocation.getArguments()[0]);
195 return postReplyBuilder;
198 when(postReplyBuilder.build()).thenAnswer(new Answer<PostReply>() {
200 public PostReply answer(InvocationOnMock invocation) throws Throwable {
201 PostReply postReply = SoneDownloaderTest.this.postReply;
202 createdPostReplies.add(postReply);
203 SoneDownloaderTest.this.postReply = mock(PostReply.class);
207 when(core.postReplyBuilder()).thenReturn(postReplyBuilder);
211 public void setupAlbums() {
212 albums.put("album-id-1", new AlbumImpl("album-id-1"));
213 albums.put("album-id-2", new AlbumImpl("album-id-2"));
214 when(core.getAlbum(anyString())).thenAnswer(new Answer<Album>() {
216 public Album answer(InvocationOnMock invocation) throws Throwable {
217 return albums.get(invocation.getArguments()[0]);
220 when(core.getAlbum(anyString(), anyBoolean())).thenAnswer(new Answer<Album>() {
222 public Album answer(InvocationOnMock invocation) throws Throwable {
223 return albums.get(invocation.getArguments()[0]);
229 public void setupImages() {
230 Image image = new ImageImpl("image-id");
231 when(core.getImage("image-id")).thenReturn(image);
232 when(core.getImage(eq("image-id"), anyBoolean())).thenReturn(image);
236 public void addingASoneWillRegisterItsKey() {
237 soneDownloader.addSone(sone);
238 verify(freenetInterface).registerUsk(sone, soneDownloader);
239 verify(freenetInterface, never()).unregisterUsk(sone);
243 public void addingASoneTwiceWillAlsoDeregisterItsKey() {
244 soneDownloader.addSone(sone);
245 soneDownloader.addSone(sone);
246 verify(freenetInterface, times(2)).registerUsk(sone, soneDownloader);
247 verify(freenetInterface).unregisterUsk(sone);
251 public void parsingASoneFailsWhenDocumentIsNotXml() throws SoneException {
252 InputStream inputStream = getClass().getResourceAsStream("sone-parser-not-xml.xml");
253 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
257 public void parsingASoneFailsWhenDocumentHasNegativeProtocolVersion() throws SoneException {
258 InputStream inputStream = getClass().getResourceAsStream("sone-parser-negative-protocol-version.xml");
259 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
263 public void parsingASoneFailsWhenProtocolVersionIsTooLarge() throws SoneException {
264 InputStream inputStream = getClass().getResourceAsStream("sone-parser-too-large-protocol-version.xml");
265 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
269 public void parsingASoneFailsWhenThereIsNoTime() throws SoneException {
270 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-time.xml");
271 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
275 public void parsingASoneFailsWhenTimeIsNotNumeric() throws SoneException {
276 InputStream inputStream = getClass().getResourceAsStream("sone-parser-time-not-numeric.xml");
277 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
281 public void parsingASoneFailsWhenProfileIsMissing() throws SoneException {
282 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-profile.xml");
283 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
287 public void parsingASoneFailsWhenProfileFieldIsMissingAFieldName() throws SoneException {
288 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-missing-field-name.xml");
289 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
293 public void parsingASoneFailsWhenProfileFieldNameIsEmpty() throws SoneException {
294 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-empty-field-name.xml");
295 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
299 public void parsingASoneFailsWhenProfileFieldNameIsNotUnique() throws SoneException {
300 InputStream inputStream = getClass().getResourceAsStream("sone-parser-profile-duplicate-field-name.xml");
301 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
305 public void parsingASoneSucceedsWithoutPayload() throws SoneException {
306 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
307 assertThat(soneDownloader.parseSone(sone, inputStream).getTime(), is(1407197508000L));
311 public void parsingASoneSucceedsWithoutProtocolVersion() throws SoneException {
312 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-protocol-version.xml");
313 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
317 public void parsingASoneFailsWithMissingClientName() throws SoneException {
318 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-name.xml");
319 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
323 public void parsingASoneFailsWithMissingClientVersion() throws SoneException {
324 InputStream inputStream = getClass().getResourceAsStream("sone-parser-missing-client-version.xml");
325 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
329 public void parsingASoneSucceedsWithClientInfo() throws SoneException {
330 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-client-info.xml");
331 assertThat(soneDownloader.parseSone(sone, inputStream).getClient(), is(new Client("some-client", "some-version")));
335 public void parsingASoneSucceedsWithRequestUri() throws SoneException, MalformedURLException {
336 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-request-uri.xml");
337 assertThat(soneDownloader.parseSone(sone, inputStream).getRequestUri().toString(), is("USK@w~RyTGmv12Lg9oO91q1Untupi7my9qczT1RheGkEkVE,E8ElVfUgukSCPHxIEJp-gHMiR80wpM7sID3Jo5O7w1s,AQACAAE/Sone/0"));
341 public void parsingASoneFailsWithInvalidRequestUri() throws SoneException, MalformedURLException {
342 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-request-uri.xml");
343 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
347 public void soneInsertUriIsCopiedToNewSone() throws SoneException {
348 InputStream inputStream = getClass().getResourceAsStream("sone-parser-no-payload.xml");
349 FreenetURI insertUri = mock(FreenetURI.class);
350 when(insertUri.setKeyType(anyString())).thenReturn(insertUri);
351 when(insertUri.setDocName(anyString())).thenReturn(insertUri);
352 when(insertUri.setMetaString(any(String[].class))).thenReturn(insertUri);
353 when(insertUri.setSuggestedEdition(anyLong())).thenReturn(insertUri);
354 when(sone.getInsertUri()).thenReturn(insertUri);
355 assertThat(soneDownloader.parseSone(sone, inputStream).getInsertUri(), is(insertUri));
359 public void parsingASoneSucceedsWithProfile() throws SoneException, MalformedURLException {
360 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-profile.xml");
361 final Profile profile = soneDownloader.parseSone(sone, inputStream).getProfile();
362 assertThat(profile.getFirstName(), is("first"));
363 assertThat(profile.getMiddleName(), is("middle"));
364 assertThat(profile.getLastName(), is("last"));
365 assertThat(profile.getBirthDay(), is(18));
366 assertThat(profile.getBirthMonth(), is(12));
367 assertThat(profile.getBirthYear(), is(1976));
371 public void parsingASoneSucceedsWithoutProfileFields() throws SoneException, MalformedURLException {
372 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-fields.xml");
373 assertThat(soneDownloader.parseSone(sone, inputStream), notNullValue());
377 public void parsingASoneFailsWithoutPostId() throws SoneException, MalformedURLException {
378 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-id.xml");
379 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
383 public void parsingASoneFailsWithoutPostTime() throws SoneException, MalformedURLException {
384 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-time.xml");
385 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
389 public void parsingASoneFailsWithoutPostText() throws SoneException, MalformedURLException {
390 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-text.xml");
391 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
395 public void parsingASoneFailsWithInvalidPostTime() throws SoneException, MalformedURLException {
396 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-time.xml");
397 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
401 public void parsingASoneSucceedsWithValidPostTime() throws SoneException, MalformedURLException {
402 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-time.xml");
403 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
404 assertThat(posts, is(createdPosts));
405 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
406 assertThat(posts.get(0).getId(), is("post-id"));
407 assertThat(posts.get(0).getTime(), is(1407197508000L));
408 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
409 assertThat(posts.get(0).getText(), is("text"));
413 public void parsingASoneSucceedsWithRecipient() throws SoneException, MalformedURLException {
414 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-recipient.xml");
415 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
416 assertThat(posts, is(createdPosts));
417 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
418 assertThat(posts.get(0).getId(), is("post-id"));
419 assertThat(posts.get(0).getTime(), is(1407197508000L));
420 assertThat(posts.get(0).getRecipientId(), is(of("1234567890123456789012345678901234567890123")));
421 assertThat(posts.get(0).getText(), is("text"));
425 public void parsingASoneSucceedsWithInvalidRecipient() throws SoneException, MalformedURLException {
426 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-recipient.xml");
427 final List<Post> posts = soneDownloader.parseSone(sone, inputStream).getPosts();
428 assertThat(posts, is(createdPosts));
429 assertThat(posts.get(0).getSone().getId(), is(sone.getId()));
430 assertThat(posts.get(0).getId(), is("post-id"));
431 assertThat(posts.get(0).getTime(), is(1407197508000L));
432 assertThat(posts.get(0).getRecipientId(), is(Optional.<String>absent()));
433 assertThat(posts.get(0).getText(), is("text"));
437 public void parsingASoneFailsWithoutPostReplyId() throws SoneException, MalformedURLException {
438 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-id.xml");
439 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
443 public void parsingASoneFailsWithoutPostReplyPostId() throws SoneException, MalformedURLException {
444 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-post-id.xml");
445 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
449 public void parsingASoneFailsWithoutPostReplyTime() throws SoneException, MalformedURLException {
450 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-time.xml");
451 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
455 public void parsingASoneFailsWithoutPostReplyText() throws SoneException, MalformedURLException {
456 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-post-reply-text.xml");
457 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
461 public void parsingASoneFailsWithInvalidPostReplyTime() throws SoneException, MalformedURLException {
462 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-post-reply-time.xml");
463 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
467 public void parsingASoneSucceedsWithValidPostReplyTime() throws SoneException, MalformedURLException {
468 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-valid-post-reply-time.xml");
469 final Set<PostReply> postReplies = soneDownloader.parseSone(sone, inputStream).getReplies();
470 assertThat(postReplies, is(createdPostReplies));
471 PostReply postReply = createdPostReplies.iterator().next();
472 assertThat(postReply.getId(), is("reply-id"));
473 assertThat(postReply.getPostId(), is("post-id"));
474 assertThat(postReply.getPost().get().getId(), is("post-id"));
475 assertThat(postReply.getSone().getId(), is("identity"));
476 assertThat(postReply.getTime(), is(1407197508000L));
477 assertThat(postReply.getText(), is("reply-text"));
481 public void parsingASoneSucceedsWithoutLikedPostIds() throws SoneException, MalformedURLException {
482 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-ids.xml");
483 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
487 public void parsingASoneSucceedsWithLikedPostIds() throws SoneException, MalformedURLException {
488 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-ids.xml");
489 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedPostIds(), is((Set<String>) ImmutableSet.of("liked-post-id")));
493 public void parsingASoneSucceedsWithoutLikedPostReplyIds() throws SoneException, MalformedURLException {
494 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-liked-post-reply-ids.xml");
495 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
499 public void parsingASoneSucceedsWithLikedPostReplyIds() throws SoneException, MalformedURLException {
500 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-liked-post-reply-ids.xml");
501 assertThat(soneDownloader.parseSone(sone, inputStream).getLikedReplyIds(), is((Set<String>) ImmutableSet.of("liked-post-reply-id")));
505 public void parsingASoneSucceedsWithoutAlbums() throws SoneException, MalformedURLException {
506 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-albums.xml");
507 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
511 public void parsingASoneFailsWithoutAlbumId() throws SoneException, MalformedURLException {
512 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-id.xml");
513 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
517 public void parsingASoneFailsWithoutAlbumTitle() throws SoneException, MalformedURLException {
518 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-album-title.xml");
519 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
523 public void parsingASoneSucceedsWithNestedAlbums() throws SoneException, MalformedURLException {
524 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-multiple-albums.xml");
525 final Sone parsedSone = soneDownloader.parseSone(sone, inputStream);
526 assertThat(parsedSone, not(nullValue()));
527 assertThat(parsedSone.getRootAlbum().getAlbums(), hasSize(1));
528 Album album = parsedSone.getRootAlbum().getAlbums().get(0);
529 assertThat(album.getId(), is("album-id-1"));
530 assertThat(album.getTitle(), is("album-title"));
531 assertThat(album.getDescription(), is("album-description"));
532 assertThat(album.getAlbums(), hasSize(1));
533 Album nestedAlbum = album.getAlbums().get(0);
534 assertThat(nestedAlbum.getId(), is("album-id-2"));
535 assertThat(nestedAlbum.getTitle(), is("album-title-2"));
536 assertThat(nestedAlbum.getDescription(), is("album-description-2"));
537 assertThat(nestedAlbum.getAlbums(), hasSize(0));
541 public void parsingASoneFailsWithInvalidParentAlbumId() throws SoneException, MalformedURLException {
542 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-parent-album-id.xml");
543 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
547 public void parsingASoneSucceedsWithoutImages() throws SoneException, MalformedURLException {
548 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-images.xml");
549 assertThat(soneDownloader.parseSone(sone, inputStream), not(nullValue()));
553 public void parsingASoneFailsWithoutImageId() throws SoneException, MalformedURLException {
554 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-id.xml");
555 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
559 public void parsingASoneFailsWithoutImageTime() throws SoneException, MalformedURLException {
560 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-time.xml");
561 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
565 public void parsingASoneFailsWithoutImageKey() throws SoneException, MalformedURLException {
566 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-key.xml");
567 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
571 public void parsingASoneFailsWithoutImageTitle() throws SoneException, MalformedURLException {
572 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-title.xml");
573 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
577 public void parsingASoneFailsWithoutImageWidth() throws SoneException, MalformedURLException {
578 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-width.xml");
579 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
583 public void parsingASoneFailsWithoutImageHeight() throws SoneException, MalformedURLException {
584 InputStream inputStream = getClass().getResourceAsStream("sone-parser-without-image-height.xml");
585 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
589 public void parsingASoneFailsWithInvalidImageWidth() throws SoneException, MalformedURLException {
590 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-width.xml");
591 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
595 public void parsingASoneFailsWithInvalidImageHeight() throws SoneException, MalformedURLException {
596 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-invalid-image-height.xml");
597 assertThat(soneDownloader.parseSone(sone, inputStream), nullValue());
601 public void parsingASoneSucceedsWithImage() throws SoneException, MalformedURLException {
602 InputStream inputStream = getClass().getResourceAsStream("sone-parser-with-image.xml");
603 final Sone sone = soneDownloader.parseSone(this.sone, inputStream);
604 assertThat(sone, not(nullValue()));
605 assertThat(sone.getRootAlbum().getAlbums(), hasSize(1));
606 assertThat(sone.getRootAlbum().getAlbums().get(0).getImages(), hasSize(1));
607 Image image = sone.getRootAlbum().getAlbums().get(0).getImages().get(0);
608 assertThat(image.getId(), is("image-id"));
609 assertThat(image.getCreationTime(), is(1407197508000L));
610 assertThat(image.getKey(), is("KSK@GPLv3.txt"));
611 assertThat(image.getTitle(), is("image-title"));
612 assertThat(image.getDescription(), is("image-description"));
613 assertThat(image.getWidth(), is(1920));
614 assertThat(image.getHeight(), is(1080));
615 assertThat(sone.getProfile().getAvatar(), is("image-id"));
619 public void stoppingTheSoneDownloaderUnregistersTheSone() {
620 soneDownloader.addSone(sone);
621 soneDownloader.stop();
622 verify(freenetInterface).unregisterUsk(sone);
626 public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
627 soneDownloader.fetchSone(sone);
628 verify(freenetInterface).fetchUri(requestUri);
629 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
630 verify(core, never()).updateSone(any(Sone.class));
633 private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
634 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
635 verify(sone, times(2)).setStatus(soneStatuses.capture());
636 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
637 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
641 public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
642 when(sone.getTime()).thenReturn(1000L);
643 soneDownloader.fetchSone(sone);
644 verify(freenetInterface).fetchUri(requestUri);
645 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
646 verify(core, never()).updateSone(any(Sone.class));
649 @Test(expected = NullPointerException.class)
650 public void exceptionWhileFetchingAnUnknownSoneDoesNotUpdateCore() {
651 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
653 soneDownloader.fetchSone(sone);
655 verify(freenetInterface).fetchUri(requestUri);
656 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
657 verify(core, never()).updateSone(any(Sone.class));
661 @Test(expected = NullPointerException.class)
662 public void exceptionWhileFetchingAKnownSoneDoesNotUpdateCore() {
663 when(sone.getTime()).thenReturn(1000L);
664 when(freenetInterface.fetchUri(requestUri)).thenThrow(NullPointerException.class);
666 soneDownloader.fetchSone(sone);
668 verify(freenetInterface).fetchUri(requestUri);
669 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
670 verify(core, never()).updateSone(any(Sone.class));
675 public void successfulFetchingOfSoneWithUskRequestUriUpdatesTheCoreWithASone() throws IOException {
676 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
677 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
678 soneDownloader.fetchSone(sone);
679 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
682 private void verifyThatParsedSoneHasTheSameIdAsTheOriginalSone() {
683 ArgumentCaptor<Sone> soneCaptor = forClass(Sone.class);
684 verify(core).updateSone(soneCaptor.capture());
685 assertThat(soneCaptor.getValue().getId(), is(sone.getId()));
689 public void successfulFetchingOfSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
690 when(requestUri.getKeyType()).thenReturn("SSK");
691 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
692 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
693 soneDownloader.fetchSone(sone);
694 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
698 public void successfulFetchingAnUnknownSoneWithSskRequestUriUpdatesTheCoreWithASone() throws IOException {
699 when(requestUri.getKeyType()).thenReturn("SSK");
700 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-with-zero-time.xml"));
701 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
702 soneDownloader.fetchSone(sone);
703 verifyThatParsedSoneHasTheSameIdAsTheOriginalSone();
707 public void fetchingSoneWithInvalidXmlWillNotUpdateTheCore() throws IOException {
708 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-not-xml.xml"));
709 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
710 soneDownloader.fetchSone(sone);
711 verify(core, never()).updateSone(any(Sone.class));
715 public void exceptionWhileFetchingSoneWillNotUpdateTheCore() throws IOException {
716 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
717 when(sone.getId()).thenThrow(NullPointerException.class);
718 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
719 soneDownloader.fetchSone(sone);
720 verify(core, never()).updateSone(any(Sone.class));
724 public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException {
725 final Fetched fetchResult = createFetchResult(requestUri, getClass().getResourceAsStream("sone-parser-no-payload.xml"));
726 when(freenetInterface.fetchUri(requestUri)).thenReturn(fetchResult);
727 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
728 verify(core, never()).updateSone(any(Sone.class));
729 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
732 private Fetched createFetchResult(FreenetURI uri, InputStream inputStream) throws IOException {
733 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
734 Bucket bucket = mock(Bucket.class);
735 when(bucket.getInputStream()).thenReturn(inputStream);
736 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
737 return new Fetched(uri, fetchResult);