b0b50ae8026a13ee324658cc209f3db8f5b97424
[jFCPlib.git] / src / test / java / net / pterodactylus / fcp / quelaton / DefaultFcpClientTest.java
1 package net.pterodactylus.fcp.quelaton;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.contains;
5 import static org.hamcrest.Matchers.containsInAnyOrder;
6 import static org.hamcrest.Matchers.hasSize;
7 import static org.hamcrest.Matchers.is;
8 import static org.hamcrest.Matchers.notNullValue;
9
10 import java.io.ByteArrayInputStream;
11 import java.io.File;
12 import java.io.IOException;
13 import java.net.URL;
14 import java.nio.charset.StandardCharsets;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Optional;
18 import java.util.concurrent.CopyOnWriteArrayList;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.ExecutorService;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.Future;
23 import java.util.stream.Collectors;
24
25 import net.pterodactylus.fcp.ARK;
26 import net.pterodactylus.fcp.DSAGroup;
27 import net.pterodactylus.fcp.FcpKeyPair;
28 import net.pterodactylus.fcp.Key;
29 import net.pterodactylus.fcp.NodeData;
30 import net.pterodactylus.fcp.NodeRef;
31 import net.pterodactylus.fcp.Peer;
32 import net.pterodactylus.fcp.PeerNote;
33 import net.pterodactylus.fcp.Priority;
34 import net.pterodactylus.fcp.fake.FakeTcpServer;
35 import net.pterodactylus.fcp.quelaton.ClientGetCommand.Data;
36
37 import com.google.common.io.ByteStreams;
38 import com.google.common.io.Files;
39 import org.hamcrest.Description;
40 import org.hamcrest.Matcher;
41 import org.hamcrest.TypeSafeDiagnosingMatcher;
42 import org.junit.After;
43 import org.junit.Assert;
44 import org.junit.Test;
45
46 /**
47  * Unit test for {@link DefaultFcpClient}.
48  *
49  * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
50  */
51 public class DefaultFcpClientTest {
52
53         private static final String INSERT_URI =
54                 "SSK@RVCHbJdkkyTCeNN9AYukEg76eyqmiosSaNKgE3U9zUw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQECAAE/";
55         private static final String REQUEST_URI =
56                 "SSK@wtbgd2loNcJCXvtQVOftl2tuWBomDQHfqS6ytpPRhfw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQACAAE/";
57
58         private static int threadCounter = 0;
59         private final FakeTcpServer fcpServer;
60         private final DefaultFcpClient fcpClient;
61
62         public DefaultFcpClientTest() throws IOException {
63                 ExecutorService threadPool =
64                         Executors.newCachedThreadPool(r -> new Thread(r, "Test-Thread-" + threadCounter++));
65                 fcpServer = new FakeTcpServer(threadPool);
66                 fcpClient = new DefaultFcpClient(threadPool, "localhost", fcpServer.getPort(), () -> "Test");
67         }
68
69         @After
70         public void tearDown() throws IOException {
71                 fcpServer.close();
72         }
73
74         @Test(expected = ExecutionException.class)
75         public void defaultFcpClientThrowsExceptionIfItCanNotConnect()
76         throws IOException, ExecutionException, InterruptedException {
77                 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
78                 fcpServer.connect().get();
79                 fcpServer.collectUntil(is("EndMessage"));
80                 fcpServer.writeLine(
81                         "CloseConnectionDuplicateClientName",
82                         "EndMessage"
83                 );
84                 keyPairFuture.get();
85         }
86
87         @Test(expected = ExecutionException.class)
88         public void defaultFcpClientThrowsExceptionIfConnectionIsClosed()
89         throws IOException, ExecutionException, InterruptedException {
90                 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
91                 fcpServer.connect().get();
92                 fcpServer.collectUntil(is("EndMessage"));
93                 fcpServer.close();
94                 keyPairFuture.get();
95         }
96
97         @Test
98         public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
99                 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
100                 connectNode();
101                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
102                 String identifier = extractIdentifier(lines);
103                 fcpServer.writeLine("SSKKeypair",
104                         "InsertURI=" + INSERT_URI + "",
105                         "RequestURI=" + REQUEST_URI + "",
106                         "Identifier=" + identifier,
107                         "EndMessage");
108                 FcpKeyPair keyPair = keyPairFuture.get();
109                 assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
110                 assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
111         }
112
113         private void connectNode() throws InterruptedException, ExecutionException, IOException {
114                 fcpServer.connect().get();
115                 fcpServer.collectUntil(is("EndMessage"));
116                 fcpServer.writeLine("NodeHello",
117                         "CompressionCodecs=4 - GZIP(0), BZIP2(1), LZMA(2), LZMA_NEW(3)",
118                         "Revision=build01466",
119                         "Testnet=false",
120                         "Version=Fred,0.7,1.0,1466",
121                         "Build=1466",
122                         "ConnectionIdentifier=14318898267048452a81b36e7f13a3f0",
123                         "Node=Fred",
124                         "ExtBuild=29",
125                         "FCPVersion=2.0",
126                         "NodeLanguage=ENGLISH",
127                         "ExtRevision=v29",
128                         "EndMessage"
129                 );
130         }
131
132         @Test
133         public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
134                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
135                 connectNode();
136                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
137                 assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
138                 String identifier = extractIdentifier(lines);
139                 fcpServer.writeLine(
140                         "AllData",
141                         "Identifier=" + identifier,
142                         "DataLength=6",
143                         "StartupTime=1435610539000",
144                         "CompletionTime=1435610540000",
145                         "Metadata.ContentType=text/plain;charset=utf-8",
146                         "Data",
147                         "Hello"
148                 );
149                 Optional<Data> data = dataFuture.get();
150                 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
151                 assertThat(data.get().size(), is(6L));
152                 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
153                         is("Hello\n".getBytes(StandardCharsets.UTF_8)));
154         }
155
156         private String extractIdentifier(List<String> lines) {
157                 return lines.stream()
158                         .filter(s -> s.startsWith("Identifier="))
159                         .map(s -> s.substring(s.indexOf('=') + 1))
160                         .findFirst()
161                         .orElse("");
162         }
163
164         @Test
165         public void clientGetDownloadsDataForCorrectIdentifier()
166         throws InterruptedException, ExecutionException, IOException {
167                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
168                 connectNode();
169                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
170                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
171                 String identifier = extractIdentifier(lines);
172                 fcpServer.writeLine(
173                         "AllData",
174                         "Identifier=not-test",
175                         "DataLength=12",
176                         "StartupTime=1435610539000",
177                         "CompletionTime=1435610540000",
178                         "Metadata.ContentType=text/plain;charset=latin-9",
179                         "Data",
180                         "Hello World"
181                 );
182                 fcpServer.writeLine(
183                         "AllData",
184                         "Identifier=" + identifier,
185                         "DataLength=6",
186                         "StartupTime=1435610539000",
187                         "CompletionTime=1435610540000",
188                         "Metadata.ContentType=text/plain;charset=utf-8",
189                         "Data",
190                         "Hello"
191                 );
192                 Optional<Data> data = dataFuture.get();
193                 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
194                 assertThat(data.get().size(), is(6L));
195                 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
196                         is("Hello\n".getBytes(StandardCharsets.UTF_8)));
197         }
198
199         @Test
200         public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
201                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
202                 connectNode();
203                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
204                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
205                 String identifier = extractIdentifier(lines);
206                 fcpServer.writeLine(
207                         "GetFailed",
208                         "Identifier=" + identifier,
209                         "Code=3",
210                         "EndMessage"
211                 );
212                 Optional<Data> data = dataFuture.get();
213                 assertThat(data.isPresent(), is(false));
214         }
215
216         @Test
217         public void clientGetRecognizesGetFailedForCorrectIdentifier()
218         throws InterruptedException, ExecutionException, IOException {
219                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
220                 connectNode();
221                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
222                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
223                 String identifier = extractIdentifier(lines);
224                 fcpServer.writeLine(
225                         "GetFailed",
226                         "Identifier=not-test",
227                         "Code=3",
228                         "EndMessage"
229                 );
230                 fcpServer.writeLine(
231                         "GetFailed",
232                         "Identifier=" + identifier,
233                         "Code=3",
234                         "EndMessage"
235                 );
236                 Optional<Data> data = dataFuture.get();
237                 assertThat(data.isPresent(), is(false));
238         }
239
240         @Test(expected = ExecutionException.class)
241         public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
242                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
243                 connectNode();
244                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
245                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
246                 fcpServer.close();
247                 dataFuture.get();
248         }
249
250         @Test
251         public void defaultFcpClientReusesConnection() throws InterruptedException, ExecutionException, IOException {
252                 Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
253                 connectNode();
254                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
255                 String identifier = extractIdentifier(lines);
256                 fcpServer.writeLine(
257                         "SSKKeypair",
258                         "InsertURI=" + INSERT_URI + "",
259                         "RequestURI=" + REQUEST_URI + "",
260                         "Identifier=" + identifier,
261                         "EndMessage"
262                 );
263                 keyPair.get();
264                 keyPair = fcpClient.generateKeypair().execute();
265                 lines = fcpServer.collectUntil(is("EndMessage"));
266                 identifier = extractIdentifier(lines);
267                 fcpServer.writeLine(
268                         "SSKKeypair",
269                         "InsertURI=" + INSERT_URI + "",
270                         "RequestURI=" + REQUEST_URI + "",
271                         "Identifier=" + identifier,
272                         "EndMessage"
273                 );
274                 keyPair.get();
275         }
276
277         @Test
278         public void defaultFcpClientCanReconnectAfterConnectionHasBeenClosed()
279         throws InterruptedException, ExecutionException, IOException {
280                 Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
281                 connectNode();
282                 fcpServer.collectUntil(is("EndMessage"));
283                 fcpServer.close();
284                 try {
285                         keyPair.get();
286                         Assert.fail();
287                 } catch (ExecutionException e) {
288                 }
289                 keyPair = fcpClient.generateKeypair().execute();
290                 connectNode();
291                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
292                 String identifier = extractIdentifier(lines);
293                 fcpServer.writeLine(
294                         "SSKKeypair",
295                         "InsertURI=" + INSERT_URI + "",
296                         "RequestURI=" + REQUEST_URI + "",
297                         "Identifier=" + identifier,
298                         "EndMessage"
299                 );
300                 keyPair.get();
301         }
302
303         @Test
304         public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands()
305         throws InterruptedException, ExecutionException, IOException {
306                 fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt").execute();
307                 connectNode();
308                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
309                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
310         }
311
312         @Test
313         public void clientGetWithDataStoreOnlySettingSendsCorrectCommands()
314         throws InterruptedException, ExecutionException, IOException {
315                 fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt").execute();
316                 connectNode();
317                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
318                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
319         }
320
321         @Test
322         public void clientGetWithMaxSizeSettingSendsCorrectCommands()
323         throws InterruptedException, ExecutionException, IOException {
324                 fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt").execute();
325                 connectNode();
326                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
327                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
328         }
329
330         @Test
331         public void clientGetWithPrioritySettingSendsCorrectCommands()
332         throws InterruptedException, ExecutionException, IOException {
333                 fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt").execute();
334                 connectNode();
335                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
336                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
337         }
338
339         @Test
340         public void clientGetWithRealTimeSettingSendsCorrectCommands()
341         throws InterruptedException, ExecutionException, IOException {
342                 fcpClient.clientGet().realTime().uri("KSK@foo.txt").execute();
343                 connectNode();
344                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
345                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
346         }
347
348         @Test
349         public void clientGetWithGlobalSettingSendsCorrectCommands()
350         throws InterruptedException, ExecutionException, IOException {
351                 fcpClient.clientGet().global().uri("KSK@foo.txt").execute();
352                 connectNode();
353                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
354                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
355         }
356
357         private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
358                 return new TypeSafeDiagnosingMatcher<List<String>>() {
359                         @Override
360                         protected boolean matchesSafely(List<String> item, Description mismatchDescription) {
361                                 if (!item.get(0).equals(name)) {
362                                         mismatchDescription.appendText("FCP message is named ").appendValue(item.get(0));
363                                         return false;
364                                 }
365                                 for (String requiredLine : requiredLines) {
366                                         if (item.indexOf(requiredLine) < 1) {
367                                                 mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
368                                                 return false;
369                                         }
370                                 }
371                                 return true;
372                         }
373
374                         @Override
375                         public void describeTo(Description description) {
376                                 description.appendText("FCP message named ").appendValue(name);
377                                 description.appendValueList(", containing the lines ", ", ", "", requiredLines);
378                         }
379                 };
380         }
381
382         @Test
383         public void clientPutWithDirectDataSendsCorrectCommand()
384         throws IOException, ExecutionException, InterruptedException {
385                 fcpClient.clientPut()
386                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
387                         .length(6)
388                         .uri("KSK@foo.txt")
389                         .execute();
390                 connectNode();
391                 List<String> lines = fcpServer.collectUntil(is("Hello"));
392                 assertThat(lines, matchesFcpMessage("ClientPut", "UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
393         }
394
395         @Test
396         public void clientPutWithDirectDataSucceedsOnCorrectIdentifier()
397         throws InterruptedException, ExecutionException, IOException {
398                 Future<Optional<Key>> key = fcpClient.clientPut()
399                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
400                         .length(6)
401                         .uri("KSK@foo.txt")
402                         .execute();
403                 connectNode();
404                 List<String> lines = fcpServer.collectUntil(is("Hello"));
405                 String identifier = extractIdentifier(lines);
406                 fcpServer.writeLine(
407                         "PutFailed",
408                         "Identifier=not-the-right-one",
409                         "EndMessage"
410                 );
411                 fcpServer.writeLine(
412                         "PutSuccessful",
413                         "URI=KSK@foo.txt",
414                         "Identifier=" + identifier,
415                         "EndMessage"
416                 );
417                 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
418         }
419
420         @Test
421         public void clientPutWithDirectDataFailsOnCorrectIdentifier()
422         throws InterruptedException, ExecutionException, IOException {
423                 Future<Optional<Key>> key = fcpClient.clientPut()
424                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
425                         .length(6)
426                         .uri("KSK@foo.txt")
427                         .execute();
428                 connectNode();
429                 List<String> lines = fcpServer.collectUntil(is("Hello"));
430                 String identifier = extractIdentifier(lines);
431                 fcpServer.writeLine(
432                         "PutSuccessful",
433                         "Identifier=not-the-right-one",
434                         "URI=KSK@foo.txt",
435                         "EndMessage"
436                 );
437                 fcpServer.writeLine(
438                         "PutFailed",
439                         "Identifier=" + identifier,
440                         "EndMessage"
441                 );
442                 assertThat(key.get().isPresent(), is(false));
443         }
444
445         @Test
446         public void clientPutWithRenamedDirectDataSendsCorrectCommand()
447         throws InterruptedException, ExecutionException, IOException {
448                 fcpClient.clientPut()
449                         .named("otherName.txt")
450                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
451                         .length(6)
452                         .uri("KSK@foo.txt")
453                         .execute();
454                 connectNode();
455                 List<String> lines = fcpServer.collectUntil(is("Hello"));
456                 assertThat(lines, matchesFcpMessage("ClientPut", "TargetFilename=otherName.txt", "UploadFrom=direct",
457                         "DataLength=6", "URI=KSK@foo.txt"));
458         }
459
460         @Test
461         public void clientPutWithRedirectSendsCorrectCommand()
462         throws IOException, ExecutionException, InterruptedException {
463                 fcpClient.clientPut().redirectTo("KSK@bar.txt").uri("KSK@foo.txt").execute();
464                 connectNode();
465                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
466                 assertThat(lines,
467                         matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
468         }
469
470         @Test
471         public void clientPutWithFileSendsCorrectCommand() throws InterruptedException, ExecutionException, IOException {
472                 fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
473                 connectNode();
474                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
475                 assertThat(lines,
476                         matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));
477         }
478
479         @Test
480         public void clientPutWithFileCanCompleteTestDdaSequence()
481         throws IOException, ExecutionException, InterruptedException {
482                 File tempFile = createTempFile();
483                 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
484                 connectNode();
485                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
486                 String identifier = extractIdentifier(lines);
487                 fcpServer.writeLine(
488                         "ProtocolError",
489                         "Identifier=" + identifier,
490                         "Code=25",
491                         "EndMessage"
492                 );
493                 lines = fcpServer.collectUntil(is("EndMessage"));
494                 assertThat(lines, matchesFcpMessage(
495                         "TestDDARequest",
496                         "Directory=" + tempFile.getParent(),
497                         "WantReadDirectory=true",
498                         "WantWriteDirectory=false",
499                         "EndMessage"
500                 ));
501                 fcpServer.writeLine(
502                         "TestDDAReply",
503                         "Directory=" + tempFile.getParent(),
504                         "ReadFilename=" + tempFile,
505                         "EndMessage"
506                 );
507                 lines = fcpServer.collectUntil(is("EndMessage"));
508                 assertThat(lines, matchesFcpMessage(
509                         "TestDDAResponse",
510                         "Directory=" + tempFile.getParent(),
511                         "ReadContent=test-content",
512                         "EndMessage"
513                 ));
514                 fcpServer.writeLine(
515                         "TestDDAComplete",
516                         "Directory=" + tempFile.getParent(),
517                         "ReadDirectoryAllowed=true",
518                         "EndMessage"
519                 );
520                 lines = fcpServer.collectUntil(is("EndMessage"));
521                 assertThat(lines,
522                         matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt",
523                                 "Filename=" + new File(tempFile.getParent(), "test.dat")));
524         }
525
526         private File createTempFile() throws IOException {
527                 File tempFile = File.createTempFile("test-dda-", ".dat");
528                 tempFile.deleteOnExit();
529                 Files.write("test-content", tempFile, StandardCharsets.UTF_8);
530                 return tempFile;
531         }
532
533         @Test
534         public void clientPutDoesNotReactToProtocolErrorForDifferentIdentifier()
535         throws InterruptedException, ExecutionException, IOException {
536                 Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
537                 connectNode();
538                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
539                 String identifier = extractIdentifier(lines);
540                 fcpServer.writeLine(
541                         "ProtocolError",
542                         "Identifier=not-the-right-one",
543                         "Code=25",
544                         "EndMessage"
545                 );
546                 fcpServer.writeLine(
547                         "PutSuccessful",
548                         "Identifier=" + identifier,
549                         "URI=KSK@foo.txt",
550                         "EndMessage"
551                 );
552                 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
553         }
554
555         @Test
556         public void clientPutAbortsOnProtocolErrorOtherThan25()
557         throws InterruptedException, ExecutionException, IOException {
558                 Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
559                 connectNode();
560                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
561                 String identifier = extractIdentifier(lines);
562                 fcpServer.writeLine(
563                         "ProtocolError",
564                         "Identifier=" + identifier,
565                         "Code=1",
566                         "EndMessage"
567                 );
568                 assertThat(key.get().isPresent(), is(false));
569         }
570
571         @Test
572         public void clientPutDoesNotReplyToWrongTestDdaReply() throws IOException, ExecutionException,
573         InterruptedException {
574                 File tempFile = createTempFile();
575                 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
576                 connectNode();
577                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
578                 String identifier = extractIdentifier(lines);
579                 fcpServer.writeLine(
580                         "ProtocolError",
581                         "Identifier=" + identifier,
582                         "Code=25",
583                         "EndMessage"
584                 );
585                 lines = fcpServer.collectUntil(is("EndMessage"));
586                 assertThat(lines, matchesFcpMessage(
587                         "TestDDARequest",
588                         "Directory=" + tempFile.getParent(),
589                         "WantReadDirectory=true",
590                         "WantWriteDirectory=false",
591                         "EndMessage"
592                 ));
593                 fcpServer.writeLine(
594                         "TestDDAReply",
595                         "Directory=/some-other-directory",
596                         "ReadFilename=" + tempFile,
597                         "EndMessage"
598                 );
599                 fcpServer.writeLine(
600                         "TestDDAReply",
601                         "Directory=" + tempFile.getParent(),
602                         "ReadFilename=" + tempFile,
603                         "EndMessage"
604                 );
605                 lines = fcpServer.collectUntil(is("EndMessage"));
606                 assertThat(lines, matchesFcpMessage(
607                         "TestDDAResponse",
608                         "Directory=" + tempFile.getParent(),
609                         "ReadContent=test-content",
610                         "EndMessage"
611                 ));
612         }
613
614         @Test
615         public void clientPutSendsResponseEvenIfFileCanNotBeRead()
616         throws IOException, ExecutionException, InterruptedException {
617                 File tempFile = createTempFile();
618                 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
619                 connectNode();
620                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
621                 String identifier = extractIdentifier(lines);
622                 fcpServer.writeLine(
623                         "ProtocolError",
624                         "Identifier=" + identifier,
625                         "Code=25",
626                         "EndMessage"
627                 );
628                 lines = fcpServer.collectUntil(is("EndMessage"));
629                 assertThat(lines, matchesFcpMessage(
630                         "TestDDARequest",
631                         "Directory=" + tempFile.getParent(),
632                         "WantReadDirectory=true",
633                         "WantWriteDirectory=false",
634                         "EndMessage"
635                 ));
636                 fcpServer.writeLine(
637                         "TestDDAReply",
638                         "Directory=" + tempFile.getParent(),
639                         "ReadFilename=" + tempFile + ".foo",
640                         "EndMessage"
641                 );
642                 lines = fcpServer.collectUntil(is("EndMessage"));
643                 assertThat(lines, matchesFcpMessage(
644                         "TestDDAResponse",
645                         "Directory=" + tempFile.getParent(),
646                         "ReadContent=failed-to-read",
647                         "EndMessage"
648                 ));
649         }
650
651         @Test
652         public void clientPutDoesNotResendOriginalClientPutOnTestDDACompleteWithWrongDirectory()
653         throws IOException, ExecutionException, InterruptedException {
654                 File tempFile = createTempFile();
655                 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
656                 connectNode();
657                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
658                 String identifier = extractIdentifier(lines);
659                 fcpServer.writeLine(
660                         "TestDDAComplete",
661                         "Directory=/some-other-directory",
662                         "EndMessage"
663                 );
664                 fcpServer.writeLine(
665                         "ProtocolError",
666                         "Identifier=" + identifier,
667                         "Code=25",
668                         "EndMessage"
669                 );
670                 lines = fcpServer.collectUntil(is("EndMessage"));
671                 assertThat(lines, matchesFcpMessage(
672                         "TestDDARequest",
673                         "Directory=" + tempFile.getParent(),
674                         "WantReadDirectory=true",
675                         "WantWriteDirectory=false",
676                         "EndMessage"
677                 ));
678         }
679
680         @Test
681         public void clientPutSendsNotificationsForGeneratedKeys()
682         throws InterruptedException, ExecutionException, IOException {
683                 List<String> generatedKeys = new CopyOnWriteArrayList<>();
684                 Future<Optional<Key>> key = fcpClient.clientPut()
685                         .onKeyGenerated(generatedKeys::add)
686                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
687                         .length(6)
688                         .uri("KSK@foo.txt")
689                         .execute();
690                 connectNode();
691                 List<String> lines = fcpServer.collectUntil(is("Hello"));
692                 String identifier = extractIdentifier(lines);
693                 fcpServer.writeLine(
694                         "URIGenerated",
695                         "Identifier=" + identifier,
696                         "URI=KSK@foo.txt",
697                         "EndMessage"
698                 );
699                 fcpServer.writeLine(
700                         "PutSuccessful",
701                         "URI=KSK@foo.txt",
702                         "Identifier=" + identifier,
703                         "EndMessage"
704                 );
705                 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
706                 assertThat(generatedKeys, contains("KSK@foo.txt"));
707         }
708
709         @Test
710         public void clientCanListPeers() throws IOException, ExecutionException, InterruptedException {
711                 Future<Collection<Peer>> peers = fcpClient.listPeers().execute();
712                 connectNode();
713                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
714                 assertThat(lines, matchesFcpMessage(
715                         "ListPeers",
716                         "WithVolatile=false",
717                         "WithMetadata=false",
718                         "EndMessage"
719                 ));
720                 String identifier = extractIdentifier(lines);
721                 fcpServer.writeLine(
722                         "Peer",
723                         "Identifier=" + identifier,
724                         "identity=id1",
725                         "EndMessage"
726                 );
727                 fcpServer.writeLine(
728                         "Peer",
729                         "Identifier=" + identifier,
730                         "identity=id2",
731                         "EndMessage"
732                 );
733                 fcpServer.writeLine(
734                         "EndListPeers",
735                         "Identifier=" + identifier,
736                         "EndMessage"
737                 );
738                 assertThat(peers.get(), hasSize(2));
739                 assertThat(peers.get().stream().map(Peer::getIdentity).collect(Collectors.toList()),
740                         containsInAnyOrder("id1", "id2"));
741         }
742
743         @Test
744         public void clientCanListPeersWithMetadata() throws IOException, ExecutionException, InterruptedException {
745                 Future<Collection<Peer>> peers = fcpClient.listPeers().includeMetadata().execute();
746                 connectNode();
747                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
748                 assertThat(lines, matchesFcpMessage(
749                         "ListPeers",
750                         "WithVolatile=false",
751                         "WithMetadata=true",
752                         "EndMessage"
753                 ));
754                 String identifier = extractIdentifier(lines);
755                 fcpServer.writeLine(
756                         "Peer",
757                         "Identifier=" + identifier,
758                         "identity=id1",
759                         "metadata.foo=bar1",
760                         "EndMessage"
761                 );
762                 fcpServer.writeLine(
763                         "Peer",
764                         "Identifier=" + identifier,
765                         "identity=id2",
766                         "metadata.foo=bar2",
767                         "EndMessage"
768                 );
769                 fcpServer.writeLine(
770                         "EndListPeers",
771                         "Identifier=" + identifier,
772                         "EndMessage"
773                 );
774                 assertThat(peers.get(), hasSize(2));
775                 assertThat(peers.get().stream().map(peer -> peer.getMetadata("foo")).collect(Collectors.toList()),
776                         containsInAnyOrder("bar1", "bar2"));
777         }
778
779         @Test
780         public void clientCanListPeersWithVolatiles() throws IOException, ExecutionException, InterruptedException {
781                 Future<Collection<Peer>> peers = fcpClient.listPeers().includeVolatile().execute();
782                 connectNode();
783                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
784                 assertThat(lines, matchesFcpMessage(
785                         "ListPeers",
786                         "WithVolatile=true",
787                         "WithMetadata=false",
788                         "EndMessage"
789                 ));
790                 String identifier = extractIdentifier(lines);
791                 fcpServer.writeLine(
792                         "Peer",
793                         "Identifier=" + identifier,
794                         "identity=id1",
795                         "volatile.foo=bar1",
796                         "EndMessage"
797                 );
798                 fcpServer.writeLine(
799                         "Peer",
800                         "Identifier=" + identifier,
801                         "identity=id2",
802                         "volatile.foo=bar2",
803                         "EndMessage"
804                 );
805                 fcpServer.writeLine(
806                         "EndListPeers",
807                         "Identifier=" + identifier,
808                         "EndMessage"
809                 );
810                 assertThat(peers.get(), hasSize(2));
811                 assertThat(peers.get().stream().map(peer -> peer.getVolatile("foo")).collect(Collectors.toList()),
812                         containsInAnyOrder("bar1", "bar2"));
813         }
814
815         @Test
816         public void defaultFcpClientCanGetNodeInformation() throws InterruptedException, ExecutionException, IOException {
817                 Future<NodeData> nodeData = fcpClient.getNode().execute();
818                 connectNode();
819                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
820                 String identifier = extractIdentifier(lines);
821                 assertThat(lines, matchesFcpMessage(
822                         "GetNode",
823                         "Identifier=" + identifier,
824                         "GiveOpennetRef=false",
825                         "WithPrivate=false",
826                         "WithVolatile=false",
827                         "EndMessage"
828                 ));
829                 fcpServer.writeLine(
830                         "NodeData",
831                         "Identifier=" + identifier,
832                         "ark.pubURI=SSK@3YEf.../ark",
833                         "ark.number=78",
834                         "auth.negTypes=2",
835                         "version=Fred,0.7,1.0,1466",
836                         "lastGoodVersion=Fred,0.7,1.0,1466",
837                         "EndMessage"
838                 );
839                 assertThat(nodeData.get(), notNullValue());
840         }
841
842         @Test
843         public void defaultFcpClientCanGetNodeInformationWithOpennetRef()
844         throws InterruptedException, ExecutionException, IOException {
845                 Future<NodeData> nodeData = fcpClient.getNode().opennetRef().execute();
846                 connectNode();
847                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
848                 String identifier = extractIdentifier(lines);
849                 assertThat(lines, matchesFcpMessage(
850                         "GetNode",
851                         "Identifier=" + identifier,
852                         "GiveOpennetRef=true",
853                         "WithPrivate=false",
854                         "WithVolatile=false",
855                         "EndMessage"
856                 ));
857                 fcpServer.writeLine(
858                         "NodeData",
859                         "Identifier=" + identifier,
860                         "opennet=true",
861                         "ark.pubURI=SSK@3YEf.../ark",
862                         "ark.number=78",
863                         "auth.negTypes=2",
864                         "version=Fred,0.7,1.0,1466",
865                         "lastGoodVersion=Fred,0.7,1.0,1466",
866                         "EndMessage"
867                 );
868                 assertThat(nodeData.get().getVersion().toString(), is("Fred,0.7,1.0,1466"));
869         }
870
871         @Test
872         public void defaultFcpClientCanGetNodeInformationWithPrivateData()
873         throws InterruptedException, ExecutionException, IOException {
874                 Future<NodeData> nodeData = fcpClient.getNode().includePrivate().execute();
875                 connectNode();
876                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
877                 String identifier = extractIdentifier(lines);
878                 assertThat(lines, matchesFcpMessage(
879                         "GetNode",
880                         "Identifier=" + identifier,
881                         "GiveOpennetRef=false",
882                         "WithPrivate=true",
883                         "WithVolatile=false",
884                         "EndMessage"
885                 ));
886                 fcpServer.writeLine(
887                         "NodeData",
888                         "Identifier=" + identifier,
889                         "opennet=false",
890                         "ark.pubURI=SSK@3YEf.../ark",
891                         "ark.number=78",
892                         "auth.negTypes=2",
893                         "version=Fred,0.7,1.0,1466",
894                         "lastGoodVersion=Fred,0.7,1.0,1466",
895                         "ark.privURI=SSK@XdHMiRl",
896                         "EndMessage"
897                 );
898                 assertThat(nodeData.get().getARK().getPrivateURI(), is("SSK@XdHMiRl"));
899         }
900
901         @Test
902         public void defaultFcpClientCanGetNodeInformationWithVolatileData()
903         throws InterruptedException, ExecutionException, IOException {
904                 Future<NodeData> nodeData = fcpClient.getNode().includeVolatile().execute();
905                 connectNode();
906                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
907                 String identifier = extractIdentifier(lines);
908                 assertThat(lines, matchesFcpMessage(
909                         "GetNode",
910                         "Identifier=" + identifier,
911                         "GiveOpennetRef=false",
912                         "WithPrivate=false",
913                         "WithVolatile=true",
914                         "EndMessage"
915                 ));
916                 fcpServer.writeLine(
917                         "NodeData",
918                         "Identifier=" + identifier,
919                         "opennet=false",
920                         "ark.pubURI=SSK@3YEf.../ark",
921                         "ark.number=78",
922                         "auth.negTypes=2",
923                         "version=Fred,0.7,1.0,1466",
924                         "lastGoodVersion=Fred,0.7,1.0,1466",
925                         "volatile.freeJavaMemory=205706528",
926                         "EndMessage"
927                 );
928                 assertThat(nodeData.get().getVolatile("freeJavaMemory"), is("205706528"));
929         }
930
931         @Test
932         public void defaultFcpClientCanListSinglePeerByIdentity()
933         throws InterruptedException, ExecutionException, IOException {
934                 Future<Optional<Peer>> peer = fcpClient.listPeer().byIdentity("id1").execute();
935                 connectNode();
936                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
937                 String identifier = extractIdentifier(lines);
938                 assertThat(lines, matchesFcpMessage(
939                         "ListPeer",
940                         "Identifier=" + identifier,
941                         "NodeIdentifier=id1",
942                         "EndMessage"
943                 ));
944                 fcpServer.writeLine(
945                         "Peer",
946                         "Identifier=" + identifier,
947                         "identity=id1",
948                         "opennet=false",
949                         "ark.pubURI=SSK@3YEf.../ark",
950                         "ark.number=78",
951                         "auth.negTypes=2",
952                         "version=Fred,0.7,1.0,1466",
953                         "lastGoodVersion=Fred,0.7,1.0,1466",
954                         "EndMessage"
955                 );
956                 assertThat(peer.get().get().getIdentity(), is("id1"));
957         }
958
959         @Test
960         public void defaultFcpClientCanListSinglePeerByHostAndPort()
961         throws InterruptedException, ExecutionException, IOException {
962                 Future<Optional<Peer>> peer = fcpClient.listPeer().byHostAndPort("host.free.net", 12345).execute();
963                 connectNode();
964                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
965                 String identifier = extractIdentifier(lines);
966                 assertThat(lines, matchesFcpMessage(
967                         "ListPeer",
968                         "Identifier=" + identifier,
969                         "NodeIdentifier=host.free.net:12345",
970                         "EndMessage"
971                 ));
972                 fcpServer.writeLine(
973                         "Peer",
974                         "Identifier=" + identifier,
975                         "identity=id1",
976                         "opennet=false",
977                         "ark.pubURI=SSK@3YEf.../ark",
978                         "ark.number=78",
979                         "auth.negTypes=2",
980                         "version=Fred,0.7,1.0,1466",
981                         "lastGoodVersion=Fred,0.7,1.0,1466",
982                         "EndMessage"
983                 );
984                 assertThat(peer.get().get().getIdentity(), is("id1"));
985         }
986
987         @Test
988         public void defaultFcpClientCanListSinglePeerByName()
989         throws InterruptedException, ExecutionException, IOException {
990                 Future<Optional<Peer>> peer = fcpClient.listPeer().byName("FriendNode").execute();
991                 connectNode();
992                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
993                 String identifier = extractIdentifier(lines);
994                 assertThat(lines, matchesFcpMessage(
995                         "ListPeer",
996                         "Identifier=" + identifier,
997                         "NodeIdentifier=FriendNode",
998                         "EndMessage"
999                 ));
1000                 fcpServer.writeLine(
1001                         "Peer",
1002                         "Identifier=" + identifier,
1003                         "identity=id1",
1004                         "opennet=false",
1005                         "ark.pubURI=SSK@3YEf.../ark",
1006                         "ark.number=78",
1007                         "auth.negTypes=2",
1008                         "version=Fred,0.7,1.0,1466",
1009                         "lastGoodVersion=Fred,0.7,1.0,1466",
1010                         "EndMessage"
1011                 );
1012                 assertThat(peer.get().get().getIdentity(), is("id1"));
1013         }
1014
1015         @Test
1016         public void defaultFcpClientRecognizesUnknownNodeIdentifiers()
1017         throws InterruptedException, ExecutionException, IOException {
1018                 Future<Optional<Peer>> peer = fcpClient.listPeer().byIdentity("id2").execute();
1019                 connectNode();
1020                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1021                 String identifier = extractIdentifier(lines);
1022                 assertThat(lines, matchesFcpMessage(
1023                         "ListPeer",
1024                         "Identifier=" + identifier,
1025                         "NodeIdentifier=id2",
1026                         "EndMessage"
1027                 ));
1028                 fcpServer.writeLine(
1029                         "UnknownNodeIdentifier",
1030                         "Identifier=" + identifier,
1031                         "NodeIdentifier=id2",
1032                         "EndMessage"
1033                 );
1034                 assertThat(peer.get().isPresent(), is(false));
1035         }
1036
1037         @Test
1038         public void defaultFcpClientCanAddPeerFromFile() throws InterruptedException, ExecutionException, IOException {
1039                 Future<Optional<Peer>> peer = fcpClient.addPeer().fromFile(new File("/tmp/ref.txt")).execute();
1040                 connectNode();
1041                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1042                 String identifier = extractIdentifier(lines);
1043                 assertThat(lines, matchesFcpMessage(
1044                         "AddPeer",
1045                         "Identifier=" + identifier,
1046                         "File=/tmp/ref.txt",
1047                         "EndMessage"
1048                 ));
1049                 fcpServer.writeLine(
1050                         "Peer",
1051                         "Identifier=" + identifier,
1052                         "identity=id1",
1053                         "opennet=false",
1054                         "ark.pubURI=SSK@3YEf.../ark",
1055                         "ark.number=78",
1056                         "auth.negTypes=2",
1057                         "version=Fred,0.7,1.0,1466",
1058                         "lastGoodVersion=Fred,0.7,1.0,1466",
1059                         "EndMessage"
1060                 );
1061                 assertThat(peer.get().get().getIdentity(), is("id1"));
1062         }
1063
1064         @Test
1065         public void defaultFcpClientCanAddPeerFromURL() throws InterruptedException, ExecutionException, IOException {
1066                 Future<Optional<Peer>> peer = fcpClient.addPeer().fromURL(new URL("http://node.ref/")).execute();
1067                 connectNode();
1068                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1069                 String identifier = extractIdentifier(lines);
1070                 assertThat(lines, matchesFcpMessage(
1071                         "AddPeer",
1072                         "Identifier=" + identifier,
1073                         "URL=http://node.ref/",
1074                         "EndMessage"
1075                 ));
1076                 fcpServer.writeLine(
1077                         "Peer",
1078                         "Identifier=" + identifier,
1079                         "identity=id1",
1080                         "opennet=false",
1081                         "ark.pubURI=SSK@3YEf.../ark",
1082                         "ark.number=78",
1083                         "auth.negTypes=2",
1084                         "version=Fred,0.7,1.0,1466",
1085                         "lastGoodVersion=Fred,0.7,1.0,1466",
1086                         "EndMessage"
1087                 );
1088                 assertThat(peer.get().get().getIdentity(), is("id1"));
1089         }
1090
1091         @Test
1092         public void defaultFcpClientCanAddPeerFromNodeRef() throws InterruptedException, ExecutionException, IOException {
1093                 NodeRef nodeRef = new NodeRef();
1094                 nodeRef.setIdentity("id1");
1095                 nodeRef.setName("name");
1096                 nodeRef.setARK(new ARK("public", "1"));
1097                 nodeRef.setDSAGroup(new DSAGroup("base", "prime", "subprime"));
1098                 nodeRef.setNegotiationTypes(new int[] { 3, 5 });
1099                 nodeRef.setPhysicalUDP("1.2.3.4:5678");
1100                 nodeRef.setDSAPublicKey("dsa-public");
1101                 nodeRef.setSignature("sig");
1102                 Future<Optional<Peer>> peer = fcpClient.addPeer().fromNodeRef(nodeRef).execute();
1103                 connectNode();
1104                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1105                 String identifier = extractIdentifier(lines);
1106                 assertThat(lines, matchesFcpMessage(
1107                         "AddPeer",
1108                         "Identifier=" + identifier,
1109                         "identity=id1",
1110                         "myName=name",
1111                         "ark.pubURI=public",
1112                         "ark.number=1",
1113                         "dsaGroup.g=base",
1114                         "dsaGroup.p=prime",
1115                         "dsaGroup.q=subprime",
1116                         "dsaPubKey.y=dsa-public",
1117                         "physical.udp=1.2.3.4:5678",
1118                         "auth.negTypes=3;5",
1119                         "sig=sig",
1120                         "EndMessage"
1121                 ));
1122                 fcpServer.writeLine(
1123                         "Peer",
1124                         "Identifier=" + identifier,
1125                         "identity=id1",
1126                         "opennet=false",
1127                         "ark.pubURI=SSK@3YEf.../ark",
1128                         "ark.number=78",
1129                         "auth.negTypes=2",
1130                         "version=Fred,0.7,1.0,1466",
1131                         "lastGoodVersion=Fred,0.7,1.0,1466",
1132                         "EndMessage"
1133                 );
1134                 assertThat(peer.get().get().getIdentity(), is("id1"));
1135         }
1136
1137         @Test
1138         public void listPeerNotesCanGetPeerNotesByNodeName() throws InterruptedException, ExecutionException, IOException {
1139                 Future<Optional<PeerNote>> peerNote = fcpClient.listPeerNotes().byName("Friend1").execute();
1140                 connectNode();
1141                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1142                 String identifier = extractIdentifier(lines);
1143                 assertThat(lines, matchesFcpMessage(
1144                         "ListPeerNotes",
1145                         "NodeIdentifier=Friend1",
1146                         "EndMessage"
1147                 ));
1148                 fcpServer.writeLine(
1149                         "PeerNote",
1150                         "Identifier=" + identifier,
1151                         "NodeIdentifier=Friend1",
1152                         "NoteText=RXhhbXBsZSBUZXh0Lg==",
1153                         "PeerNoteType=1",
1154                         "EndMessage"
1155                 );
1156                 fcpServer.writeLine(
1157                         "EndListPeerNotes",
1158                         "Identifier=" + identifier,
1159                         "EndMessage"
1160                 );
1161                 assertThat(peerNote.get().get().getNoteText(), is("RXhhbXBsZSBUZXh0Lg=="));
1162                 assertThat(peerNote.get().get().getPeerNoteType(), is(1));
1163         }
1164
1165         @Test
1166         public void listPeerNotesReturnsEmptyOptionalWhenNodeIdenfierUnknown()
1167         throws InterruptedException, ExecutionException,
1168         IOException {
1169                 Future<Optional<PeerNote>> peerNote = fcpClient.listPeerNotes().byName("Friend1").execute();
1170                 connectNode();
1171                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1172                 String identifier = extractIdentifier(lines);
1173                 assertThat(lines, matchesFcpMessage(
1174                         "ListPeerNotes",
1175                         "NodeIdentifier=Friend1",
1176                         "EndMessage"
1177                 ));
1178                 fcpServer.writeLine(
1179                         "UnknownNodeIdentifier",
1180                         "Identifier=" + identifier,
1181                         "NodeIdentifier=Friend1",
1182                         "EndMessage"
1183                 );
1184                 assertThat(peerNote.get().isPresent(), is(false));
1185         }
1186
1187         @Test
1188         public void listPeerNotesCanGetPeerNotesByNodeIdentifier()
1189         throws InterruptedException, ExecutionException, IOException {
1190                 Future<Optional<PeerNote>> peerNote = fcpClient.listPeerNotes().byIdentity("id1").execute();
1191                 connectNode();
1192                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1193                 String identifier = extractIdentifier(lines);
1194                 assertThat(lines, matchesFcpMessage(
1195                         "ListPeerNotes",
1196                         "NodeIdentifier=id1",
1197                         "EndMessage"
1198                 ));
1199                 fcpServer.writeLine(
1200                         "PeerNote",
1201                         "Identifier=" + identifier,
1202                         "NodeIdentifier=id1",
1203                         "NoteText=RXhhbXBsZSBUZXh0Lg==",
1204                         "PeerNoteType=1",
1205                         "EndMessage"
1206                 );
1207                 fcpServer.writeLine(
1208                         "EndListPeerNotes",
1209                         "Identifier=" + identifier,
1210                         "EndMessage"
1211                 );
1212                 assertThat(peerNote.get().get().getNoteText(), is("RXhhbXBsZSBUZXh0Lg=="));
1213                 assertThat(peerNote.get().get().getPeerNoteType(), is(1));
1214         }
1215
1216         @Test
1217         public void listPeerNotesCanGetPeerNotesByHostNameAndPortNumber()
1218         throws InterruptedException, ExecutionException, IOException {
1219                 Future<Optional<PeerNote>> peerNote = fcpClient.listPeerNotes().byHostAndPort("1.2.3.4", 5678).execute();
1220                 connectNode();
1221                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1222                 String identifier = extractIdentifier(lines);
1223                 assertThat(lines, matchesFcpMessage(
1224                         "ListPeerNotes",
1225                         "NodeIdentifier=1.2.3.4:5678",
1226                         "EndMessage"
1227                 ));
1228                 fcpServer.writeLine(
1229                         "PeerNote",
1230                         "Identifier=" + identifier,
1231                         "NodeIdentifier=id1",
1232                         "NoteText=RXhhbXBsZSBUZXh0Lg==",
1233                         "PeerNoteType=1",
1234                         "EndMessage"
1235                 );
1236                 fcpServer.writeLine(
1237                         "EndListPeerNotes",
1238                         "Identifier=" + identifier,
1239                         "EndMessage"
1240                 );
1241                 assertThat(peerNote.get().get().getNoteText(), is("RXhhbXBsZSBUZXh0Lg=="));
1242                 assertThat(peerNote.get().get().getPeerNoteType(), is(1));
1243         }
1244
1245         @Test
1246         public void defaultFcpClientCanEnablePeerByName() throws InterruptedException, ExecutionException, IOException {
1247                 Future<Optional<Peer>> peer = fcpClient.modifyPeer().enable().byName("Friend1").execute();
1248                 connectNode();
1249                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1250                 String identifier = extractIdentifier(lines);
1251                 assertThat(lines, matchesFcpMessage(
1252                         "ModifyPeer",
1253                         "Identifier=" + identifier,
1254                         "NodeIdentifier=Friend1",
1255                         "IsDisabled=false",
1256                         "EndMessage"
1257                 ));
1258                 fcpServer.writeLine(
1259                         "Peer",
1260                         "Identifier=" + identifier,
1261                         "NodeIdentifier=Friend1",
1262                         "identity=id1",
1263                         "EndMessage"
1264                 );
1265                 assertThat(peer.get().get().getIdentity(), is("id1"));
1266         }
1267
1268         @Test
1269         public void defaultFcpClientCanDisablePeerByName() throws InterruptedException, ExecutionException, IOException {
1270                 Future<Optional<Peer>> peer = fcpClient.modifyPeer().disable().byName("Friend1").execute();
1271                 connectNode();
1272                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1273                 String identifier = extractIdentifier(lines);
1274                 assertThat(lines, matchesFcpMessage(
1275                         "ModifyPeer",
1276                         "Identifier=" + identifier,
1277                         "NodeIdentifier=Friend1",
1278                         "IsDisabled=true",
1279                         "EndMessage"
1280                 ));
1281                 fcpServer.writeLine(
1282                         "Peer",
1283                         "Identifier=" + identifier,
1284                         "NodeIdentifier=Friend1",
1285                         "identity=id1",
1286                         "EndMessage"
1287                 );
1288                 assertThat(peer.get().get().getIdentity(), is("id1"));
1289         }
1290
1291         @Test
1292         public void defaultFcpClientCanEnablePeerByIdentity() throws InterruptedException, ExecutionException, IOException {
1293                 Future<Optional<Peer>> peer = fcpClient.modifyPeer().enable().byIdentity("id1").execute();
1294                 connectNode();
1295                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
1296                 String identifier = extractIdentifier(lines);
1297                 assertThat(lines, matchesFcpMessage(
1298                         "ModifyPeer",
1299                         "Identifier=" + identifier,
1300                         "NodeIdentifier=id1",
1301                         "IsDisabled=false",
1302                         "EndMessage"
1303                 ));
1304                 fcpServer.writeLine(
1305                         "Peer",
1306                         "Identifier=" + identifier,
1307                         "NodeIdentifier=Friend1",
1308                         "identity=id1",
1309                         "EndMessage"
1310                 );
1311                 assertThat(peer.get().get().getIdentity(), is("id1"));
1312         }
1313
1314 }