663b709d03e4d6bb44d59fccf4bda1f9dac99775
[Sone.git] / src / test / java / net / pterodactylus / sone / core / UpdateCheckerTest.java
1 package net.pterodactylus.sone.core;
2
3 import static java.lang.Long.MAX_VALUE;
4 import static org.hamcrest.MatcherAssert.assertThat;
5 import static org.hamcrest.Matchers.instanceOf;
6 import static org.hamcrest.Matchers.is;
7 import static org.mockito.ArgumentCaptor.forClass;
8 import static org.mockito.ArgumentMatchers.any;
9 import static org.mockito.Mockito.mock;
10 import static org.mockito.Mockito.never;
11 import static org.mockito.Mockito.times;
12 import static org.mockito.Mockito.verify;
13 import static org.mockito.Mockito.when;
14 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
15
16 import java.io.IOException;
17 import java.io.InputStream;
18
19 import net.pterodactylus.sone.core.FreenetInterface.Callback;
20 import net.pterodactylus.sone.core.event.UpdateFoundEvent;
21 import net.pterodactylus.util.version.Version;
22
23 import freenet.client.ClientMetadata;
24 import freenet.client.FetchResult;
25 import freenet.keys.FreenetURI;
26 import freenet.support.api.Bucket;
27 import freenet.support.io.ArrayBucket;
28
29 import com.google.common.eventbus.EventBus;
30 import org.junit.Before;
31 import org.junit.Test;
32 import org.mockito.ArgumentCaptor;
33 import org.mockito.invocation.InvocationOnMock;
34 import org.mockito.stubbing.Answer;
35
36 /**
37  * Unit test for {@link UpdateChecker}.
38  */
39 public class UpdateCheckerTest {
40
41         private final EventBus eventBus = mock(EventBus.class);
42         private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
43         private final Version currentVersion = new Version(1, 0, 0);
44         private final UpdateChecker updateChecker = new UpdateChecker(eventBus, freenetInterface, currentVersion);
45
46         @Before
47         public void startUpdateChecker() {
48                 updateChecker.start();
49         }
50
51         @Test
52         public void newUpdateCheckerDoesNotHaveALatestVersion() {
53                 assertThat(updateChecker.hasLatestVersion(), is(false));
54                 assertThat(updateChecker.getLatestVersion(), is(currentVersion));
55         }
56
57         @Test
58         public void startingAnUpdateCheckerRegisterAUsk() {
59                 verify(freenetInterface).registerUsk(any(FreenetURI.class), any(Callback.class));
60         }
61
62         @Test
63         public void stoppingAnUpdateCheckerUnregistersAUsk() {
64                 updateChecker.stop();
65                 verify(freenetInterface).unregisterUsk(any(FreenetURI.class));
66         }
67
68         @Test
69         public void callbackDoesNotDownloadIfNewEditionIsNotFound() {
70                 setupCallbackWithEdition(MAX_VALUE, false, false);
71                 verify(freenetInterface, never()).fetchUri(any(FreenetURI.class));
72                 verify(eventBus, never()).post(argThat(instanceOf(UpdateFoundEvent.class)));
73         }
74
75         private void setupCallbackWithEdition(long edition, boolean newKnownGood, boolean newSlot) {
76                 ArgumentCaptor<FreenetURI> uri = forClass(FreenetURI.class);
77                 ArgumentCaptor<Callback> callback = forClass(Callback.class);
78                 verify(freenetInterface).registerUsk(uri.capture(), callback.capture());
79                 callback.getValue().editionFound(uri.getValue(), edition, newKnownGood, newSlot);
80         }
81
82         @Test
83         public void callbackStartsIfNewEditionIsFound() {
84                 setupFetchResult(createFutureFetchResult());
85                 setupCallbackWithEdition(MAX_VALUE, true, false);
86                 verifyAFreenetUriIsFetched();
87                 verifyEventIsFired(new Version(99, 0, 0), 11865368297000L, false);
88                 verifyThatUpdateCheckerKnowsLatestVersion(new Version(99, 0, 0), 11865368297000L);
89         }
90
91         private FetchResult createFutureFetchResult() {
92                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
93                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
94                                 "CurrentVersion/Version: 99.0.0\n" +
95                                 "CurrentVersion/ReleaseTime: 11865368297000\n" +
96                                 "DisruptiveVersion/0.1.2: true").getBytes());
97                 return new FetchResult(clientMetadata, fetched);
98         }
99
100         private void verifyEventIsFired(Version version, long releaseTime, boolean disruptive) {
101                 ArgumentCaptor<UpdateFoundEvent> updateFoundEvent = forClass(UpdateFoundEvent.class);
102                 verify(eventBus, times(1)).post(updateFoundEvent.capture());
103                 assertThat(updateFoundEvent.getValue().version(), is(version));
104                 assertThat(updateFoundEvent.getValue().releaseTime(), is(releaseTime));
105                 assertThat(updateFoundEvent.getValue().disruptive(), is(disruptive));
106         }
107
108         private void verifyThatUpdateCheckerKnowsLatestVersion(Version version, long releaseTime) {
109                 assertThat(updateChecker.getLatestVersion(), is(version));
110                 assertThat(updateChecker.getLatestVersionDate(), is(releaseTime));
111                 assertThat(updateChecker.hasLatestVersion(), is(true));
112         }
113
114         @Test
115         public void callbackDoesNotStartIfNoNewEditionIsFound() {
116                 setupFetchResult(createPastFetchResult());
117                 setupCallbackWithEdition(updateChecker.getLatestEdition(), true, false);
118                 verifyAFreenetUriIsFetched();
119                 verifyNoUpdateFoundEventIsFired();
120         }
121
122         private void setupFetchResult(final FetchResult pastFetchResult) {
123                 when(freenetInterface.fetchUri(any(FreenetURI.class))).thenAnswer(new Answer<Fetched>() {
124                         @Override
125                         public Fetched answer(InvocationOnMock invocation) throws Throwable {
126                                 FreenetURI freenetUri = (FreenetURI) invocation.getArguments()[0];
127                                 return new Fetched(freenetUri, pastFetchResult);
128                         }
129                 });
130         }
131
132         private FetchResult createPastFetchResult() {
133                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
134                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
135                                 "CurrentVersion/Version: 0.2\n" +
136                                 "CurrentVersion/ReleaseTime: 1289417883000").getBytes());
137                 return new FetchResult(clientMetadata, fetched);
138         }
139
140         @Test
141         public void invalidUpdateFileDoesNotStartCallback() {
142                 setupFetchResult(createInvalidFetchResult());
143                 setupCallbackWithEdition(MAX_VALUE, true, false);
144                 verifyAFreenetUriIsFetched();
145                 verifyNoUpdateFoundEventIsFired();
146         }
147
148         private FetchResult createInvalidFetchResult() {
149                 ClientMetadata clientMetadata = new ClientMetadata("text/plain");
150                 Bucket fetched = new ArrayBucket("Some other data.".getBytes());
151                 return new FetchResult(clientMetadata, fetched);
152         }
153
154         @Test
155         public void nonExistingPropertiesWillNotCauseUpdateToBeFound() {
156                 setupCallbackWithEdition(MAX_VALUE, true, false);
157                 verifyAFreenetUriIsFetched();
158                 verifyNoUpdateFoundEventIsFired();
159         }
160
161         private void verifyNoUpdateFoundEventIsFired() {
162                 verify(eventBus, never()).post(any(UpdateFoundEvent.class));
163         }
164
165         private void verifyAFreenetUriIsFetched() {
166                 verify(freenetInterface).fetchUri(any(FreenetURI.class));
167         }
168
169         @Test
170         public void brokenBucketDoesNotCauseUpdateToBeFound() {
171                 setupFetchResult(createBrokenBucketFetchResult());
172                 setupCallbackWithEdition(MAX_VALUE, true, false);
173                 verifyAFreenetUriIsFetched();
174                 verifyNoUpdateFoundEventIsFired();
175         }
176
177         private FetchResult createBrokenBucketFetchResult() {
178                 ClientMetadata clientMetadata = new ClientMetadata("text/plain");
179                 Bucket fetched = new ArrayBucket("Some other data.".getBytes()) {
180                         @Override
181                         public InputStream getInputStream() {
182                                 try {
183                                         return when(mock(InputStream.class).read()).thenThrow(IOException.class).getMock();
184                                 } catch (IOException ioe1) {
185                                         /* won’t throw here. */
186                                         return null;
187                                 }
188                         }
189                 };
190                 return new FetchResult(clientMetadata, fetched);
191         }
192
193         @Test
194         public void invalidTimeDoesNotCauseAnUpdateToBeFound() {
195                 setupFetchResult(createInvalidTimeFetchResult());
196                 setupCallbackWithEdition(MAX_VALUE, true, false);
197                 verifyAFreenetUriIsFetched();
198                 verifyNoUpdateFoundEventIsFired();
199         }
200
201         private FetchResult createInvalidTimeFetchResult() {
202                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
203                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
204                                 "CurrentVersion/Version: 0.2\n" +
205                                 "CurrentVersion/ReleaseTime: invalid").getBytes());
206                 return new FetchResult(clientMetadata, fetched);
207         }
208
209         @Test
210         public void invalidPropertiesDoesNotCauseAnUpdateToBeFound() {
211                 setupFetchResult(createMissingTimeFetchResult());
212                 setupCallbackWithEdition(MAX_VALUE, true, false);
213                 verifyAFreenetUriIsFetched();
214                 verifyNoUpdateFoundEventIsFired();
215         }
216
217         private FetchResult createMissingTimeFetchResult() {
218                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
219                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
220                                 "CurrentVersion/Version: 0.2\n").getBytes());
221                 return new FetchResult(clientMetadata, fetched);
222         }
223
224         @Test
225         public void invalidVersionDoesNotCauseAnUpdateToBeFound() {
226                 setupFetchResult(createInvalidVersionFetchResult());
227                 setupCallbackWithEdition(MAX_VALUE, true, false);
228                 verifyAFreenetUriIsFetched();
229                 verifyNoUpdateFoundEventIsFired();
230         }
231
232         private FetchResult createInvalidVersionFetchResult() {
233                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
234                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
235                                 "CurrentVersion/Version: foo\n" +
236                                 "CurrentVersion/ReleaseTime: 1289417883000").getBytes());
237                 return new FetchResult(clientMetadata, fetched);
238         }
239
240         @Test
241         public void disruptiveVersionGetsNotification() {
242                 setupFetchResult(createDisruptiveVersionFetchResult());
243                 setupCallbackWithEdition(MAX_VALUE, true, false);
244                 verifyAFreenetUriIsFetched();
245                 verifyEventIsFired(new Version(1, 2, 3), 1289417883000L, true);
246                 verifyThatUpdateCheckerKnowsLatestVersion(new Version(1, 2, 3), 1289417883000L);
247         }
248
249         private FetchResult createDisruptiveVersionFetchResult() {
250                 ClientMetadata clientMetadata = new ClientMetadata("application/xml");
251                 Bucket fetched = new ArrayBucket(("# MapConfigurationBackendVersion=1\n" +
252                                 "CurrentVersion/Version: 1.2.3\n" +
253                                 "CurrentVersion/ReleaseTime: 1289417883000\n" +
254                                 "DisruptiveVersion/1.2.3: true").getBytes());
255                 return new FetchResult(clientMetadata, fetched);
256         }
257
258 }