Add more tests for the Sone inserter.
[Sone.git] / src / test / java / net / pterodactylus / sone / core / SoneInserterTest.java
1 package net.pterodactylus.sone.core;
2
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.of;
5 import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
6 import static org.hamcrest.MatcherAssert.assertThat;
7 import static org.hamcrest.Matchers.containsInAnyOrder;
8 import static org.hamcrest.Matchers.instanceOf;
9 import static org.hamcrest.Matchers.is;
10 import static org.mockito.Matchers.any;
11 import static org.mockito.Matchers.anyString;
12 import static org.mockito.Matchers.argThat;
13 import static org.mockito.Matchers.eq;
14 import static org.mockito.Mockito.doAnswer;
15 import static org.mockito.Mockito.doThrow;
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.util.HashMap;
23
24 import net.pterodactylus.sone.core.SoneInserter.InsertInformation;
25 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
26 import net.pterodactylus.sone.core.event.SoneEvent;
27 import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent;
28 import net.pterodactylus.sone.core.event.SoneInsertedEvent;
29 import net.pterodactylus.sone.core.event.SoneInsertingEvent;
30 import net.pterodactylus.sone.data.Album;
31 import net.pterodactylus.sone.data.Sone;
32
33 import freenet.keys.FreenetURI;
34
35 import com.google.common.base.Optional;
36 import com.google.common.eventbus.AsyncEventBus;
37 import com.google.common.eventbus.EventBus;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.mockito.ArgumentCaptor;
41 import org.mockito.invocation.InvocationOnMock;
42 import org.mockito.stubbing.Answer;
43
44 /**
45  * Unit test for {@link SoneInserter} and its subclasses.
46  *
47  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
48  */
49 public class SoneInserterTest {
50
51         private final Core core = mock(Core.class);
52         private final EventBus eventBus = mock(EventBus.class);
53         private final FreenetInterface freenetInterface = mock(FreenetInterface.class);
54
55         @Before
56         public void setupCore() {
57                 UpdateChecker updateChecker = mock(UpdateChecker.class);
58                 when(core.getUpdateChecker()).thenReturn(updateChecker);
59                 when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
60         }
61
62         @Test
63         public void insertionDelayIsForwardedToSoneInserter() {
64                 EventBus eventBus = new AsyncEventBus(sameThreadExecutor());
65                 eventBus.register(new SoneInserter(core, eventBus, freenetInterface, "SoneId"));
66                 eventBus.post(new InsertionDelayChangedEvent(15));
67                 assertThat(SoneInserter.getInsertionDelay().get(), is(15));
68         }
69
70         @Test
71         /* this test is hilariously bad. */
72         public void manifestEntriesAreCreated() {
73                 FreenetURI insertUri = mock(FreenetURI.class);
74                 String fingerprint = "fingerprint";
75                 Sone sone = createSone(insertUri, fingerprint);
76                 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
77                 InsertInformation insertInformation = soneInserter.new InsertInformation(sone);
78                 HashMap<String, Object> manifestEntries = insertInformation.generateManifestEntries();
79                 assertThat(manifestEntries.keySet(), containsInAnyOrder("index.html", "sone.xml"));
80                 assertThat(insertInformation.getFingerprint(), is(fingerprint));
81         }
82
83         private Sone createSone(FreenetURI insertUri, String fingerprint) {
84                 Sone sone = mock(Sone.class);
85                 when(sone.getInsertUri()).thenReturn(insertUri);
86                 when(sone.getFingerprint()).thenReturn(fingerprint);
87                 when(sone.getRootAlbum()).thenReturn(mock(Album.class));
88                 when(core.getSone(anyString())).thenReturn(of(sone));
89                 return sone;
90         }
91
92         @Test
93         public void isModifiedIsTrueIfModificationDetectorSaysSo() {
94                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
95                 when(soneModificationDetector.isModified()).thenReturn(true);
96                 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
97                 assertThat(soneInserter.isModified(), is(true));
98         }
99
100         @Test
101         public void isModifiedIsFalseIfModificationDetectorSaysSo() {
102                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
103                 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
104                 assertThat(soneInserter.isModified(), is(false));
105         }
106
107         @Test
108         public void lastFingerprintIsStoredCorrectly() {
109                 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
110                 soneInserter.setLastInsertFingerprint("last-fingerprint");
111                 assertThat(soneInserter.getLastInsertFingerprint(), is("last-fingerprint"));
112         }
113
114         @Test
115         public void soneInserterStopsWhenItShould() {
116                 SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId");
117                 soneInserter.stop();
118                 soneInserter.serviceRun();
119         }
120
121         @Test
122         public void soneInserterInsertsASoneIfItIsEligible() throws SoneException {
123                 FreenetURI insertUri = mock(FreenetURI.class);
124                 final FreenetURI finalUri = mock(FreenetURI.class);
125                 String fingerprint = "fingerprint";
126                 Sone sone = createSone(insertUri, fingerprint);
127                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
128                 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
129                 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenReturn(finalUri);
130                 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
131                 doAnswer(new Answer<Void>() {
132                         @Override
133                         public Void answer(InvocationOnMock invocation) throws Throwable {
134                                 soneInserter.stop();
135                                 return null;
136                         }
137                 }).when(core).touchConfiguration();
138                 soneInserter.serviceRun();
139                 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
140                 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
141                 verify(eventBus, times(2)).post(soneEvents.capture());
142                 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
143                 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
144                 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
145                 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
146         }
147
148         @Test
149         public void soneInserterBailsOutIfItIsStoppedWhileInserting() throws SoneException {
150                 FreenetURI insertUri = mock(FreenetURI.class);
151                 final FreenetURI finalUri = mock(FreenetURI.class);
152                 String fingerprint = "fingerprint";
153                 Sone sone = createSone(insertUri, fingerprint);
154                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
155                 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
156                 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
157                 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
158                         @Override
159                         public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
160                                 soneInserter.stop();
161                                 return finalUri;
162                         }
163                 });
164                 soneInserter.serviceRun();
165                 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
166                 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
167                 verify(eventBus, times(2)).post(soneEvents.capture());
168                 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
169                 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
170                 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertedEvent.class));
171                 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
172                 verify(core, never()).touchConfiguration();
173         }
174
175         @Test
176         public void soneInserterDoesNotInsertSoneIfItIsNotEligible() throws SoneException {
177                 FreenetURI insertUri = mock(FreenetURI.class);
178                 String fingerprint = "fingerprint";
179                 Sone sone = createSone(insertUri, fingerprint);
180                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
181                 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
182                 new Thread(new Runnable() {
183                         @Override
184                         public void run() {
185                                 try {
186                                         Thread.sleep(500);
187                                 } catch (InterruptedException ie1) {
188                                         throw new RuntimeException(ie1);
189                                 }
190                                 soneInserter.stop();
191                         }
192                 }).start();
193                 soneInserter.serviceRun();
194                 verify(freenetInterface, never()).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
195                 verify(eventBus, never()).post(argThat(org.hamcrest.Matchers.any(SoneEvent.class)));
196         }
197
198         @Test
199         public void soneInserterPostsAbortedEventIfAnExceptionOccurs() throws SoneException {
200                 FreenetURI insertUri = mock(FreenetURI.class);
201                 String fingerprint = "fingerprint";
202                 Sone sone = createSone(insertUri, fingerprint);
203                 SoneModificationDetector soneModificationDetector = mock(SoneModificationDetector.class);
204                 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
205                 final SoneInserter soneInserter = new SoneInserter(core, eventBus, freenetInterface, "SoneId", soneModificationDetector, 1);
206                 final SoneException soneException = new SoneException(new Exception());
207                 when(freenetInterface.insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"))).thenAnswer(new Answer<FreenetURI>() {
208                         @Override
209                         public FreenetURI answer(InvocationOnMock invocation) throws Throwable {
210                                 soneInserter.stop();
211                                 throw soneException;
212                         }
213                 });
214                 soneInserter.serviceRun();
215                 ArgumentCaptor<SoneEvent> soneEvents = ArgumentCaptor.forClass(SoneEvent.class);
216                 verify(freenetInterface).insertDirectory(eq(insertUri), any(HashMap.class), eq("index.html"));
217                 verify(eventBus, times(2)).post(soneEvents.capture());
218                 assertThat(soneEvents.getAllValues().get(0), instanceOf(SoneInsertingEvent.class));
219                 assertThat(soneEvents.getAllValues().get(0).sone(), is(sone));
220                 assertThat(soneEvents.getAllValues().get(1), instanceOf(SoneInsertAbortedEvent.class));
221                 assertThat(soneEvents.getAllValues().get(1).sone(), is(sone));
222                 verify(core, never()).touchConfiguration();
223         }
224
225         @Test
226         public void soneInserterExitsIfSoneIsUnknown() {
227                 SoneModificationDetector soneModificationDetector =
228                                 mock(SoneModificationDetector.class);
229                 SoneInserter soneInserter =
230                                 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
231                                                 soneModificationDetector, 1);
232                 when(soneModificationDetector.isEligibleForInsert()).thenReturn(true);
233                 when(core.getSone("SoneId")).thenReturn(Optional.<Sone>absent());
234                 soneInserter.serviceRun();
235         }
236
237         @Test
238         public void soneInserterCatchesExceptionAndContinues() {
239                 SoneModificationDetector soneModificationDetector =
240                                 mock(SoneModificationDetector.class);
241                 final SoneInserter soneInserter =
242                                 new SoneInserter(core, eventBus, freenetInterface, "SoneId",
243                                                 soneModificationDetector, 1);
244                 Answer<Optional<Sone>> stopInserterAndThrowException =
245                                 new Answer<Optional<Sone>>() {
246                                         @Override
247                                         public Optional<Sone> answer(
248                                                         InvocationOnMock invocation) {
249                                                 soneInserter.stop();
250                                                 throw new NullPointerException();
251                                         }
252                                 };
253                 when(soneModificationDetector.isEligibleForInsert()).thenAnswer(
254                                 stopInserterAndThrowException);
255                 soneInserter.serviceRun();
256         }
257
258 }