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