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