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