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