Add tests for web of trust updater.
[Sone.git] / src / test / java / net / pterodactylus / sone / core / WebOfTrustUpdaterTest.java
1 package net.pterodactylus.sone.core;
2
3 import static java.lang.Thread.sleep;
4 import static java.util.concurrent.TimeUnit.SECONDS;
5 import static org.hamcrest.MatcherAssert.assertThat;
6 import static org.hamcrest.Matchers.containsString;
7 import static org.hamcrest.Matchers.is;
8 import static org.hamcrest.Matchers.not;
9 import static org.mockito.Matchers.eq;
10 import static org.mockito.Mockito.doAnswer;
11 import static org.mockito.Mockito.doThrow;
12 import static org.mockito.Mockito.mock;
13 import static org.mockito.Mockito.never;
14 import static org.mockito.Mockito.times;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.when;
17
18 import java.util.concurrent.CountDownLatch;
19 import java.util.concurrent.atomic.AtomicInteger;
20
21 import net.pterodactylus.sone.core.WebOfTrustUpdater.AddContextJob;
22 import net.pterodactylus.sone.core.WebOfTrustUpdater.RemoveContextJob;
23 import net.pterodactylus.sone.core.WebOfTrustUpdater.SetPropertyJob;
24 import net.pterodactylus.sone.core.WebOfTrustUpdater.SetTrustJob;
25 import net.pterodactylus.sone.core.WebOfTrustUpdater.WebOfTrustContextUpdateJob;
26 import net.pterodactylus.sone.core.WebOfTrustUpdater.WebOfTrustUpdateJob;
27 import net.pterodactylus.sone.freenet.plugin.PluginException;
28 import net.pterodactylus.sone.freenet.wot.Identity;
29 import net.pterodactylus.sone.freenet.wot.OwnIdentity;
30 import net.pterodactylus.sone.freenet.wot.Trust;
31 import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector;
32 import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
33
34 import org.junit.Test;
35 import org.mockito.invocation.InvocationOnMock;
36 import org.mockito.stubbing.Answer;
37
38 /**
39  * Unit test for {@link WebOfTrustUpdater} and its subclasses.
40  *
41  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
42  */
43 public class WebOfTrustUpdaterTest {
44
45         private static final String CONTEXT = "test-context";
46         private static final Integer SCORE = 50;
47         private static final Integer OTHER_SCORE = 25;
48         private static final String TRUST_COMMENT = "set in a test";
49         private static final String PROPERTY_NAME = "test-property";
50         private final WebOfTrustConnector webOfTrustConnector = mock(WebOfTrustConnector.class);
51         private final WebOfTrustUpdater webOfTrustUpdater = new WebOfTrustUpdater(webOfTrustConnector);
52         private final OwnIdentity ownIdentity = when(mock(OwnIdentity.class).getId()).thenReturn("own-identity-id").getMock();
53         private final WebOfTrustUpdateJob successfulWebOfTrustUpdateJob = createWebOfTrustUpdateJob(true);
54         private final WebOfTrustUpdateJob failingWebOfTrustUpdateJob = createWebOfTrustUpdateJob(false);
55         private final WebOfTrustContextUpdateJob contextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
56         private final AddContextJob addContextJob = webOfTrustUpdater.new AddContextJob(ownIdentity, CONTEXT);
57         private final RemoveContextJob removeContextJob = webOfTrustUpdater.new RemoveContextJob(ownIdentity, CONTEXT);
58         private final Identity trustee = when(mock(Identity.class).getId()).thenReturn("trustee-id").getMock();
59
60         private WebOfTrustUpdateJob createWebOfTrustUpdateJob(final boolean success) {
61                 return webOfTrustUpdater.new WebOfTrustUpdateJob() {
62                         @Override
63                         public void run() {
64                                 super.run();
65                                 try {
66                                         sleep(100);
67                                 } catch (InterruptedException ie1) {
68                                         throw new RuntimeException(ie1);
69                                 }
70                                 finish(success);
71                         }
72                 };
73         }
74
75         @Test
76         public void webOfTrustUpdateJobWaitsUntilFinishedHasBeenCalledAndReturnsSuccess() throws InterruptedException {
77                 new Thread(successfulWebOfTrustUpdateJob).start();
78                 assertThat(successfulWebOfTrustUpdateJob.waitForCompletion(), is(true));
79         }
80
81         @Test
82         public void webOfTrustUpdateJobWaitsUntilFinishedHasBeenCalledAndReturnsFailure() throws InterruptedException {
83                 new Thread(failingWebOfTrustUpdateJob).start();
84                 assertThat(failingWebOfTrustUpdateJob.waitForCompletion(), is(false));
85         }
86
87         @Test
88         public void webOfTrustContextUpdateJobsAreEqualIfTheirClassOwnIdentityAndContextAreEqual() {
89                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
90                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(true));
91                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(true));
92                 assertThat(contextUpdateJob.hashCode(), is(secondContextUpdateJob.hashCode()));
93         }
94
95         @Test
96         public void webOfTrustContextUpdatesJobsAreNotEqualIfTheirClassDiffers() {
97                 assertThat(contextUpdateJob.equals(addContextJob), is(false));
98         }
99
100         @Test
101         public void webOfTrustContextUpdateJobToStringContainsIdentityAndContext() {
102                 assertThat(contextUpdateJob.toString(), containsString(ownIdentity.toString()));
103                 assertThat(contextUpdateJob.toString(), containsString(CONTEXT));
104         }
105
106         @Test
107         public void webOfTrustContextUpdateJobsAreNotEqualIfTheIdentitiesDiffer() {
108                 OwnIdentity ownIdentity = mock(OwnIdentity.class);
109                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT);
110                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(false));
111                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(false));
112         }
113
114         @Test
115         public void webOfTrustContextUpdateJobsAreNotEqualIfTheirContextsDiffer() {
116                 WebOfTrustContextUpdateJob secondContextUpdateJob = webOfTrustUpdater.new WebOfTrustContextUpdateJob(ownIdentity, CONTEXT + CONTEXT);
117                 assertThat(contextUpdateJob.equals(secondContextUpdateJob), is(false));
118                 assertThat(secondContextUpdateJob.equals(contextUpdateJob), is(false));
119         }
120
121         @Test
122         public void webOfTrustContextUpdateJobsAreNotEqualToNull() {
123                 assertThat(contextUpdateJob.equals(null), is(false));
124         }
125
126         @Test
127         public void addContextJobAddsTheContext() throws PluginException {
128                 addContextJob.run();
129                 verify(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
130                 verify(ownIdentity).addContext(eq(CONTEXT));
131                 assertThat(addContextJob.waitForCompletion(), is(true));
132         }
133
134         @Test
135         public void exceptionWhileAddingAContextIsExposed() throws PluginException {
136                 doThrow(PluginException.class).when(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
137                 addContextJob.run();
138                 verify(webOfTrustConnector).addContext(eq(ownIdentity), eq(CONTEXT));
139                 verify(ownIdentity, never()).addContext(eq(CONTEXT));
140                 assertThat(addContextJob.waitForCompletion(), is(false));
141         }
142
143         @Test
144         public void removeContextJobRemovesTheContext() throws PluginException {
145                 removeContextJob.run();
146                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
147                 verify(ownIdentity).removeContext(eq(CONTEXT));
148                 assertThat(removeContextJob.waitForCompletion(), is(true));
149         }
150
151         @Test
152         public void exceptionWhileRemovingAContextIsExposed() throws PluginException {
153                 doThrow(PluginException.class).when(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
154                 removeContextJob.run();
155                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
156                 verify(ownIdentity, never()).removeContext(eq(CONTEXT));
157                 assertThat(removeContextJob.waitForCompletion(), is(false));
158         }
159
160         @Test
161         public void settingAPropertySetsTheProperty() throws PluginException {
162                 String propertyName = "property-name";
163                 String propertyValue = "property-value";
164                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
165                 setPropertyJob.run();
166                 verify(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
167                 verify(ownIdentity).setProperty(eq(propertyName), eq(propertyValue));
168                 assertThat(setPropertyJob.waitForCompletion(), is(true));
169         }
170
171         @Test
172         public void settingAPropertyToNullRemovesTheProperty() throws PluginException {
173                 String propertyName = "property-name";
174                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, null);
175                 setPropertyJob.run();
176                 verify(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(propertyName));
177                 verify(ownIdentity).removeProperty(eq(propertyName));
178                 assertThat(setPropertyJob.waitForCompletion(), is(true));
179         }
180
181         @Test
182         public void pluginExceptionWhileSettingAPropertyIsHandled() throws PluginException {
183                 String propertyName = "property-name";
184                 String propertyValue = "property-value";
185                 doThrow(PluginException.class).when(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
186                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
187                 setPropertyJob.run();
188                 verify(webOfTrustConnector).setProperty(eq(ownIdentity), eq(propertyName), eq(propertyValue));
189                 verify(ownIdentity, never()).setProperty(eq(propertyName), eq(propertyValue));
190                 assertThat(setPropertyJob.waitForCompletion(), is(false));
191         }
192
193         @Test
194         public void setPropertyJobsWithSameClassPropertyAndValueAreEqual() {
195                 String propertyName = "property-name";
196                 String propertyValue = "property-value";
197                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
198                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
199                 assertThat(firstSetPropertyJob, is(secondSetPropertyJob));
200                 assertThat(secondSetPropertyJob, is(firstSetPropertyJob));
201                 assertThat(firstSetPropertyJob.hashCode(), is(secondSetPropertyJob.hashCode()));
202         }
203
204         @Test
205         public void setPropertyJobsWithDifferentClassesAreNotEqual() {
206                 String propertyName = "property-name";
207                 String propertyValue = "property-value";
208                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
209                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue) {
210                 };
211                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
212         }
213
214         @Test
215         public void nullIsNotASetProjectJobEither() {
216                 String propertyName = "property-name";
217                 String propertyValue = "property-value";
218                 SetPropertyJob setPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
219                 assertThat(setPropertyJob, not(is((Object) null)));
220         }
221
222         @Test
223         public void setPropertyJobsWithDifferentPropertiesAreNotEqual() {
224                 String propertyName = "property-name";
225                 String propertyValue = "property-value";
226                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
227                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName + "2", propertyValue);
228                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
229         }
230
231         @Test
232         public void setPropertyJobsWithDifferentOwnIdentitiesAreNotEqual() {
233                 OwnIdentity otherOwnIdentity = mock(OwnIdentity.class);
234                 String propertyName = "property-name";
235                 String propertyValue = "property-value";
236                 SetPropertyJob firstSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(ownIdentity, propertyName, propertyValue);
237                 SetPropertyJob secondSetPropertyJob = webOfTrustUpdater.new SetPropertyJob(otherOwnIdentity, propertyName, propertyValue);
238                 assertThat(firstSetPropertyJob, not(is(secondSetPropertyJob)));
239         }
240
241         @Test
242         public void setTrustJobSetsTrust() throws PluginException {
243                 SetTrustJob setTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
244                 setTrustJob.run();
245                 verify(webOfTrustConnector).setTrust(eq(ownIdentity), eq(trustee), eq(SCORE), eq(TRUST_COMMENT));
246                 verify(trustee).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
247                 assertThat(setTrustJob.waitForCompletion(), is(true));
248         }
249
250         @Test
251         public void settingNullTrustRemovesTrust() throws WebOfTrustException {
252                 SetTrustJob setTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, null, TRUST_COMMENT);
253                 setTrustJob.run();
254                 verify(webOfTrustConnector).removeTrust(eq(ownIdentity), eq(trustee));
255                 verify(trustee).removeTrust(eq(ownIdentity));
256                 assertThat(setTrustJob.waitForCompletion(), is(true));
257         }
258
259         @Test
260         public void exceptionWhileSettingTrustIsCaught() throws PluginException {
261                 doThrow(PluginException.class).when(webOfTrustConnector).setTrust(eq(ownIdentity), eq(trustee), eq(SCORE), eq(TRUST_COMMENT));
262                 SetTrustJob setTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
263                 setTrustJob.run();
264                 verify(webOfTrustConnector).setTrust(eq(ownIdentity), eq(trustee), eq(SCORE), eq(TRUST_COMMENT));
265                 verify(trustee, never()).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
266                 assertThat(setTrustJob.waitForCompletion(), is(false));
267         }
268
269         @Test
270         public void setTrustJobsWithDifferentClassesAreNotEqual() {
271                 SetTrustJob firstSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
272                 SetTrustJob secondSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT) {
273                 };
274                 assertThat(firstSetTrustJob, not(is(secondSetTrustJob)));
275                 assertThat(secondSetTrustJob, not(is(firstSetTrustJob)));
276         }
277
278         @Test
279         public void setTrustJobsWithDifferentTrustersAreNotEqual() {
280                 SetTrustJob firstSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
281                 SetTrustJob secondSetTrustJob = webOfTrustUpdater.new SetTrustJob(mock(OwnIdentity.class), trustee, SCORE, TRUST_COMMENT);
282                 assertThat(firstSetTrustJob, not(is(secondSetTrustJob)));
283                 assertThat(secondSetTrustJob, not(is(firstSetTrustJob)));
284         }
285
286         @Test
287         public void setTrustJobsWithDifferentTrusteesAreNotEqual() {
288                 SetTrustJob firstSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
289                 SetTrustJob secondSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, mock(Identity.class), SCORE, TRUST_COMMENT);
290                 assertThat(firstSetTrustJob, not(is(secondSetTrustJob)));
291                 assertThat(secondSetTrustJob, not(is(firstSetTrustJob)));
292         }
293
294         @Test
295         public void setTrustJobsWithDifferentScoreAreEqual() {
296                 SetTrustJob firstSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
297                 SetTrustJob secondSetTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, OTHER_SCORE, TRUST_COMMENT);
298                 assertThat(firstSetTrustJob, is(secondSetTrustJob));
299                 assertThat(secondSetTrustJob, is(firstSetTrustJob));
300                 assertThat(firstSetTrustJob.hashCode(), is(secondSetTrustJob.hashCode()));
301         }
302
303         @Test
304         public void setTrustJobDoesNotEqualNull() {
305                 SetTrustJob setTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
306                 assertThat(setTrustJob, not(is((Object) null)));
307         }
308
309         @Test
310         public void toStringOfSetTrustJobContainsIdsOfTrusterAndTrustee() {
311                 SetTrustJob setTrustJob = webOfTrustUpdater.new SetTrustJob(ownIdentity, trustee, SCORE, TRUST_COMMENT);
312                 assertThat(setTrustJob.toString(), containsString(ownIdentity.getId()));
313                 assertThat(setTrustJob.toString(), containsString(trustee.getId()));
314         }
315
316         @Test
317         public void webOfTrustUpdaterStopsWhenItShould() {
318                 webOfTrustUpdater.stop();
319                 webOfTrustUpdater.serviceRun();
320         }
321
322         @Test
323         public void webOfTrustUpdaterStopsAfterItWasStarted() {
324                 webOfTrustUpdater.start();
325                 webOfTrustUpdater.stop();
326         }
327
328         @Test
329         public void removePropertyRemovesProperty() throws InterruptedException, PluginException {
330                 final CountDownLatch wotCallTriggered = new CountDownLatch(1);
331                 doAnswer(new Answer<Void>() {
332                         @Override
333                         public Void answer(InvocationOnMock invocation) throws Throwable {
334                                 wotCallTriggered.countDown();
335                                 return null;
336                         }
337                 }).when(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
338                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
339                 webOfTrustUpdater.start();
340                 assertThat(wotCallTriggered.await(1, SECONDS), is(true));
341         }
342
343         @Test
344         public void multipleCallsToSetPropertyAreCollapsed() throws InterruptedException, PluginException {
345                 final CountDownLatch wotCallTriggered = new CountDownLatch(1);
346                 doAnswer(new Answer<Void>() {
347                         @Override
348                         public Void answer(InvocationOnMock invocation) throws Throwable {
349                                 wotCallTriggered.countDown();
350                                 return null;
351                         }
352                 }).when(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
353                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
354                 webOfTrustUpdater.removeProperty(ownIdentity, PROPERTY_NAME);
355                 webOfTrustUpdater.start();
356                 assertThat(wotCallTriggered.await(1, SECONDS), is(true));
357                 verify(webOfTrustConnector).removeProperty(eq(ownIdentity), eq(PROPERTY_NAME));
358         }
359
360         @Test
361         public void addContextWaitWaitsForTheContextToBeAdded() {
362                 webOfTrustUpdater.start();
363                 assertThat(webOfTrustUpdater.addContextWait(ownIdentity, CONTEXT), is(true));
364                 verify(ownIdentity).addContext(eq(CONTEXT));
365         }
366
367         @Test
368         public void multipleCallsToAddContextAreCollapsed() throws InterruptedException, PluginException {
369                 final AtomicInteger errorCount = new AtomicInteger();
370                 final CountDownLatch addContextsFinished = new CountDownLatch(2);
371                 for (int i = 1; i <= 2; i++) {
372                         /* this is so fucking volatile. */
373                         if (i > 1) {
374                                 sleep(200);
375                         }
376                         new Thread(new Runnable() {
377                                 public void run() {
378                                         if (!webOfTrustUpdater.addContextWait(ownIdentity, CONTEXT)) {
379                                                 errorCount.incrementAndGet();
380                                         }
381                                         addContextsFinished.countDown();
382                                 }
383                         }).start();
384                 }
385                 webOfTrustUpdater.start();
386                 assertThat(addContextsFinished.await(1, SECONDS), is(true));
387                 verify(ownIdentity).addContext(eq(CONTEXT));
388                 assertThat(errorCount.get(), is(0));
389         }
390
391         @Test
392         public void removeContextRemovesAContext() throws InterruptedException, PluginException {
393                 webOfTrustUpdater.start();
394                 final CountDownLatch removeContextTrigger = new CountDownLatch(1);
395                 doAnswer(new Answer<Void>() {
396                         @Override
397                         public Void answer(InvocationOnMock invocation) throws Throwable {
398                                 removeContextTrigger.countDown();
399                                 return null;
400                         }
401                 }).when(ownIdentity).removeContext(eq(CONTEXT));
402                 webOfTrustUpdater.removeContext(ownIdentity, CONTEXT);
403                 removeContextTrigger.await(1, SECONDS);
404                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
405                 verify(ownIdentity).removeContext(eq(CONTEXT));
406         }
407
408         @Test
409         public void removeContextRequestsAreCoalesced() throws InterruptedException, PluginException {
410                 final CountDownLatch contextRemovedTrigger = new CountDownLatch(1);
411                 doAnswer(new Answer<Void>() {
412                         @Override
413                         public Void answer(InvocationOnMock invocation) throws Throwable {
414                                 contextRemovedTrigger.countDown();
415                                 return null;
416                         }
417                 }).when(ownIdentity).removeContext(eq(CONTEXT));
418                 for (int i = 1; i <= 2; i++) {
419                         /* this is so fucking volatile. */
420                         if (i > 1) {
421                                 sleep(200);
422                         }
423                         new Thread(new Runnable() {
424                                 public void run() {
425                                         webOfTrustUpdater.removeContext(ownIdentity, CONTEXT);
426                                 }
427                         }).start();
428                 }
429                 webOfTrustUpdater.start();
430                 assertThat(contextRemovedTrigger.await(1, SECONDS), is(true));
431                 verify(webOfTrustConnector).removeContext(eq(ownIdentity), eq(CONTEXT));
432                 verify(ownIdentity).removeContext(eq(CONTEXT));
433         }
434
435         @Test
436         public void setTrustSetsTrust() throws InterruptedException, PluginException {
437                 final CountDownLatch trustSetTrigger =new CountDownLatch(1);
438                 doAnswer(new Answer<Void>() {
439                         @Override
440                         public Void answer(InvocationOnMock invocation) throws Throwable {
441                                 trustSetTrigger.countDown();
442                                 return null;
443                         }
444                 }).when(trustee).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
445                 webOfTrustUpdater.start();
446                 webOfTrustUpdater.setTrust(ownIdentity, trustee, SCORE, TRUST_COMMENT);
447                 assertThat(trustSetTrigger.await(1, SECONDS), is(true));
448                 verify(trustee).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
449                 verify(webOfTrustConnector).setTrust(eq(ownIdentity), eq(trustee), eq(SCORE), eq(TRUST_COMMENT));
450         }
451
452         @Test
453         public void setTrustRequestsAreCoalesced() throws InterruptedException, PluginException {
454                 final CountDownLatch trustSetTrigger = new CountDownLatch(1);
455                 doAnswer(new Answer<Void>() {
456                         @Override
457                         public Void answer(InvocationOnMock invocation) throws Throwable {
458                                 trustSetTrigger.countDown();
459                                 return null;
460                         }
461                 }).when(trustee).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
462                 for (int i = 1; i <= 2; i++) {
463                         /* this is so fucking volatile. */
464                         if (i > 1) {
465                                 sleep(200);
466                         }
467                         new Thread(new Runnable() {
468                                 public void run() {
469                                         webOfTrustUpdater.setTrust(ownIdentity, trustee, SCORE, TRUST_COMMENT);
470                                 }
471                         }).start();
472                 }
473                 webOfTrustUpdater.start();
474                 assertThat(trustSetTrigger.await(1, SECONDS), is(true));
475                 verify(trustee).setTrust(eq(ownIdentity), eq(new Trust(SCORE, null, 0)));
476                 verify(webOfTrustConnector).setTrust(eq(ownIdentity), eq(trustee), eq(SCORE), eq(TRUST_COMMENT));
477         }
478
479 }