package net.pterodactylus.fcp.quelaton;
+import static net.pterodactylus.fcp.RequestProgressMatcher.isRequestProgress;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.contains;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import net.pterodactylus.fcp.PeerNote;
import net.pterodactylus.fcp.PluginInfo;
import net.pterodactylus.fcp.Priority;
+import net.pterodactylus.fcp.RequestProgress;
import net.pterodactylus.fcp.fake.FakeTcpServer;
import net.pterodactylus.fcp.quelaton.ClientGetCommand.Data;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
/**
threadPool.shutdown();
}
- @Test(expected = ExecutionException.class)
- public void defaultFcpClientThrowsExceptionIfItCanNotConnect()
- throws IOException, ExecutionException, InterruptedException {
- Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
- fcpServer.connect().get();
- fcpServer.collectUntil(is("EndMessage"));
- fcpServer.writeLine(
- "CloseConnectionDuplicateClientName",
- "EndMessage"
- );
- keyPairFuture.get();
- }
-
- @Test(expected = ExecutionException.class)
- public void defaultFcpClientThrowsExceptionIfConnectionIsClosed()
- throws IOException, ExecutionException, InterruptedException {
- Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
- fcpServer.connect().get();
- fcpServer.collectUntil(is("EndMessage"));
- fcpServer.close();
- keyPairFuture.get();
- }
-
private void connectNode() throws InterruptedException, ExecutionException, IOException {
fcpServer.connect().get();
fcpServer.collectUntil(is("EndMessage"));
);
}
- @Test
- public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "AllData",
- "Identifier=" + identifier,
- "DataLength=6",
- "StartupTime=1435610539000",
- "CompletionTime=1435610540000",
- "Metadata.ContentType=text/plain;charset=utf-8",
- "Data",
- "Hello"
- );
- Optional<Data> data = dataFuture.get();
- assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
- assertThat(data.get().size(), is(6L));
- assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
- is("Hello\n".getBytes(StandardCharsets.UTF_8)));
- }
-
private String extractIdentifier(List<String> lines) {
return lines.stream()
.filter(s -> s.startsWith("Identifier="))
.orElse("");
}
- @Test
- public void clientGetDownloadsDataForCorrectIdentifier()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "AllData",
- "Identifier=not-test",
- "DataLength=12",
- "StartupTime=1435610539000",
- "CompletionTime=1435610540000",
- "Metadata.ContentType=text/plain;charset=latin-9",
- "Data",
- "Hello World"
- );
- fcpServer.writeLine(
- "AllData",
- "Identifier=" + identifier,
- "DataLength=6",
- "StartupTime=1435610539000",
- "CompletionTime=1435610540000",
- "Metadata.ContentType=text/plain;charset=utf-8",
- "Data",
- "Hello"
- );
- Optional<Data> data = dataFuture.get();
- assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
- assertThat(data.get().size(), is(6L));
- assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
- is("Hello\n".getBytes(StandardCharsets.UTF_8)));
- }
-
- @Test
- public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "GetFailed",
- "Identifier=" + identifier,
- "Code=3",
- "EndMessage"
- );
- Optional<Data> data = dataFuture.get();
- assertThat(data.isPresent(), is(false));
- }
-
- @Test
- public void clientGetRecognizesGetFailedForCorrectIdentifier()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "GetFailed",
- "Identifier=not-test",
- "Code=3",
- "EndMessage"
- );
- fcpServer.writeLine(
- "GetFailed",
- "Identifier=" + identifier,
- "Code=3",
- "EndMessage"
- );
- Optional<Data> data = dataFuture.get();
- assertThat(data.isPresent(), is(false));
- }
-
- @Test(expected = ExecutionException.class)
- public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
- fcpServer.close();
- dataFuture.get();
- }
-
- @Test
- public void defaultFcpClientReusesConnection() throws InterruptedException, ExecutionException, IOException {
- Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "SSKKeypair",
- "InsertURI=" + INSERT_URI + "",
- "RequestURI=" + REQUEST_URI + "",
- "Identifier=" + identifier,
- "EndMessage"
- );
- keyPair.get();
- keyPair = fcpClient.generateKeypair().execute();
- lines = fcpServer.collectUntil(is("EndMessage"));
- identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "SSKKeypair",
- "InsertURI=" + INSERT_URI + "",
- "RequestURI=" + REQUEST_URI + "",
- "Identifier=" + identifier,
- "EndMessage"
- );
- keyPair.get();
- }
-
- @Test
- public void defaultFcpClientCanReconnectAfterConnectionHasBeenClosed()
- throws InterruptedException, ExecutionException, IOException {
- Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
- connectNode();
- fcpServer.collectUntil(is("EndMessage"));
- fcpServer.close();
- try {
- keyPair.get();
- Assert.fail();
- } catch (ExecutionException e) {
- }
- keyPair = fcpClient.generateKeypair().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "SSKKeypair",
- "InsertURI=" + INSERT_URI + "",
- "RequestURI=" + REQUEST_URI + "",
- "Identifier=" + identifier,
- "EndMessage"
- );
- keyPair.get();
- }
-
- @Test
- public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
- }
-
- @Test
- public void clientGetWithDataStoreOnlySettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
- }
-
- @Test
- public void clientGetWithMaxSizeSettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
+ private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
+ return matchesFcpMessageWithTerminator(name, "EndMessage", requiredLines);
}
- @Test
- public void clientGetWithPrioritySettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
- }
+ private Matcher<Iterable<String>> hasHead(String firstElement) {
+ return new TypeSafeDiagnosingMatcher<Iterable<String>>() {
+ @Override
+ protected boolean matchesSafely(Iterable<String> iterable, Description mismatchDescription) {
+ if (!iterable.iterator().hasNext()) {
+ mismatchDescription.appendText("is empty");
+ return false;
+ }
+ String element = iterable.iterator().next();
+ if (!element.equals(firstElement)) {
+ mismatchDescription.appendText("starts with ").appendValue(element);
+ return false;
+ }
+ return true;
+ }
- @Test
- public void clientGetWithRealTimeSettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().realTime().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("starts with ").appendValue(firstElement);
+ }
+ };
}
- @Test
- public void clientGetWithGlobalSettingSendsCorrectCommands()
- throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientGet().global().uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
+ private Matcher<List<String>> matchesFcpMessageWithTerminator(
+ String name, String terminator, String... requiredLines) {
+ return allOf(hasHead(name), hasParameters(1, 1, requiredLines), hasTail(terminator));
}
- private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
+ private Matcher<List<String>> hasParameters(int ignoreStart, int ignoreEnd, String... lines) {
return new TypeSafeDiagnosingMatcher<List<String>>() {
@Override
protected boolean matchesSafely(List<String> item, Description mismatchDescription) {
- if (!item.get(0).equals(name)) {
- mismatchDescription.appendText("FCP message is named ").appendValue(item.get(0));
+ if (item.size() < (ignoreStart + ignoreEnd)) {
+ mismatchDescription.appendText("has only ").appendValue(item.size()).appendText(" elements");
return false;
}
- for (String requiredLine : requiredLines) {
- if (item.indexOf(requiredLine) < 1) {
- mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
+ for (String line : lines) {
+ if ((item.indexOf(line) < ignoreStart) || (item.indexOf(line) >= (item.size() - ignoreEnd))) {
+ mismatchDescription.appendText("does not contains ").appendValue(line);
return false;
}
}
@Override
public void describeTo(Description description) {
- description.appendText("FCP message named ").appendValue(name);
- description.appendValueList(", containing the lines ", ", ", "", requiredLines);
+ description.appendText("contains ").appendValueList("(", ", ", ")", lines);
+ description.appendText(", ignoring the first ").appendValue(ignoreStart);
+ description.appendText(" and the last ").appendValue(ignoreEnd);
}
};
}
- @Test
- public void clientPutWithDirectDataSendsCorrectCommand()
- throws IOException, ExecutionException, InterruptedException {
- fcpClient.clientPut()
- .from(new ByteArrayInputStream("Hello\n".getBytes()))
- .length(6)
- .uri("KSK@foo.txt")
- .execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("Hello"));
- assertThat(lines, matchesFcpMessage("ClientPut", "UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
- }
+ private Matcher<List<String>> hasTail(String... lastElements) {
+ return new TypeSafeDiagnosingMatcher<List<String>>() {
+ @Override
+ protected boolean matchesSafely(List<String> list, Description mismatchDescription) {
+ if (list.size() < lastElements.length) {
+ mismatchDescription.appendText("is too small");
+ return false;
+ }
+ List<String> tail = list.subList(list.size() - lastElements.length, list.size());
+ if (!tail.equals(Arrays.asList(lastElements))) {
+ mismatchDescription.appendText("ends with ").appendValueList("(", ", ", ")", tail);
+ return false;
+ }
+ return true;
+ }
- @Test
- public void clientPutWithDirectDataSucceedsOnCorrectIdentifier()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Key>> key = fcpClient.clientPut()
- .from(new ByteArrayInputStream("Hello\n".getBytes()))
- .length(6)
- .uri("KSK@foo.txt")
- .execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("Hello"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "PutFailed",
- "Identifier=not-the-right-one",
- "EndMessage"
- );
- fcpServer.writeLine(
- "PutSuccessful",
- "URI=KSK@foo.txt",
- "Identifier=" + identifier,
- "EndMessage"
- );
- assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("ends with ").appendValueList("(", ", ", ")", lastElements);
+ }
+ };
}
- @Test
- public void clientPutWithDirectDataFailsOnCorrectIdentifier()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Key>> key = fcpClient.clientPut()
- .from(new ByteArrayInputStream("Hello\n".getBytes()))
- .length(6)
- .uri("KSK@foo.txt")
- .execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("Hello"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "PutSuccessful",
- "Identifier=not-the-right-one",
- "URI=KSK@foo.txt",
- "EndMessage"
- );
- fcpServer.writeLine(
- "PutFailed",
- "Identifier=" + identifier,
- "EndMessage"
- );
- assertThat(key.get().isPresent(), is(false));
- }
+ private List<String> lines;
+ private String identifier;
- @Test
- public void clientPutWithRenamedDirectDataSendsCorrectCommand()
+ private void connectAndAssert(Supplier<Matcher<List<String>>> requestMatcher)
throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientPut()
- .named("otherName.txt")
- .from(new ByteArrayInputStream("Hello\n".getBytes()))
- .length(6)
- .uri("KSK@foo.txt")
- .execute();
connectNode();
- List<String> lines = fcpServer.collectUntil(is("Hello"));
- assertThat(lines, matchesFcpMessage("ClientPut", "TargetFilename=otherName.txt", "UploadFrom=direct",
- "DataLength=6", "URI=KSK@foo.txt"));
+ readMessage(requestMatcher);
}
- @Test
- public void clientPutWithRedirectSendsCorrectCommand()
- throws IOException, ExecutionException, InterruptedException {
- fcpClient.clientPut().redirectTo("KSK@bar.txt").uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines,
- matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
+ private void readMessage(Supplier<Matcher<List<String>>> requestMatcher) throws IOException {
+ readMessage("EndMessage", requestMatcher);
}
- @Test
- public void clientPutWithFileSendsCorrectCommand() throws InterruptedException, ExecutionException, IOException {
- fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines,
- matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));
+ private void readMessage(String terminator, Supplier<Matcher<List<String>>> requestMatcher) throws IOException {
+ lines = fcpServer.collectUntil(is(terminator));
+ identifier = extractIdentifier(lines);
+ assertThat(lines, requestMatcher.get());
}
- @Test
- public void clientPutWithFileCanCompleteTestDdaSequence()
- throws IOException, ExecutionException, InterruptedException {
- File tempFile = createTempFile();
- fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
+ private void replyWithProtocolError() throws IOException {
fcpServer.writeLine(
"ProtocolError",
"Identifier=" + identifier,
- "Code=25",
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDARequest",
- "Directory=" + tempFile.getParent(),
- "WantReadDirectory=true",
- "WantWriteDirectory=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "TestDDAReply",
- "Directory=" + tempFile.getParent(),
- "ReadFilename=" + tempFile,
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDAResponse",
- "Directory=" + tempFile.getParent(),
- "ReadContent=test-content",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "TestDDAComplete",
- "Directory=" + tempFile.getParent(),
- "ReadDirectoryAllowed=true",
"EndMessage"
);
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines,
- matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt",
- "Filename=" + new File(tempFile.getParent(), "test.dat")));
}
- private File createTempFile() throws IOException {
- File tempFile = File.createTempFile("test-dda-", ".dat");
- tempFile.deleteOnExit();
- Files.write("test-content", tempFile, StandardCharsets.UTF_8);
- return tempFile;
- }
+ public class ConnectionsAndKeyPairs {
- @Test
- public void clientPutDoesNotReactToProtocolErrorForDifferentIdentifier()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "ProtocolError",
- "Identifier=not-the-right-one",
- "Code=25",
- "EndMessage"
- );
- fcpServer.writeLine(
- "PutSuccessful",
- "Identifier=" + identifier,
- "URI=KSK@foo.txt",
- "EndMessage"
- );
- assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
- }
+ public class Connections {
- @Test
- public void clientPutAbortsOnProtocolErrorOtherThan25()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "ProtocolError",
- "Identifier=" + identifier,
- "Code=1",
- "EndMessage"
- );
- assertThat(key.get().isPresent(), is(false));
- }
+ @Test(expected = ExecutionException.class)
+ public void throwsExceptionOnFailure() throws IOException, ExecutionException, InterruptedException {
+ Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ fcpServer.writeLine(
+ "CloseConnectionDuplicateClientName",
+ "EndMessage"
+ );
+ keyPairFuture.get();
+ }
- @Test
- public void clientPutDoesNotReplyToWrongTestDdaReply() throws IOException, ExecutionException,
- InterruptedException {
- File tempFile = createTempFile();
- fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "ProtocolError",
- "Identifier=" + identifier,
- "Code=25",
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDARequest",
- "Directory=" + tempFile.getParent(),
- "WantReadDirectory=true",
- "WantWriteDirectory=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "TestDDAReply",
- "Directory=/some-other-directory",
- "ReadFilename=" + tempFile,
- "EndMessage"
- );
- fcpServer.writeLine(
- "TestDDAReply",
- "Directory=" + tempFile.getParent(),
- "ReadFilename=" + tempFile,
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDAResponse",
- "Directory=" + tempFile.getParent(),
- "ReadContent=test-content",
- "EndMessage"
- ));
- }
+ @Test(expected = ExecutionException.class)
+ public void throwsExceptionIfConnectionIsClosed() throws IOException, ExecutionException, InterruptedException {
+ Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ fcpServer.close();
+ keyPairFuture.get();
+ }
- @Test
- public void clientPutSendsResponseEvenIfFileCanNotBeRead()
- throws IOException, ExecutionException, InterruptedException {
- File tempFile = createTempFile();
- fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "ProtocolError",
- "Identifier=" + identifier,
- "Code=25",
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDARequest",
- "Directory=" + tempFile.getParent(),
- "WantReadDirectory=true",
- "WantWriteDirectory=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "TestDDAReply",
- "Directory=" + tempFile.getParent(),
- "ReadFilename=" + tempFile + ".foo",
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDAResponse",
- "Directory=" + tempFile.getParent(),
- "ReadContent=failed-to-read",
- "EndMessage"
- ));
- }
+ @Test
+ public void connectionIsReused() throws InterruptedException, ExecutionException, IOException {
+ Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ replyWithKeyPair();
+ keyPair.get();
+ keyPair = fcpClient.generateKeypair().execute();
+ readMessage(() -> matchesFcpMessage("GenerateSSK"));
+ identifier = extractIdentifier(lines);
+ replyWithKeyPair();
+ keyPair.get();
+ }
- @Test
- public void clientPutDoesNotResendOriginalClientPutOnTestDDACompleteWithWrongDirectory()
- throws IOException, ExecutionException, InterruptedException {
- File tempFile = createTempFile();
- fcpClient.clientPut().from(new File(tempFile.getParent(), "test.dat")).uri("KSK@foo.txt").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "TestDDAComplete",
- "Directory=/some-other-directory",
- "EndMessage"
- );
- fcpServer.writeLine(
- "ProtocolError",
- "Identifier=" + identifier,
- "Code=25",
- "EndMessage"
- );
- lines = fcpServer.collectUntil(is("EndMessage"));
- assertThat(lines, matchesFcpMessage(
- "TestDDARequest",
- "Directory=" + tempFile.getParent(),
- "WantReadDirectory=true",
- "WantWriteDirectory=false",
- "EndMessage"
- ));
- }
+ @Test
+ public void defaultFcpClientCanReconnectAfterConnectionHasBeenClosed()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<FcpKeyPair> keyPair = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ fcpServer.close();
+ try {
+ keyPair.get();
+ Assert.fail();
+ } catch (ExecutionException e) {
+ /* ignore. */
+ }
+ keyPair = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ replyWithKeyPair();
+ keyPair.get();
+ }
- @Test
- public void clientPutSendsNotificationsForGeneratedKeys()
- throws InterruptedException, ExecutionException, IOException {
- List<String> generatedKeys = new CopyOnWriteArrayList<>();
- Future<Optional<Key>> key = fcpClient.clientPut()
- .onKeyGenerated(generatedKeys::add)
- .from(new ByteArrayInputStream("Hello\n".getBytes()))
- .length(6)
- .uri("KSK@foo.txt")
- .execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("Hello"));
- String identifier = extractIdentifier(lines);
- fcpServer.writeLine(
- "URIGenerated",
- "Identifier=" + identifier,
- "URI=KSK@foo.txt",
- "EndMessage"
- );
- fcpServer.writeLine(
- "PutSuccessful",
- "URI=KSK@foo.txt",
- "Identifier=" + identifier,
- "EndMessage"
- );
- assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
- assertThat(generatedKeys, contains("KSK@foo.txt"));
- }
+ }
- @Test
- public void defaultFcpClientCanGetNodeInformation() throws InterruptedException, ExecutionException, IOException {
- Future<NodeData> nodeData = fcpClient.getNode().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetNode",
- "Identifier=" + identifier,
- "GiveOpennetRef=false",
- "WithPrivate=false",
- "WithVolatile=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "NodeData",
- "Identifier=" + identifier,
- "ark.pubURI=SSK@3YEf.../ark",
- "ark.number=78",
- "auth.negTypes=2",
- "version=Fred,0.7,1.0,1466",
- "lastGoodVersion=Fred,0.7,1.0,1466",
- "EndMessage"
- );
- assertThat(nodeData.get(), notNullValue());
- }
+ public class GenerateKeyPair {
- @Test
- public void defaultFcpClientCanGetNodeInformationWithOpennetRef()
- throws InterruptedException, ExecutionException, IOException {
- Future<NodeData> nodeData = fcpClient.getNode().opennetRef().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetNode",
- "Identifier=" + identifier,
- "GiveOpennetRef=true",
- "WithPrivate=false",
- "WithVolatile=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "NodeData",
- "Identifier=" + identifier,
- "opennet=true",
- "ark.pubURI=SSK@3YEf.../ark",
- "ark.number=78",
- "auth.negTypes=2",
- "version=Fred,0.7,1.0,1466",
- "lastGoodVersion=Fred,0.7,1.0,1466",
- "EndMessage"
- );
- assertThat(nodeData.get().getVersion().toString(), is("Fred,0.7,1.0,1466"));
- }
+ @Test
+ public void defaultFcpClientCanGenerateKeypair()
+ throws ExecutionException, InterruptedException, IOException {
+ Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
+ connectAndAssert(() -> matchesFcpMessage("GenerateSSK"));
+ replyWithKeyPair();
+ FcpKeyPair keyPair = keyPairFuture.get();
+ assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
+ assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
+ }
- @Test
- public void defaultFcpClientCanGetNodeInformationWithPrivateData()
- throws InterruptedException, ExecutionException, IOException {
- Future<NodeData> nodeData = fcpClient.getNode().includePrivate().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetNode",
- "Identifier=" + identifier,
- "GiveOpennetRef=false",
- "WithPrivate=true",
- "WithVolatile=false",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "NodeData",
- "Identifier=" + identifier,
- "opennet=false",
- "ark.pubURI=SSK@3YEf.../ark",
- "ark.number=78",
- "auth.negTypes=2",
- "version=Fred,0.7,1.0,1466",
- "lastGoodVersion=Fred,0.7,1.0,1466",
- "ark.privURI=SSK@XdHMiRl",
- "EndMessage"
- );
- assertThat(nodeData.get().getARK().getPrivateURI(), is("SSK@XdHMiRl"));
- }
-
- @Test
- public void defaultFcpClientCanGetNodeInformationWithVolatileData()
- throws InterruptedException, ExecutionException, IOException {
- Future<NodeData> nodeData = fcpClient.getNode().includeVolatile().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetNode",
- "Identifier=" + identifier,
- "GiveOpennetRef=false",
- "WithPrivate=false",
- "WithVolatile=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "NodeData",
- "Identifier=" + identifier,
- "opennet=false",
- "ark.pubURI=SSK@3YEf.../ark",
- "ark.number=78",
- "auth.negTypes=2",
- "version=Fred,0.7,1.0,1466",
- "lastGoodVersion=Fred,0.7,1.0,1466",
- "volatile.freeJavaMemory=205706528",
- "EndMessage"
- );
- assertThat(nodeData.get().getVolatile("freeJavaMemory"), is("205706528"));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithoutDetails()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "EndMessage"
- );
- assertThat(configData.get(), notNullValue());
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithCurrent()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withCurrent().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithCurrent=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "current.foo=bar",
- "EndMessage"
- );
- assertThat(configData.get().getCurrent("foo"), is("bar"));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithDefaults()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withDefaults().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithDefaults=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "default.foo=bar",
- "EndMessage"
- );
- assertThat(configData.get().getDefault("foo"), is("bar"));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithSortOrder()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withSortOrder().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithSortOrder=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "sortOrder.foo=17",
- "EndMessage"
- );
- assertThat(configData.get().getSortOrder("foo"), is(17));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithExpertFlag()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withExpertFlag().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithExpertFlag=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "expertFlag.foo=true",
- "EndMessage"
- );
- assertThat(configData.get().getExpertFlag("foo"), is(true));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithForceWriteFlag()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withForceWriteFlag().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithForceWriteFlag=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "forceWriteFlag.foo=true",
- "EndMessage"
- );
- assertThat(configData.get().getForceWriteFlag("foo"), is(true));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithShortDescription()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withShortDescription().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithShortDescription=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "shortDescription.foo=bar",
- "EndMessage"
- );
- assertThat(configData.get().getShortDescription("foo"), is("bar"));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithLongDescription()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withLongDescription().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithLongDescription=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "longDescription.foo=bar",
- "EndMessage"
- );
- assertThat(configData.get().getLongDescription("foo"), is("bar"));
- }
-
- @Test
- public void defaultFcpClientCanGetConfigWithDataTypes()
- throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> configData = fcpClient.getConfig().withDataTypes().execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "GetConfig",
- "Identifier=" + identifier,
- "WithDataTypes=true",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "dataType.foo=number",
- "EndMessage"
- );
- assertThat(configData.get().getDataType("foo"), is("number"));
- }
-
- @Test
- public void defaultFcpClientCanModifyConfigData() throws InterruptedException, ExecutionException, IOException {
- Future<ConfigData> newConfigData = fcpClient.modifyConfig().set("foo.bar").to("baz").execute();
- connectNode();
- List<String> lines = fcpServer.collectUntil(is("EndMessage"));
- String identifier = extractIdentifier(lines);
- assertThat(lines, matchesFcpMessage(
- "ModifyConfig",
- "Identifier=" + identifier,
- "foo.bar=baz",
- "EndMessage"
- ));
- fcpServer.writeLine(
- "ConfigData",
- "Identifier=" + identifier,
- "current.foo.bar=baz",
- "EndMessage"
- );
- assertThat(newConfigData.get().getCurrent("foo.bar"), is("baz"));
- }
-
- private List<String> lines;
- private String identifier;
-
- private void connectAndAssert(Supplier<Matcher<List<String>>> requestMatcher)
- throws InterruptedException, ExecutionException, IOException {
- connectNode();
- readMessage(requestMatcher);
- }
-
- private void readMessage(Supplier<Matcher<List<String>>> requestMatcher) throws IOException {
- lines = fcpServer.collectUntil(is("EndMessage"));
- identifier = extractIdentifier(lines);
- assertThat(lines, requestMatcher.get());
- }
-
- public class GenerateKeyPair {
-
- @Test
- public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
- Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
- connectAndAssert(() ->matchesFcpMessage("GenerateSSK", "EndMessage"));
- replyWithKeyPair();
- FcpKeyPair keyPair = keyPairFuture.get();
- assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
- assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
}
private void replyWithKeyPair() throws IOException {
return matchesFcpMessage(
"ListPeer",
"Identifier=" + identifier,
- "NodeIdentifier=" + nodeId,
- "EndMessage"
+ "NodeIdentifier=" + nodeId
);
}
return matchesFcpMessage(
"ListPeers",
"WithVolatile=" + withVolatile,
- "WithMetadata=" + withMetadata,
- "EndMessage"
+ "WithMetadata=" + withMetadata
);
}
assertThat(peer.get().get().getIdentity(), is("id1"));
}
+ @Test
+ public void protocolErrorEndsCommand() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Peer>> peer = fcpClient.addPeer().fromFile(new File("/tmp/ref.txt")).execute();
+ connectAndAssert(() -> allOf(matchesAddPeer(), hasItem("File=/tmp/ref.txt")));
+ replyWithProtocolError();
+ assertThat(peer.get().isPresent(), is(false));
+ }
+
private NodeRef createNodeRef() {
NodeRef nodeRef = new NodeRef();
nodeRef.setIdentity("id1");
private Matcher<List<String>> matchesAddPeer() {
return matchesFcpMessage(
"AddPeer",
- "Identifier=" + identifier,
- "EndMessage"
+ "Identifier=" + identifier
);
}
"ModifyPeer",
"Identifier=" + identifier,
"NodeIdentifier=" + nodeIdentifier,
- setting + "=" + value,
- "EndMessage"
+ setting + "=" + value
);
}
return matchesFcpMessage(
"RemovePeer",
"Identifier=" + identifier,
- "NodeIdentifier=" + nodeIdentifier,
- "EndMessage"
+ "NodeIdentifier=" + nodeIdentifier
);
}
private Matcher<List<String>> matchesListPeerNotes(String nodeIdentifier) {
return matchesFcpMessage(
"ListPeerNotes",
- "NodeIdentifier=" + nodeIdentifier,
- "EndMessage"
+ "NodeIdentifier=" + nodeIdentifier
);
}
"Identifier=" + identifier,
"NodeIdentifier=" + nodeIdentifier,
"PeerNoteType=1",
- "NoteText=Zm9v",
- "EndMessage"
+ "NoteText=Zm9v"
);
}
"Identifier=" + identifier,
"PluginURL=superPlugin",
"URLType=official",
- "OfficialSource=" + officialSource,
- "EndMessage"
+ "OfficialSource=" + officialSource
);
}
verifyPluginInfo(pluginInfo);
}
- @Test
- public void fromUrl() throws ExecutionException, InterruptedException, IOException {
- Future<Optional<PluginInfo>> pluginInfo = fcpClient.loadPlugin().fromUrl(URL).execute();
- connectAndAssert(() -> createMatcher("url", URL));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
- }
+ @Test
+ public void fromUrl() throws ExecutionException, InterruptedException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo = fcpClient.loadPlugin().fromUrl(URL).execute();
+ connectAndAssert(() -> createMatcher("url", URL));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void fromFreenet() throws ExecutionException, InterruptedException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo = fcpClient.loadPlugin().fromFreenet(KEY).execute();
+ connectAndAssert(() -> createMatcher("freenet", KEY));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ private Matcher<List<String>> createMatcher(String urlType, String url) {
+ return matchesFcpMessage(
+ "LoadPlugin",
+ "Identifier=" + identifier,
+ "PluginURL=" + url,
+ "URLType=" + urlType
+ );
+ }
+
+ }
+
+ public class Failed {
+
+ @Test
+ public void failedLoad() throws ExecutionException, InterruptedException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.loadPlugin().officialFromFreenet("superPlugin").execute();
+ connectAndAssert(() -> matchesFcpMessage("LoadPlugin"));
+ replyWithProtocolError();
+ assertThat(pluginInfo.get().isPresent(), is(false));
+ }
+
+ }
+
+ }
+
+ public class ReloadPlugin {
+
+ @Test
+ public void reloadingPluginWorks() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo = fcpClient.reloadPlugin().plugin(CLASS_NAME).execute();
+ connectAndAssert(this::matchReloadPluginMessage);
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void reloadingPluginWithMaxWaitTimeWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.reloadPlugin().waitFor(1234).plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("MaxWaitTime=1234")));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void reloadingPluginWithPurgeWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.reloadPlugin().purge().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("Purge=true")));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void reloadingPluginWithStoreWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.reloadPlugin().addToConfig().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("Store=true")));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void protocolErrorIsRecognizedAsFailure()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo = fcpClient.reloadPlugin().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> matchReloadPluginMessage());
+ replyWithProtocolError();
+ assertThat(pluginInfo.get().isPresent(), is(false));
+ }
+
+ private Matcher<List<String>> matchReloadPluginMessage() {
+ return matchesFcpMessage(
+ "ReloadPlugin",
+ "Identifier=" + identifier,
+ "PluginName=" + CLASS_NAME
+ );
+ }
+
+ }
+
+ public class RemovePlugin {
+
+ @Test
+ public void removingPluginWorks() throws InterruptedException, ExecutionException, IOException {
+ Future<Boolean> pluginRemoved = fcpClient.removePlugin().plugin(CLASS_NAME).execute();
+ connectAndAssert(this::matchPluginRemovedMessage);
+ replyWithPluginRemoved();
+ assertThat(pluginRemoved.get(), is(true));
+ }
+
+ @Test
+ public void removingPluginWithMaxWaitTimeWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Boolean> pluginRemoved = fcpClient.removePlugin().waitFor(1234).plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchPluginRemovedMessage(), hasItem("MaxWaitTime=1234")));
+ replyWithPluginRemoved();
+ assertThat(pluginRemoved.get(), is(true));
+ }
+
+ @Test
+ public void removingPluginWithPurgeWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Boolean> pluginRemoved = fcpClient.removePlugin().purge().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchPluginRemovedMessage(), hasItem("Purge=true")));
+ replyWithPluginRemoved();
+ assertThat(pluginRemoved.get(), is(true));
+ }
+
+ private void replyWithPluginRemoved() throws IOException {
+ fcpServer.writeLine(
+ "PluginRemoved",
+ "Identifier=" + identifier,
+ "PluginName=" + CLASS_NAME,
+ "EndMessage"
+ );
+ }
+
+ private Matcher<List<String>> matchPluginRemovedMessage() {
+ return matchesFcpMessage(
+ "RemovePlugin",
+ "Identifier=" + identifier,
+ "PluginName=" + CLASS_NAME
+ );
+ }
+
+ }
+
+ public class GetPluginInfo {
+
+ @Test
+ public void gettingPluginInfoWorks() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo = fcpClient.getPluginInfo().plugin(CLASS_NAME).execute();
+ connectAndAssert(this::matchGetPluginInfoMessage);
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void gettingPluginInfoWithDetailsWorks()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.getPluginInfo().detailed().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchGetPluginInfoMessage(), hasItem("Detailed=true")));
+ replyWithPluginInfo();
+ verifyPluginInfo(pluginInfo);
+ }
+
+ @Test
+ public void protocolErrorIsRecognizedAsFailure()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<PluginInfo>> pluginInfo =
+ fcpClient.getPluginInfo().detailed().plugin(CLASS_NAME).execute();
+ connectAndAssert(() -> allOf(matchGetPluginInfoMessage(), hasItem("Detailed=true")));
+ replyWithProtocolError();
+ assertThat(pluginInfo.get(), is(Optional.empty()));
+ }
+
+ private Matcher<List<String>> matchGetPluginInfoMessage() {
+ return matchesFcpMessage(
+ "GetPluginInfo",
+ "Identifier=" + identifier,
+ "PluginName=" + CLASS_NAME
+ );
+ }
+
+ }
+
+ }
+
+ public class UskSubscriptionCommands {
+
+ private static final String URI = "USK@some,uri/file.txt";
+
+ @Test
+ public void subscriptionWorks() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
+ connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI));
+ replyWithSubscribed();
+ assertThat(uskSubscription.get().get().getUri(), is(URI));
+ AtomicInteger edition = new AtomicInteger();
+ CountDownLatch updated = new CountDownLatch(2);
+ uskSubscription.get().get().onUpdate(e -> {
+ edition.set(e);
+ updated.countDown();
+ });
+ sendUpdateNotification(23);
+ sendUpdateNotification(24);
+ assertThat("updated in time", updated.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(edition.get(), is(24));
+ }
+
+ @Test
+ public void subscriptionUpdatesMultipleTimes() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
+ connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI));
+ replyWithSubscribed();
+ assertThat(uskSubscription.get().get().getUri(), is(URI));
+ AtomicInteger edition = new AtomicInteger();
+ CountDownLatch updated = new CountDownLatch(2);
+ uskSubscription.get().get().onUpdate(e -> {
+ edition.set(e);
+ updated.countDown();
+ });
+ uskSubscription.get().get().onUpdate(e -> updated.countDown());
+ sendUpdateNotification(23);
+ assertThat("updated in time", updated.await(5, TimeUnit.SECONDS), is(true));
+ assertThat(edition.get(), is(23));
+ }
+
+ @Test
+ public void subscriptionCanBeCancelled() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
+ connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI));
+ replyWithSubscribed();
+ assertThat(uskSubscription.get().get().getUri(), is(URI));
+ AtomicBoolean updated = new AtomicBoolean();
+ uskSubscription.get().get().onUpdate(e -> updated.set(true));
+ uskSubscription.get().get().cancel();
+ readMessage(() -> matchesFcpMessage("UnsubscribeUSK", "Identifier=" + identifier));
+ sendUpdateNotification(23);
+ assertThat(updated.get(), is(false));
+ }
+
+ private void replyWithSubscribed() throws IOException {
+ fcpServer.writeLine(
+ "SubscribedUSK",
+ "Identifier=" + identifier,
+ "URI=" + URI,
+ "DontPoll=false",
+ "EndMessage"
+ );
+ }
+
+ private void sendUpdateNotification(int edition, String... additionalLines) throws IOException {
+ fcpServer.writeLine(
+ "SubscribedUSKUpdate",
+ "Identifier=" + identifier,
+ "URI=" + URI,
+ "Edition=" + edition
+ );
+ fcpServer.writeLine(additionalLines);
+ fcpServer.writeLine("EndMessage");
+ }
+
+ }
+
+ public class ClientGet {
+
+ @Test
+ public void works() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "ReturnType=direct"));
+ replyWithAllData("not-test", "Hello World", "text/plain;charset=latin-9");
+ replyWithAllData(identifier, "Hello", "text/plain;charset=utf-8");
+ Optional<Data> data = dataFuture.get();
+ verifyData(data);
+ }
+
+ @Test
+ public void getFailedIsRecognized() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
+ replyWithGetFailed("not-test");
+ replyWithGetFailed(identifier);
+ Optional<Data> data = dataFuture.get();
+ assertThat(data.isPresent(), is(false));
+ }
+
+ @Test
+ public void getFailedForDifferentIdentifierIsIgnored()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
+ replyWithGetFailed("not-test");
+ replyWithAllData(identifier, "Hello", "text/plain;charset=utf-8");
+ Optional<Data> data = dataFuture.get();
+ verifyData(data);
+ }
+
+ @Test(expected = ExecutionException.class)
+ public void connectionClosedIsRecognized() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
+ fcpServer.close();
+ dataFuture.get();
+ }
+
+ @Test
+ public void withIgnoreData() throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
+ }
+
+ @Test
+ public void withDataStoreOnly() throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
+ }
+
+ @Test
+ public void clientGetWithMaxSizeSettingSendsCorrectCommands()
+ throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
+ }
+
+ @Test
+ public void clientGetWithPrioritySettingSendsCorrectCommands()
+ throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
+ }
+
+ @Test
+ public void clientGetWithRealTimeSettingSendsCorrectCommands()
+ throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().realTime().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
+ }
+
+ @Test
+ public void clientGetWithGlobalSettingSendsCorrectCommands()
+ throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientGet().global().uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
+ }
+
+ private void replyWithGetFailed(String identifier) throws IOException {
+ fcpServer.writeLine(
+ "GetFailed",
+ "Identifier=" + identifier,
+ "Code=3",
+ "EndMessage"
+ );
+ }
+
+ private void replyWithAllData(String identifier, String text, String contentType) throws IOException {
+ fcpServer.writeLine(
+ "AllData",
+ "Identifier=" + identifier,
+ "DataLength=" + (text.length() + 1),
+ "StartupTime=1435610539000",
+ "CompletionTime=1435610540000",
+ "Metadata.ContentType=" + contentType,
+ "Data",
+ text
+ );
+ }
+
+ private void verifyData(Optional<Data> data) throws IOException {
+ assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
+ assertThat(data.get().size(), is(6L));
+ assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
+ is("Hello\n".getBytes(StandardCharsets.UTF_8)));
+ }
+
+ }
+
+ public class ClientPut {
+
+ @Test
+ public void sendsCorrectCommand() throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut()
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ readMessage("Hello", this::matchesDirectClientPut);
+ }
+
+ @Test
+ public void succeedsOnCorrectIdentifier() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPut()
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ readMessage("Hello", this::matchesDirectClientPut);
+ replyWithPutFailed("not-the-right-one");
+ replyWithPutSuccessful(identifier);
+ assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
+ }
+
+ @Test
+ public void failsOnCorrectIdentifier() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPut()
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ readMessage("Hello", this::matchesDirectClientPut);
+ replyWithPutSuccessful("not-the-right-one");
+ replyWithPutFailed(identifier);
+ assertThat(key.get().isPresent(), is(false));
+ }
+
+ @Test
+ public void renameIsSentCorrectly() throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientPut()
+ .named("otherName.txt")
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ readMessage("Hello", () -> allOf(
+ hasHead("ClientPut"),
+ hasParameters(1, 2, "TargetFilename=otherName.txt", "UploadFrom=direct", "DataLength=6",
+ "URI=KSK@foo.txt"),
+ hasTail("EndMessage", "Hello")
+ ));
+ }
+
+ @Test
+ public void redirectIsSentCorrecly() throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut().redirectTo("KSK@bar.txt").uri("KSK@foo.txt").execute();
+ connectAndAssert(() ->
+ matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
+ }
+
+ @Test
+ public void withFileIsSentCorrectly() throws InterruptedException, ExecutionException, IOException {
+ fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
+ connectAndAssert(() ->
+ matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));
+ }
+
+ public class DDA {
+
+ private final File ddaFile;
+ private final File fileToUpload;
+
+ public DDA() throws IOException {
+ ddaFile = createDdaFile();
+ fileToUpload = new File(ddaFile.getParent(), "test.dat");
+ }
+
+ private Matcher<List<String>> matchesFileClientPut(File file) {
+ return matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=" + file);
+ }
+
+ @Test
+ public void completeDda() throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut().from(fileToUpload).uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFileClientPut(fileToUpload));
+ sendDdaRequired(identifier);
+ readMessage(() -> matchesTestDDARequest(ddaFile));
+ sendTestDDAReply(ddaFile.getParent(), ddaFile);
+ readMessage(() -> matchesTestDDAResponse(ddaFile));
+ writeTestDDAComplete(ddaFile);
+ readMessage(() -> matchesFileClientPut(fileToUpload));
+ }
+
+ @Test
+ public void ignoreOtherDda() throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut().from(fileToUpload).uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFileClientPut(fileToUpload));
+ sendDdaRequired(identifier);
+ readMessage(() -> matchesTestDDARequest(ddaFile));
+ sendTestDDAReply("/some-other-directory", ddaFile);
+ sendTestDDAReply(ddaFile.getParent(), ddaFile);
+ readMessage(() -> matchesTestDDAResponse(ddaFile));
+ }
+
+ @Test
+ public void sendResponseIfFileUnreadable() throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut().from(fileToUpload).uri("KSK@foo.txt").execute();
+ connectAndAssert(() -> matchesFileClientPut(fileToUpload));
+ sendDdaRequired(identifier);
+ readMessage(() -> matchesTestDDARequest(ddaFile));
+ sendTestDDAReply(ddaFile.getParent(), new File(ddaFile + ".foo"));
+ readMessage(this::matchesFailedToReadResponse);
+ }
+
+ @Test
+ public void clientPutDoesNotResendOriginalClientPutOnTestDDACompleteWithWrongDirectory()
+ throws IOException, ExecutionException, InterruptedException {
+ fcpClient.clientPut().from(fileToUpload).uri("KSK@foo.txt").execute();
+ connectNode();
+ List<String> lines = fcpServer.collectUntil(is("EndMessage"));
+ String identifier = extractIdentifier(lines);
+ fcpServer.writeLine(
+ "TestDDAComplete",
+ "Directory=/some-other-directory",
+ "EndMessage"
+ );
+ sendDdaRequired(identifier);
+ lines = fcpServer.collectUntil(is("EndMessage"));
+ assertThat(lines, matchesFcpMessage(
+ "TestDDARequest",
+ "Directory=" + ddaFile.getParent(),
+ "WantReadDirectory=true",
+ "WantWriteDirectory=false"
+ ));
+ }
+
+ private Matcher<List<String>> matchesFailedToReadResponse() {
+ return matchesFcpMessage(
+ "TestDDAResponse",
+ "Directory=" + ddaFile.getParent(),
+ "ReadContent=failed-to-read"
+ );
+ }
+
+ private void writeTestDDAComplete(File tempFile) throws IOException {
+ fcpServer.writeLine(
+ "TestDDAComplete",
+ "Directory=" + tempFile.getParent(),
+ "ReadDirectoryAllowed=true",
+ "EndMessage"
+ );
+ }
+
+ private Matcher<List<String>> matchesTestDDAResponse(File tempFile) {
+ return matchesFcpMessage(
+ "TestDDAResponse",
+ "Directory=" + tempFile.getParent(),
+ "ReadContent=test-content"
+ );
+ }
- @Test
- public void fromFreenet() throws ExecutionException, InterruptedException, IOException {
- Future<Optional<PluginInfo>> pluginInfo = fcpClient.loadPlugin().fromFreenet(KEY).execute();
- connectAndAssert(() -> createMatcher("freenet", KEY));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
- }
+ private void sendTestDDAReply(String directory, File tempFile) throws IOException {
+ fcpServer.writeLine(
+ "TestDDAReply",
+ "Directory=" + directory,
+ "ReadFilename=" + tempFile,
+ "EndMessage"
+ );
+ }
- private Matcher<List<String>> createMatcher(String urlType, String url) {
- return matchesFcpMessage(
- "LoadPlugin",
- "Identifier=" + identifier,
- "PluginURL=" + url,
- "URLType=" + urlType,
- "EndMessage"
- );
- }
+ private Matcher<List<String>> matchesTestDDARequest(File tempFile) {
+ return matchesFcpMessage(
+ "TestDDARequest",
+ "Directory=" + tempFile.getParent(),
+ "WantReadDirectory=true",
+ "WantWriteDirectory=false"
+ );
+ }
+ private void sendDdaRequired(String identifier) throws IOException {
+ fcpServer.writeLine(
+ "ProtocolError",
+ "Identifier=" + identifier,
+ "Code=25",
+ "EndMessage"
+ );
}
- public class Failed {
+ }
- @Test
- public void failedLoad() throws ExecutionException, InterruptedException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.loadPlugin().officialFromFreenet("superPlugin").execute();
- connectAndAssert(() -> matchesFcpMessage("LoadPlugin", "EndMessage"));
- replyWithProtocolError();
- assertThat(pluginInfo.get().isPresent(), is(false));
- }
+ private void replyWithPutSuccessful(String identifier) throws IOException {
+ fcpServer.writeLine(
+ "PutSuccessful",
+ "URI=KSK@foo.txt",
+ "Identifier=" + identifier,
+ "EndMessage"
+ );
+ }
- }
+ private void replyWithPutFailed(String identifier) throws IOException {
+ fcpServer.writeLine(
+ "PutFailed",
+ "Identifier=" + identifier,
+ "EndMessage"
+ );
+ }
+ private Matcher<List<String>> matchesDirectClientPut(String... additionalLines) {
+ List<String> lines = new ArrayList<>(Arrays.asList("UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
+ Arrays.asList(additionalLines).forEach(lines::add);
+ return allOf(
+ hasHead("ClientPut"),
+ hasParameters(1, 2, lines.toArray(new String[lines.size()])),
+ hasTail("EndMessage", "Hello")
+ );
+ }
+
+ private File createDdaFile() throws IOException {
+ File tempFile = File.createTempFile("test-dda-", ".dat");
+ tempFile.deleteOnExit();
+ Files.write("test-content", tempFile, StandardCharsets.UTF_8);
+ return tempFile;
}
- private void replyWithProtocolError() throws IOException {
+ @Test
+ public void clientPutDoesNotReactToProtocolErrorForDifferentIdentifier()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
+ connectNode();
+ List<String> lines = fcpServer.collectUntil(is("EndMessage"));
+ String identifier = extractIdentifier(lines);
fcpServer.writeLine(
"ProtocolError",
+ "Identifier=not-the-right-one",
+ "Code=25",
+ "EndMessage"
+ );
+ fcpServer.writeLine(
+ "PutSuccessful",
"Identifier=" + identifier,
+ "URI=KSK@foo.txt",
"EndMessage"
);
+ assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
}
- public class ReloadPlugin {
+ @Test
+ public void clientPutAbortsOnProtocolErrorOtherThan25()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPut().from(new File("/tmp/data.txt")).uri("KSK@foo.txt").execute();
+ connectNode();
+ List<String> lines = fcpServer.collectUntil(is("EndMessage"));
+ String identifier = extractIdentifier(lines);
+ fcpServer.writeLine(
+ "ProtocolError",
+ "Identifier=" + identifier,
+ "Code=1",
+ "EndMessage"
+ );
+ assertThat(key.get().isPresent(), is(false));
+ }
+
+ @Test
+ public void clientPutSendsNotificationsForGeneratedKeys()
+ throws InterruptedException, ExecutionException, IOException {
+ List<String> generatedKeys = new CopyOnWriteArrayList<>();
+ Future<Optional<Key>> key = fcpClient.clientPut()
+ .onKeyGenerated(generatedKeys::add)
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ List<String> lines = fcpServer.collectUntil(is("Hello"));
+ String identifier = extractIdentifier(lines);
+ fcpServer.writeLine(
+ "URIGenerated",
+ "Identifier=" + identifier,
+ "URI=KSK@foo.txt",
+ "EndMessage"
+ );
+ replyWithPutSuccessful(identifier);
+ assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
+ assertThat(generatedKeys, contains("KSK@foo.txt"));
+ }
+
+ @Test
+ public void clientPutSendsNotificationOnProgress() throws InterruptedException, ExecutionException, IOException {
+ List<RequestProgress> requestProgress = new ArrayList<>();
+ Future<Optional<Key>> key = fcpClient.clientPut()
+ .onProgress(requestProgress::add)
+ .from(new ByteArrayInputStream("Hello\n".getBytes()))
+ .length(6)
+ .uri("KSK@foo.txt")
+ .execute();
+ connectNode();
+ readMessage("Hello", () -> matchesDirectClientPut("Verbosity=1"));
+ replyWithSimpleProgress(1, 2, 3, 4, 5, 6, true, 8);
+ replyWithSimpleProgress(11, 12, 13, 14, 15, 16, false, 18);
+ replyWithPutSuccessful(identifier);
+ assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
+ assertThat(requestProgress, contains(
+ isRequestProgress(1, 2, 3, 4, 5, 6, true, 8),
+ isRequestProgress(11, 12, 13, 14, 15, 16, false, 18)
+ ));
+ }
+
+ private void replyWithSimpleProgress(
+ int total, int required, int failed, int fatallyFailed, int succeeded, int lastProgress,
+ boolean finalizedTotal, int minSuccessFetchBlocks) throws IOException {
+ fcpServer.writeLine(
+ "SimpleProgress",
+ "Identifier=" + identifier,
+ "Total=" + total,
+ "Required=" + required,
+ "Failed=" + failed,
+ "FatallyFailed=" + fatallyFailed,
+ "Succeeded=" + succeeded,
+ "LastProgress=" + lastProgress,
+ "FinalizedTotal=" + finalizedTotal,
+ "MinSuccessFetchBlocks=" + minSuccessFetchBlocks,
+ "EndMessage"
+ );
+ }
+
+ }
+
+ public class ClientPutDiskDir {
+
+ @Test
+ public void commandIsSentCorrectly() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPutDiskDir().fromDirectory(new File("")).uri("CHK@").execute();
+ connectAndAssert(this::matchesClientPutDiskDir);
+ fcpServer.writeLine("PutSuccessful", "Identifier=" + identifier, "URI=CHK@abc", "EndMessage");
+ assertThat(key.get().get().getKey(), is("CHK@abc"));
+ }
+
+ @Test
+ public void protocolErrorAbortsCommand() throws InterruptedException, ExecutionException, IOException {
+ Future<Optional<Key>> key = fcpClient.clientPutDiskDir().fromDirectory(new File("")).uri("CHK@").execute();
+ connectAndAssert(this::matchesClientPutDiskDir);
+ replyWithProtocolError();
+ assertThat(key.get().isPresent(), is(false));
+ }
+
+ private Matcher<List<String>> matchesClientPutDiskDir() {
+ return matchesFcpMessage(
+ "ClientPutDiskDir",
+ "Identifier=" + identifier,
+ "URI=CHK@",
+ "Filename=" + new File("").getPath()
+ );
+ }
+
+ }
+
+ public class ConfigCommand {
+
+ public class GetConfig {
@Test
- public void reloadingPluginWorks() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo = fcpClient.reloadPlugin().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> matchReloadPluginMessage());
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
+ public void defaultFcpClientCanGetConfigWithoutDetails()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<ConfigData> configData = fcpClient.getConfig().execute();
+ connectAndAssert(() -> matchesFcpMessage("GetConfig", "Identifier=" + identifier));
+ replyWithConfigData();
+ assertThat(configData.get(), notNullValue());
}
@Test
- public void reloadingPluginWithMaxWaitTimeWorks()
+ public void defaultFcpClientCanGetConfigWithCurrent()
throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.reloadPlugin().waitFor(1234).plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("MaxWaitTime=1234")));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
+ Future<ConfigData> configData = fcpClient.getConfig().withCurrent().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithCurrent"));
+ replyWithConfigData("current.foo=bar");
+ assertThat(configData.get().getCurrent("foo"), is("bar"));
}
@Test
- public void reloadingPluginWithPurgeWorks()
+ public void defaultFcpClientCanGetConfigWithDefaults()
throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.reloadPlugin().purge().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("Purge=true")));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
+ Future<ConfigData> configData = fcpClient.getConfig().withDefaults().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithDefaults"));
+ replyWithConfigData("default.foo=bar");
+ assertThat(configData.get().getDefault("foo"), is("bar"));
}
@Test
- public void reloadingPluginWithStoreWorks()
+ public void defaultFcpClientCanGetConfigWithSortOrder()
throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.reloadPlugin().addToConfig().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchReloadPluginMessage(), hasItem("Store=true")));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
+ Future<ConfigData> configData = fcpClient.getConfig().withSortOrder().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithSortOrder"));
+ replyWithConfigData("sortOrder.foo=17");
+ assertThat(configData.get().getSortOrder("foo"), is(17));
}
- private Matcher<List<String>> matchReloadPluginMessage() {
- return matchesFcpMessage(
- "ReloadPlugin",
- "Identifier=" + identifier,
- "PluginName=" + CLASS_NAME,
- "EndMessage"
- );
+ @Test
+ public void defaultFcpClientCanGetConfigWithExpertFlag()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<ConfigData> configData = fcpClient.getConfig().withExpertFlag().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithExpertFlag"));
+ replyWithConfigData("expertFlag.foo=true");
+ assertThat(configData.get().getExpertFlag("foo"), is(true));
}
- }
-
- public class RemovePlugin {
-
@Test
- public void removingPluginWorks() throws InterruptedException, ExecutionException, IOException {
- Future<Boolean> pluginRemoved = fcpClient.removePlugin().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> matchPluginRemovedMessage());
- replyWithPluginRemoved();
- assertThat(pluginRemoved.get(), is(true));
+ public void defaultFcpClientCanGetConfigWithForceWriteFlag()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<ConfigData> configData = fcpClient.getConfig().withForceWriteFlag().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithForceWriteFlag"));
+ replyWithConfigData("forceWriteFlag.foo=true");
+ assertThat(configData.get().getForceWriteFlag("foo"), is(true));
}
@Test
- public void removingPluginWithMaxWaitTimeWorks()
+ public void defaultFcpClientCanGetConfigWithShortDescription()
throws InterruptedException, ExecutionException, IOException {
- Future<Boolean> pluginRemoved = fcpClient.removePlugin().waitFor(1234).plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchPluginRemovedMessage(), hasItem("MaxWaitTime=1234")));
- replyWithPluginRemoved();
- assertThat(pluginRemoved.get(), is(true));
+ Future<ConfigData> configData = fcpClient.getConfig().withShortDescription().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithShortDescription"));
+ replyWithConfigData("shortDescription.foo=bar");
+ assertThat(configData.get().getShortDescription("foo"), is("bar"));
}
@Test
- public void removingPluginWithPurgeWorks()
+ public void defaultFcpClientCanGetConfigWithLongDescription()
throws InterruptedException, ExecutionException, IOException {
- Future<Boolean> pluginRemoved = fcpClient.removePlugin().purge().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchPluginRemovedMessage(), hasItem("Purge=true")));
- replyWithPluginRemoved();
- assertThat(pluginRemoved.get(), is(true));
+ Future<ConfigData> configData = fcpClient.getConfig().withLongDescription().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithLongDescription"));
+ replyWithConfigData("longDescription.foo=bar");
+ assertThat(configData.get().getLongDescription("foo"), is("bar"));
}
- private void replyWithPluginRemoved() throws IOException {
- fcpServer.writeLine(
- "PluginRemoved",
- "Identifier=" + identifier,
- "PluginName=" + CLASS_NAME,
- "EndMessage"
- );
+ @Test
+ public void defaultFcpClientCanGetConfigWithDataTypes()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<ConfigData> configData = fcpClient.getConfig().withDataTypes().execute();
+ connectAndAssert(() -> matchesGetConfigWithAdditionalParameter("WithDataTypes"));
+ replyWithConfigData("dataType.foo=number");
+ assertThat(configData.get().getDataType("foo"), is("number"));
}
- private Matcher<List<String>> matchPluginRemovedMessage() {
+ private Matcher<List<String>> matchesGetConfigWithAdditionalParameter(String additionalParameter) {
return matchesFcpMessage(
- "RemovePlugin",
+ "GetConfig",
"Identifier=" + identifier,
- "PluginName=" + CLASS_NAME,
- "EndMessage"
+ additionalParameter + "=true"
);
}
}
- public class GetPluginInfo {
-
- @Test
- public void gettingPluginInfoWorks() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo = fcpClient.getPluginInfo().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> matchGetPluginInfoMessage());
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
- }
-
- @Test
- public void gettingPluginInfoWithDetailsWorks()
- throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.getPluginInfo().detailed().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchGetPluginInfoMessage(), hasItem("Detailed=true")));
- replyWithPluginInfo();
- verifyPluginInfo(pluginInfo);
- }
+ public class ModifyConfig {
@Test
- public void protocolErrorIsRecognizedAsFailure()
+ public void defaultFcpClientCanModifyConfigData()
throws InterruptedException, ExecutionException, IOException {
- Future<Optional<PluginInfo>> pluginInfo =
- fcpClient.getPluginInfo().detailed().plugin(CLASS_NAME).execute();
- connectAndAssert(() -> allOf(matchGetPluginInfoMessage(), hasItem("Detailed=true")));
- replyWithProtocolError();
- assertThat(pluginInfo.get(), is(Optional.empty()));
- }
-
- private Matcher<List<String>> matchGetPluginInfoMessage() {
- return matchesFcpMessage(
- "GetPluginInfo",
+ Future<ConfigData> newConfigData = fcpClient.modifyConfig().set("foo.bar").to("baz").execute();
+ connectAndAssert(() -> matchesFcpMessage(
+ "ModifyConfig",
"Identifier=" + identifier,
- "PluginName=" + CLASS_NAME,
- "EndMessage"
- );
+ "foo.bar=baz"
+ ));
+ replyWithConfigData("current.foo.bar=baz");
+ assertThat(newConfigData.get().getCurrent("foo.bar"), is("baz"));
}
}
+ private void replyWithConfigData(String... additionalLines) throws IOException {
+ fcpServer.writeLine("ConfigData", "Identifier=" + identifier);
+ fcpServer.writeLine(additionalLines);
+ fcpServer.writeLine("EndMessage");
+ }
+
}
- public class UskSubscriptionCommands {
+ public class NodeInformation {
- private static final String URI = "USK@some,uri/file.txt";
+ @Test
+ public void defaultFcpClientCanGetNodeInformation() throws InterruptedException, ExecutionException, IOException {
+ Future<NodeData> nodeData = fcpClient.getNode().execute();
+ connectAndAssert(() -> matchesGetNode(false, false, false));
+ replyWithNodeData();
+ assertThat(nodeData.get(), notNullValue());
+ assertThat(nodeData.get().getNodeRef().isOpennet(), is(false));
+ }
@Test
- public void subscriptionWorks() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
- connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI, "EndMessage"));
- replyWithSubscribed();
- assertThat(uskSubscription.get().get().getUri(), is(URI));
- AtomicInteger edition = new AtomicInteger();
- CountDownLatch updated = new CountDownLatch(2);
- uskSubscription.get().get().onUpdate(e -> {
- edition.set(e);
- updated.countDown();
- });
- sendUpdateNotification(23);
- sendUpdateNotification(24);
- assertThat("updated in time", updated.await(5, TimeUnit.SECONDS), is(true));
- assertThat(edition.get(), is(24));
+ public void defaultFcpClientCanGetNodeInformationWithOpennetRef()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<NodeData> nodeData = fcpClient.getNode().opennetRef().execute();
+ connectAndAssert(() -> matchesGetNode(true, false, false));
+ replyWithNodeData("opennet=true");
+ assertThat(nodeData.get().getVersion().toString(), is("Fred,0.7,1.0,1466"));
+ assertThat(nodeData.get().getNodeRef().isOpennet(), is(true));
}
@Test
- public void subscriptionUpdatesMultipleTimes() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
- connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI, "EndMessage"));
- replyWithSubscribed();
- assertThat(uskSubscription.get().get().getUri(), is(URI));
- AtomicInteger edition = new AtomicInteger();
- CountDownLatch updated = new CountDownLatch(2);
- uskSubscription.get().get().onUpdate(e -> {
- edition.set(e);
- updated.countDown();
- });
- uskSubscription.get().get().onUpdate(e -> updated.countDown());
- sendUpdateNotification(23);
- assertThat("updated in time", updated.await(5, TimeUnit.SECONDS), is(true));
- assertThat(edition.get(), is(23));
+ public void defaultFcpClientCanGetNodeInformationWithPrivateData()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<NodeData> nodeData = fcpClient.getNode().includePrivate().execute();
+ connectAndAssert(() -> matchesGetNode(false, true, false));
+ replyWithNodeData("ark.privURI=SSK@XdHMiRl");
+ assertThat(nodeData.get().getARK().getPrivateURI(), is("SSK@XdHMiRl"));
}
@Test
- public void subscriptionCanBeCancelled() throws InterruptedException, ExecutionException, IOException {
- Future<Optional<UskSubscription>> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute();
- connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI, "EndMessage"));
- replyWithSubscribed();
- assertThat(uskSubscription.get().get().getUri(), is(URI));
- AtomicBoolean updated = new AtomicBoolean();
- uskSubscription.get().get().onUpdate(e -> updated.set(true));
- uskSubscription.get().get().cancel();
- readMessage(() -> matchesFcpMessage("UnsubscribeUSK", "Identifier=" + identifier, "EndMessage"));
- sendUpdateNotification(23);
- assertThat(updated.get(), is(false));
+ public void defaultFcpClientCanGetNodeInformationWithVolatileData()
+ throws InterruptedException, ExecutionException, IOException {
+ Future<NodeData> nodeData = fcpClient.getNode().includeVolatile().execute();
+ connectAndAssert(() -> matchesGetNode(false, false, true));
+ replyWithNodeData("volatile.freeJavaMemory=205706528");
+ assertThat(nodeData.get().getVolatile("freeJavaMemory"), is("205706528"));
}
- private void replyWithSubscribed() throws IOException {
- fcpServer.writeLine(
- "SubscribedUSK",
+ private Matcher<List<String>> matchesGetNode(boolean withOpennetRef, boolean withPrivate, boolean withVolatile) {
+ return matchesFcpMessage(
+ "GetNode",
"Identifier=" + identifier,
- "URI=" + URI,
- "DontPoll=false",
- "EndMessage"
+ "GiveOpennetRef=" + withOpennetRef,
+ "WithPrivate=" + withPrivate,
+ "WithVolatile=" + withVolatile
);
}
- private void sendUpdateNotification(int edition, String... additionalLines) throws IOException {
+ private void replyWithNodeData(String... additionalLines) throws IOException {
fcpServer.writeLine(
- "SubscribedUSKUpdate",
+ "NodeData",
"Identifier=" + identifier,
- "URI=" + URI,
- "Edition=" + edition
+ "ark.pubURI=SSK@3YEf.../ark",
+ "ark.number=78",
+ "auth.negTypes=2",
+ "version=Fred,0.7,1.0,1466",
+ "lastGoodVersion=Fred,0.7,1.0,1466"
);
fcpServer.writeLine(additionalLines);
fcpServer.writeLine("EndMessage");