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