Verify that no events are sent on invalid property change
[Sone.git] / src / test / java / net / pterodactylus / sone / core / SoneDownloaderTest.java
1 package net.pterodactylus.sone.core;
2
3 import static freenet.keys.InsertableClientSSK.createRandom;
4 import static java.lang.System.currentTimeMillis;
5 import static java.util.concurrent.TimeUnit.DAYS;
6 import static net.pterodactylus.sone.data.Sone.SoneStatus.downloading;
7 import static net.pterodactylus.sone.data.Sone.SoneStatus.idle;
8 import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
9 import static net.pterodactylus.sone.web.AllPagesTestKt.getBaseInjector;
10 import static org.hamcrest.MatcherAssert.assertThat;
11 import static org.hamcrest.Matchers.is;
12 import static org.hamcrest.Matchers.notNullValue;
13 import static org.mockito.ArgumentCaptor.forClass;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.eq;
16 import static org.mockito.Mockito.mock;
17 import static org.mockito.Mockito.never;
18 import static org.mockito.Mockito.times;
19 import static org.mockito.Mockito.verify;
20 import static org.mockito.Mockito.when;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24
25 import net.pterodactylus.sone.data.Sone;
26 import net.pterodactylus.sone.data.Sone.SoneStatus;
27 import net.pterodactylus.sone.freenet.wot.Identity;
28 import net.pterodactylus.sone.test.GuiceKt;
29
30 import freenet.client.ClientMetadata;
31 import freenet.client.FetchResult;
32 import freenet.client.async.USKCallback;
33 import freenet.crypt.DummyRandomSource;
34 import freenet.keys.FreenetURI;
35 import freenet.keys.InsertableClientSSK;
36 import freenet.support.api.Bucket;
37
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.mockito.ArgumentCaptor;
41
42 /**
43  * Unit test for {@link SoneDownloaderImpl} and its subclasses.
44  */
45 public class SoneDownloaderTest {
46
47         private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
48         private final SoneParser soneParser = mock(SoneParser.class);
49         private final UpdatedSoneProcessor updatedSoneProcessor = mock(UpdatedSoneProcessor.class);
50         private final SoneDownloaderImpl soneDownloader = new SoneDownloaderImpl(updatedSoneProcessor, freenetInterface, soneParser);
51         private final InsertableClientSSK clientSSK = createRandom(new DummyRandomSource(), "WoT");
52         private final FreenetURI requestUri = clientSSK.getURI().setKeyType("USK").setDocName("Sone");
53         private final FreenetURI finalRequestUri = requestUri.setMetaString(new String[] { "sone.xml" });
54         private final Sone sone = mock(Sone.class);
55         private final Sone parsedSone = mock(Sone.class);
56
57         @Before
58         public void setupSone() {
59                 Identity identity = mock(Identity.class);
60                 when(identity.getRequestUri()).thenReturn(clientSSK.getURI().toString());
61                 when(identity.getId()).thenReturn("identity");
62                 when(sone.getId()).thenReturn("identity");
63                 when(sone.getIdentity()).thenReturn(identity);
64                 when(sone.getRequestUri()).thenReturn(requestUri);
65                 when(sone.getTime()).thenReturn(currentTimeMillis() - DAYS.toMillis(1));
66         }
67
68         private void setupSoneAsUnknown() {
69                 when(sone.getTime()).thenReturn(0L);
70         }
71
72         @Test
73         public void addingASoneWillRegisterItsKey() {
74                 soneDownloader.addSone(sone);
75                 verify(freenetInterface).registerActiveUsk(eq(sone.getRequestUri()), any(
76                                 USKCallback.class));
77                 verify(freenetInterface, never()).unregisterUsk(sone);
78         }
79
80         @Test
81         public void addingASoneTwiceWillAlsoDeregisterItsKey() {
82                 soneDownloader.addSone(sone);
83                 soneDownloader.addSone(sone);
84                 verify(freenetInterface, times(2)).registerActiveUsk(eq(
85                                 sone.getRequestUri()), any(USKCallback.class));
86                 verify(freenetInterface).unregisterUsk(sone);
87         }
88
89
90         @Test
91         public void stoppingTheSoneDownloaderUnregistersTheSone() {
92                 soneDownloader.addSone(sone);
93                 soneDownloader.stop();
94                 verify(freenetInterface).unregisterUsk(sone);
95         }
96
97         @Test
98         public void notBeingAbleToFetchAnUnknownSoneDoesNotUpdateCore() {
99                 setupSoneAsUnknown();
100                 soneDownloader.fetchSoneAsSskAction(sone).run();
101                 verify(freenetInterface).fetchUri(finalRequestUri.sskForUSK());
102                 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(unknown);
103                 verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
104         }
105
106         private void verifyThatSoneStatusWasChangedToDownloadingAndBackTo(SoneStatus soneStatus) {
107                 ArgumentCaptor<SoneStatus> soneStatuses = forClass(SoneStatus.class);
108                 verify(sone, times(2)).setStatus(soneStatuses.capture());
109                 assertThat(soneStatuses.getAllValues().get(0), is(downloading));
110                 assertThat(soneStatuses.getAllValues().get(1), is(soneStatus));
111         }
112
113         @Test
114         public void notBeingAbleToFetchAKnownSoneDoesNotUpdateCore() {
115                 soneDownloader.fetchSoneAsSskAction(sone).run();
116                 verify(freenetInterface).fetchUri(finalRequestUri.sskForUSK());
117                 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
118                 verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
119         }
120
121         @Test(expected = NullPointerException.class)
122         public void exceptionWhileFetchingSoneDoesNotProcessUpdatedSone() {
123                 when(freenetInterface.fetchUri(any(FreenetURI.class))).thenThrow(NullPointerException.class);
124                 try {
125                         soneDownloader.fetchSoneAsSskAction(sone).run();
126                 } finally {
127                         verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
128                 }
129         }
130
131         @Test
132         public void onlyFetchingASoneWillNotUpdateTheCore() throws IOException, SoneException {
133                 setupParsedSone();
134                 soneDownloader.fetchSone(sone, sone.getRequestUri(), true);
135                 verify(updatedSoneProcessor, never()).updateSone(any(Sone.class));
136                 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
137         }
138
139         @Test
140         public void fetchingACompleteSoneNotifiesTheUpdatedSoneProcessor() throws IOException, SoneException {
141                 setupParsedSone();
142                 soneDownloader.fetchSone(sone, sone.getRequestUri(), false);
143                 verify(updatedSoneProcessor).updateSone(parsedSone);
144                 verifyThatSoneStatusWasChangedToDownloadingAndBackTo(idle);
145         }
146
147         private void setupParsedSone() throws IOException, SoneException {
148                 InputStream inputStream = mock(InputStream.class);
149                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
150                 Bucket bucket = mock(Bucket.class);
151                 when(bucket.getInputStream()).thenReturn(inputStream);
152                 FetchResult fetchResult = new FetchResult(clientMetadata, bucket);
153                 Fetched fetched = new Fetched(finalRequestUri, fetchResult);
154                 when(freenetInterface.fetchUri(eq(finalRequestUri))).thenReturn(fetched);
155                 when(soneParser.parseSone(sone, inputStream)).thenReturn(parsedSone);
156         }
157
158         @Test
159         public void soneDownloaderCanBeCreatedByDependencyInjection() {
160                 assertThat(getBaseInjector().createChildInjector(
161                                 GuiceKt.supply(UpdatedSoneProcessor.class).byInstance(mock(UpdatedSoneProcessor.class)),
162                                 GuiceKt.supply(SoneParser.class).byInstance(mock(SoneParser.class))
163                 ).getInstance(SoneDownloader.class), notNullValue());
164         }
165
166 }