1 package net.pterodactylus.fcp.quelaton;
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.is;
6 import java.io.ByteArrayInputStream;
8 import java.io.IOException;
9 import java.nio.charset.StandardCharsets;
10 import java.util.List;
11 import java.util.Optional;
12 import java.util.concurrent.ExecutionException;
13 import java.util.concurrent.ExecutorService;
14 import java.util.concurrent.Executors;
15 import java.util.concurrent.Future;
17 import net.pterodactylus.fcp.FcpKeyPair;
18 import net.pterodactylus.fcp.Key;
19 import net.pterodactylus.fcp.Priority;
20 import net.pterodactylus.fcp.fake.FakeTcpServer;
21 import net.pterodactylus.fcp.quelaton.ClientGetCommand.Data;
23 import com.google.common.io.ByteStreams;
24 import com.google.common.io.Files;
25 import org.hamcrest.Description;
26 import org.hamcrest.Matcher;
27 import org.hamcrest.TypeSafeDiagnosingMatcher;
28 import org.junit.After;
29 import org.junit.Test;
32 * Unit test for {@link DefaultFcpClient}.
34 * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
36 public class DefaultFcpClientTest {
38 private static final String INSERT_URI =
39 "SSK@RVCHbJdkkyTCeNN9AYukEg76eyqmiosSaNKgE3U9zUw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQECAAE/";
40 private static final String REQUEST_URI =
41 "SSK@wtbgd2loNcJCXvtQVOftl2tuWBomDQHfqS6ytpPRhfw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQACAAE/";
43 private static int threadCounter = 0;
44 private final ExecutorService threadPool =
45 Executors.newCachedThreadPool(r -> new Thread(r, "Test-Thread-" + threadCounter++));
46 private final FakeTcpServer fcpServer;
47 private final DefaultFcpClient fcpClient;
49 public DefaultFcpClientTest() throws IOException {
50 fcpServer = new FakeTcpServer(threadPool);
51 fcpClient = new DefaultFcpClient(threadPool, "localhost", fcpServer.getPort(), () -> "Test");
55 public void tearDown() throws IOException {
60 public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
61 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
63 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
64 String identifier = extractIdentifier(lines);
65 fcpServer.writeLine("SSKKeypair",
66 "InsertURI=" + INSERT_URI + "",
67 "RequestURI=" + REQUEST_URI + "",
68 "Identifier=" + identifier,
70 FcpKeyPair keyPair = keyPairFuture.get();
71 assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
72 assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
75 private void connectNode() throws InterruptedException, ExecutionException, IOException {
76 fcpServer.connect().get();
77 fcpServer.collectUntil(is("EndMessage"));
78 fcpServer.writeLine("NodeHello",
79 "CompressionCodecs=4 - GZIP(0), BZIP2(1), LZMA(2), LZMA_NEW(3)",
80 "Revision=build01466",
82 "Version=Fred,0.7,1.0,1466",
84 "ConnectionIdentifier=14318898267048452a81b36e7f13a3f0",
88 "NodeLanguage=ENGLISH",
95 public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
96 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
98 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
99 assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
100 String identifier = extractIdentifier(lines);
103 "Identifier=" + identifier,
105 "StartupTime=1435610539000",
106 "CompletionTime=1435610540000",
107 "Metadata.ContentType=text/plain;charset=utf-8",
111 Optional<Data> data = dataFuture.get();
112 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
113 assertThat(data.get().size(), is(6L));
114 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
115 is("Hello\n".getBytes(StandardCharsets.UTF_8)));
118 private String extractIdentifier(List<String> lines) {
119 return lines.stream()
120 .filter(s -> s.startsWith("Identifier="))
121 .map(s -> s.substring(s.indexOf('=') + 1))
127 public void clientGetDownloadsDataForCorrectIdentifier()
128 throws InterruptedException, ExecutionException, IOException {
129 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
131 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
132 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
133 String identifier = extractIdentifier(lines);
136 "Identifier=not-test",
138 "StartupTime=1435610539000",
139 "CompletionTime=1435610540000",
140 "Metadata.ContentType=text/plain;charset=latin-9",
146 "Identifier=" + identifier,
148 "StartupTime=1435610539000",
149 "CompletionTime=1435610540000",
150 "Metadata.ContentType=text/plain;charset=utf-8",
154 Optional<Data> data = dataFuture.get();
155 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
156 assertThat(data.get().size(), is(6L));
157 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
158 is("Hello\n".getBytes(StandardCharsets.UTF_8)));
162 public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
163 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
165 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
166 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
167 String identifier = extractIdentifier(lines);
170 "Identifier=" + identifier,
174 Optional<Data> data = dataFuture.get();
175 assertThat(data.isPresent(), is(false));
179 public void clientGetRecognizesGetFailedForCorrectIdentifier()
180 throws InterruptedException, ExecutionException, IOException {
181 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
183 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
184 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
185 String identifier = extractIdentifier(lines);
188 "Identifier=not-test",
194 "Identifier=" + identifier,
198 Optional<Data> data = dataFuture.get();
199 assertThat(data.isPresent(), is(false));
203 public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
204 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
206 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
207 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
209 Optional<Data> data = dataFuture.get();
210 assertThat(data.isPresent(), is(false));
214 public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands()
215 throws InterruptedException, ExecutionException, IOException {
216 fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt");
218 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
219 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
223 public void clientGetWithDataStoreOnlySettingSendsCorrectCommands()
224 throws InterruptedException, ExecutionException, IOException {
225 fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt");
227 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
228 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
232 public void clientGetWithMaxSizeSettingSendsCorrectCommands()
233 throws InterruptedException, ExecutionException, IOException {
234 fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt");
236 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
237 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
241 public void clientGetWithPrioritySettingSendsCorrectCommands()
242 throws InterruptedException, ExecutionException, IOException {
243 fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt");
245 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
246 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
250 public void clientGetWithRealTimeSettingSendsCorrectCommands()
251 throws InterruptedException, ExecutionException, IOException {
252 fcpClient.clientGet().realTime().uri("KSK@foo.txt");
254 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
255 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
259 public void clientGetWithGlobalSettingSendsCorrectCommands()
260 throws InterruptedException, ExecutionException, IOException {
261 fcpClient.clientGet().global().uri("KSK@foo.txt");
263 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
264 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
267 private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
268 return new TypeSafeDiagnosingMatcher<List<String>>() {
270 protected boolean matchesSafely(List<String> item, Description mismatchDescription) {
271 if (!item.get(0).equals(name)) {
272 mismatchDescription.appendText("FCP message is named ").appendValue(item.get(0));
275 for (String requiredLine : requiredLines) {
276 if (item.indexOf(requiredLine) < 1) {
277 mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
285 public void describeTo(Description description) {
286 description.appendText("FCP message named ").appendValue(name);
287 description.appendValueList(", containing the lines ", ", ", "", requiredLines);
293 public void clientPutWithDirectDataSendsCorrectCommand()
294 throws IOException, ExecutionException, InterruptedException {
295 fcpClient.clientPut()
296 .from(new ByteArrayInputStream("Hello\n".getBytes()))
300 List<String> lines = fcpServer.collectUntil(is("Hello"));
301 assertThat(lines, matchesFcpMessage("ClientPut", "UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
305 public void clientPutWithDirectDataSucceedsOnCorrectIdentifier()
306 throws InterruptedException, ExecutionException, IOException {
307 Future<Optional<Key>> key = fcpClient.clientPut()
308 .from(new ByteArrayInputStream("Hello\n".getBytes()))
312 List<String> lines = fcpServer.collectUntil(is("Hello"));
313 String identifier = extractIdentifier(lines);
316 "Identifier=not-the-right-one",
322 "Identifier=" + identifier,
325 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
329 public void clientPutWithDirectDataFailsOnCorrectIdentifier()
330 throws InterruptedException, ExecutionException, IOException {
331 Future<Optional<Key>> key = fcpClient.clientPut()
332 .from(new ByteArrayInputStream("Hello\n".getBytes()))
336 List<String> lines = fcpServer.collectUntil(is("Hello"));
337 String identifier = extractIdentifier(lines);
340 "Identifier=not-the-right-one",
346 "Identifier=" + identifier,
349 assertThat(key.get().isPresent(), is(false));
353 public void clientPutWithRenamedDirectDataSendsCorrectCommand()
354 throws InterruptedException, ExecutionException, IOException {
355 fcpClient.clientPut()
356 .named("otherName.txt")
357 .from(new ByteArrayInputStream("Hello\n".getBytes()))
361 List<String> lines = fcpServer.collectUntil(is("Hello"));
362 assertThat(lines, matchesFcpMessage("ClientPut", "TargetFilename=otherName.txt", "UploadFrom=direct",
363 "DataLength=6", "URI=KSK@foo.txt"));
367 public void clientPutWithRedirectSendsCorrectCommand()
368 throws IOException, ExecutionException, InterruptedException {
369 fcpClient.clientPut().redirectTo("KSK@bar.txt").uri("KSK@foo.txt");
371 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
373 matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
377 public void clientPutWithFileSendsCorrectCommand() throws InterruptedException, ExecutionException, IOException {
378 fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt");
380 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
382 matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));
386 public void clientPutWithFileCanCompleteTestDdaSequence()
387 throws IOException, ExecutionException, InterruptedException {
388 File tempFile = createTempFile();
389 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt");
391 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
392 String identifier = extractIdentifier(lines);
395 "Identifier=" + identifier,
399 lines = fcpServer.collectUntil(is("EndMessage"));
400 assertThat(lines, matchesFcpMessage(
402 "Directory=" + tempFile.getParent(),
403 "WantReadDirectory=true",
404 "WantWriteDirectory=false",
409 "Directory=" + tempFile.getParent(),
410 "ReadFilename=" + tempFile,
413 lines = fcpServer.collectUntil(is("EndMessage"));
414 assertThat(lines, matchesFcpMessage(
416 "Directory=" + tempFile.getParent(),
417 "ReadContent=test-content",
422 "Directory=" + tempFile.getParent(),
423 "ReadDirectoryAllowed=true",
426 lines = fcpServer.collectUntil(is("EndMessage"));
428 matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt",
429 "Filename=" + new File(tempFile.getParent(), "test.dat")));
432 private File createTempFile() throws IOException {
433 File tempFile = File.createTempFile("test-dda-", ".dat");
434 tempFile.deleteOnExit();
435 Files.write("test-content", tempFile, StandardCharsets.UTF_8);
440 public void clientPutDoesNotReactToProtocolErrorForDifferentIdentifier()
441 throws InterruptedException, ExecutionException, IOException {
442 Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt");
444 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
445 String identifier = extractIdentifier(lines);
448 "Identifier=not-the-right-one",
454 "Identifier=" + identifier,
458 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
462 public void clientPutAbortsOnProtocolErrorOtherThan25()
463 throws InterruptedException, ExecutionException, IOException {
464 Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt");
466 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
467 String identifier = extractIdentifier(lines);
470 "Identifier=" + identifier,
474 assertThat(key.get().isPresent(), is(false));
478 public void clientPutDoesNotReplyToWrongTestDdaReply() throws IOException, ExecutionException,
479 InterruptedException {
480 File tempFile = createTempFile();
481 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt");
483 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
484 String identifier = extractIdentifier(lines);
487 "Identifier=" + identifier,
491 lines = fcpServer.collectUntil(is("EndMessage"));
492 assertThat(lines, matchesFcpMessage(
494 "Directory=" + tempFile.getParent(),
495 "WantReadDirectory=true",
496 "WantWriteDirectory=false",
501 "Directory=/some-other-directory",
502 "ReadFilename=" + tempFile,
507 "Directory=" + tempFile.getParent(),
508 "ReadFilename=" + tempFile,
511 lines = fcpServer.collectUntil(is("EndMessage"));
512 assertThat(lines, matchesFcpMessage(
514 "Directory=" + tempFile.getParent(),
515 "ReadContent=test-content",
521 public void clientPutSendsResponseEvenIfFileCanNotBeRead()
522 throws IOException, ExecutionException, InterruptedException {
523 File tempFile = createTempFile();
524 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt");
526 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
527 String identifier = extractIdentifier(lines);
530 "Identifier=" + identifier,
534 lines = fcpServer.collectUntil(is("EndMessage"));
535 assertThat(lines, matchesFcpMessage(
537 "Directory=" + tempFile.getParent(),
538 "WantReadDirectory=true",
539 "WantWriteDirectory=false",
544 "Directory=" + tempFile.getParent(),
545 "ReadFilename=" + tempFile + ".foo",
548 lines = fcpServer.collectUntil(is("EndMessage"));
549 assertThat(lines, matchesFcpMessage(
551 "Directory=" + tempFile.getParent(),
552 "ReadContent=failed-to-read",
558 public void clientPutDoesNotResendOriginalClientPutOnTestDDACompleteWithWrongDirectory()
559 throws IOException, ExecutionException, InterruptedException {
560 File tempFile = createTempFile();
561 fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt");
563 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
564 String identifier = extractIdentifier(lines);
567 "Directory=/some-other-directory",
572 "Identifier=" + identifier,
576 lines = fcpServer.collectUntil(is("EndMessage"));
577 assertThat(lines, matchesFcpMessage(
579 "Directory=" + tempFile.getParent(),
580 "WantReadDirectory=true",
581 "WantWriteDirectory=false",