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 org.hamcrest.Description;
25 import org.hamcrest.Matcher;
26 import org.hamcrest.TypeSafeDiagnosingMatcher;
27 import org.junit.After;
28 import org.junit.Test;
31 * Unit test for {@link DefaultFcpClient}.
33 * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
35 public class DefaultFcpClientTest {
37 private static final String INSERT_URI =
38 "SSK@RVCHbJdkkyTCeNN9AYukEg76eyqmiosSaNKgE3U9zUw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQECAAE/";
39 private static final String REQUEST_URI =
40 "SSK@wtbgd2loNcJCXvtQVOftl2tuWBomDQHfqS6ytpPRhfw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQACAAE/";
42 private static int threadCounter = 0;
43 private final ExecutorService threadPool =
44 Executors.newCachedThreadPool(r -> new Thread(r, "Test-Thread-" + threadCounter++));
45 private final FakeTcpServer fcpServer;
46 private final DefaultFcpClient fcpClient;
48 public DefaultFcpClientTest() throws IOException {
49 fcpServer = new FakeTcpServer(threadPool);
50 fcpClient = new DefaultFcpClient(threadPool, "localhost", fcpServer.getPort(), () -> "Test", () -> "2.0");
54 public void tearDown() throws IOException {
59 public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
60 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
62 fcpServer.collectUntil(is("EndMessage"));
63 fcpServer.writeLine("SSKKeypair",
64 "InsertURI=" + INSERT_URI + "",
65 "RequestURI=" + REQUEST_URI + "",
66 "Identifier=My Identifier from GenerateSSK",
68 FcpKeyPair keyPair = keyPairFuture.get();
69 assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
70 assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
73 private void connectNode() throws InterruptedException, ExecutionException, IOException {
74 fcpServer.connect().get();
75 fcpServer.collectUntil(is("EndMessage"));
76 fcpServer.writeLine("NodeHello",
77 "CompressionCodecs=4 - GZIP(0), BZIP2(1), LZMA(2), LZMA_NEW(3)",
78 "Revision=build01466",
80 "Version=Fred,0.7,1.0,1466",
82 "ConnectionIdentifier=14318898267048452a81b36e7f13a3f0",
86 "NodeLanguage=ENGLISH",
93 public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
94 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
96 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
97 assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
98 String identifier = extractIdentifier(lines);
101 "Identifier=" + identifier,
103 "StartupTime=1435610539000",
104 "CompletionTime=1435610540000",
105 "Metadata.ContentType=text/plain;charset=utf-8",
109 Optional<Data> data = dataFuture.get();
110 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
111 assertThat(data.get().size(), is(6L));
112 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
113 is("Hello\n".getBytes(StandardCharsets.UTF_8)));
116 private String extractIdentifier(List<String> lines) {
117 return lines.stream()
118 .filter(s -> s.startsWith("Identifier="))
119 .map(s -> s.substring(s.indexOf('=') + 1))
125 public void clientGetDownloadsDataForCorrectIdentifier()
126 throws InterruptedException, ExecutionException, IOException {
127 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
129 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
130 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
131 String identifier = extractIdentifier(lines);
134 "Identifier=not-test",
136 "StartupTime=1435610539000",
137 "CompletionTime=1435610540000",
138 "Metadata.ContentType=text/plain;charset=latin-9",
144 "Identifier=" + identifier,
146 "StartupTime=1435610539000",
147 "CompletionTime=1435610540000",
148 "Metadata.ContentType=text/plain;charset=utf-8",
152 Optional<Data> data = dataFuture.get();
153 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
154 assertThat(data.get().size(), is(6L));
155 assertThat(ByteStreams.toByteArray(data.get().getInputStream()),
156 is("Hello\n".getBytes(StandardCharsets.UTF_8)));
160 public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
161 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
163 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
164 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
165 String identifier = extractIdentifier(lines);
168 "Identifier=" + identifier,
172 Optional<Data> data = dataFuture.get();
173 assertThat(data.isPresent(), is(false));
177 public void clientGetRecognizesGetFailedForCorrectIdentifier()
178 throws InterruptedException, ExecutionException, IOException {
179 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
181 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
182 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
183 String identifier = extractIdentifier(lines);
186 "Identifier=not-test",
192 "Identifier=" + identifier,
196 Optional<Data> data = dataFuture.get();
197 assertThat(data.isPresent(), is(false));
201 public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
202 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
204 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
205 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
207 Optional<Data> data = dataFuture.get();
208 assertThat(data.isPresent(), is(false));
212 public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands()
213 throws InterruptedException, ExecutionException, IOException {
214 fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt");
216 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
217 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
221 public void clientGetWithDataStoreOnlySettingSendsCorrectCommands()
222 throws InterruptedException, ExecutionException, IOException {
223 fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt");
225 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
226 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
230 public void clientGetWithMaxSizeSettingSendsCorrectCommands()
231 throws InterruptedException, ExecutionException, IOException {
232 fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt");
234 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
235 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
239 public void clientGetWithPrioritySettingSendsCorrectCommands()
240 throws InterruptedException, ExecutionException, IOException {
241 fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt");
243 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
244 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
248 public void clientGetWithRealTimeSettingSendsCorrectCommands()
249 throws InterruptedException, ExecutionException, IOException {
250 fcpClient.clientGet().realTime().uri("KSK@foo.txt");
252 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
253 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
257 public void clientGetWithGlobalSettingSendsCorrectCommands()
258 throws InterruptedException, ExecutionException, IOException {
259 fcpClient.clientGet().global().uri("KSK@foo.txt");
261 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
262 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
265 private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
266 return new TypeSafeDiagnosingMatcher<List<String>>() {
268 protected boolean matchesSafely(List<String> item, Description mismatchDescription) {
269 if (!item.get(0).equals(name)) {
270 mismatchDescription.appendText("FCP message is named ").appendValue(item.get(0));
273 for (String requiredLine : requiredLines) {
274 if (item.indexOf(requiredLine) < 1) {
275 mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
283 public void describeTo(Description description) {
284 description.appendText("FCP message named ").appendValue(name);
285 description.appendValueList(", containing the lines ", ", ", "", requiredLines);
291 public void clientPutWithDirectDataSendsCorrectCommand()
292 throws IOException, ExecutionException, InterruptedException {
293 fcpClient.clientPut()
294 .from(new ByteArrayInputStream("Hello\n".getBytes()))
296 .key(new Key("KSK@foo.txt"));
298 List<String> lines = fcpServer.collectUntil(is("Hello"));
299 assertThat(lines, matchesFcpMessage("ClientPut", "UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
303 public void clientPutWithDirectDataSucceedsOnCorrectIdentifier()
304 throws InterruptedException, ExecutionException, IOException {
305 Future<Optional<Key>> key = fcpClient.clientPut()
306 .from(new ByteArrayInputStream("Hello\n".getBytes()))
308 .key(new Key("KSK@foo.txt"));
310 List<String> lines = fcpServer.collectUntil(is("Hello"));
311 String identifier = extractIdentifier(lines);
314 "Identifier=not-the-right-one",
320 "Identifier=" + identifier,
323 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
327 public void clientPutWithDirectDataFailsOnCorrectIdentifier()
328 throws InterruptedException, ExecutionException, IOException {
329 Future<Optional<Key>> key = fcpClient.clientPut()
330 .from(new ByteArrayInputStream("Hello\n".getBytes()))
332 .key(new Key("KSK@foo.txt"));
334 List<String> lines = fcpServer.collectUntil(is("Hello"));
335 String identifier = extractIdentifier(lines);
338 "Identifier=not-the-right-one",
344 "Identifier=" + identifier,
347 assertThat(key.get().isPresent(), is(false));
351 public void clientPutWithRenamedDirectDataSendsCorrectCommand()
352 throws InterruptedException, ExecutionException, IOException {
353 fcpClient.clientPut()
354 .named("otherName.txt")
355 .from(new ByteArrayInputStream("Hello\n".getBytes()))
357 .key(new Key("KSK@foo.txt"));
359 List<String> lines = fcpServer.collectUntil(is("Hello"));
360 assertThat(lines, matchesFcpMessage("ClientPut", "TargetFilename=otherName.txt", "UploadFrom=direct",
361 "DataLength=6", "URI=KSK@foo.txt"));
365 public void clientPutWithRedirectSendsCorrectCommand()
366 throws IOException, ExecutionException, InterruptedException {
367 fcpClient.clientPut().redirectTo(new Key("KSK@bar.txt")).key(new Key("KSK@foo.txt"));
369 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
371 matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
375 public void clientPutWithFileSendsCorrectCommand() throws InterruptedException, ExecutionException, IOException {
376 fcpClient.clientPut().from(new File("/tmp/data.txt")).key(new Key("KSK@foo.txt"));
378 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
380 matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));