Reformatting
[jFCPlib.git] / src / test / java / net / pterodactylus / fcp / quelaton / DefaultFcpClientTest.java
1 package net.pterodactylus.fcp.quelaton;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.is;
5
6 import java.io.ByteArrayInputStream;
7 import java.io.File;
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;
16
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;
22
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;
29
30 /**
31  * Unit test for {@link DefaultFcpClient}.
32  *
33  * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
34  */
35 public class DefaultFcpClientTest {
36
37         private static final String INSERT_URI =
38                 "SSK@RVCHbJdkkyTCeNN9AYukEg76eyqmiosSaNKgE3U9zUw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQECAAE/";
39         private static final String REQUEST_URI =
40                 "SSK@wtbgd2loNcJCXvtQVOftl2tuWBomDQHfqS6ytpPRhfw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQACAAE/";
41
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;
47
48         public DefaultFcpClientTest() throws IOException {
49                 fcpServer = new FakeTcpServer(threadPool);
50                 fcpClient = new DefaultFcpClient(threadPool, "localhost", fcpServer.getPort(), () -> "Test", () -> "2.0");
51         }
52
53         @After
54         public void tearDown() throws IOException {
55                 fcpServer.close();
56         }
57
58         @Test
59         public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
60                 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
61                 connectNode();
62                 fcpServer.collectUntil(is("EndMessage"));
63                 fcpServer.writeLine("SSKKeypair",
64                         "InsertURI=" + INSERT_URI + "",
65                         "RequestURI=" + REQUEST_URI + "",
66                         "Identifier=My Identifier from GenerateSSK",
67                         "EndMessage");
68                 FcpKeyPair keyPair = keyPairFuture.get();
69                 assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
70                 assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
71         }
72
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",
79                         "Testnet=false",
80                         "Version=Fred,0.7,1.0,1466",
81                         "Build=1466",
82                         "ConnectionIdentifier=14318898267048452a81b36e7f13a3f0",
83                         "Node=Fred",
84                         "ExtBuild=29",
85                         "FCPVersion=2.0",
86                         "NodeLanguage=ENGLISH",
87                         "ExtRevision=v29",
88                         "EndMessage"
89                 );
90         }
91
92         @Test
93         public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
94                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
95                 connectNode();
96                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
97                 assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
98                 String identifier = extractIdentifier(lines);
99                 fcpServer.writeLine(
100                         "AllData",
101                         "Identifier=" + identifier,
102                         "DataLength=6",
103                         "StartupTime=1435610539000",
104                         "CompletionTime=1435610540000",
105                         "Metadata.ContentType=text/plain;charset=utf-8",
106                         "Data",
107                         "Hello"
108                 );
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)));
114         }
115
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))
120                         .findFirst()
121                         .orElse("");
122         }
123
124         @Test
125         public void clientGetDownloadsDataForCorrectIdentifier()
126         throws InterruptedException, ExecutionException, IOException {
127                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
128                 connectNode();
129                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
130                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
131                 String identifier = extractIdentifier(lines);
132                 fcpServer.writeLine(
133                         "AllData",
134                         "Identifier=not-test",
135                         "DataLength=12",
136                         "StartupTime=1435610539000",
137                         "CompletionTime=1435610540000",
138                         "Metadata.ContentType=text/plain;charset=latin-9",
139                         "Data",
140                         "Hello World"
141                 );
142                 fcpServer.writeLine(
143                         "AllData",
144                         "Identifier=" + identifier,
145                         "DataLength=6",
146                         "StartupTime=1435610539000",
147                         "CompletionTime=1435610540000",
148                         "Metadata.ContentType=text/plain;charset=utf-8",
149                         "Data",
150                         "Hello"
151                 );
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)));
157         }
158
159         @Test
160         public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
161                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
162                 connectNode();
163                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
164                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
165                 String identifier = extractIdentifier(lines);
166                 fcpServer.writeLine(
167                         "GetFailed",
168                         "Identifier=" + identifier,
169                         "Code=3",
170                         "EndMessage"
171                 );
172                 Optional<Data> data = dataFuture.get();
173                 assertThat(data.isPresent(), is(false));
174         }
175
176         @Test
177         public void clientGetRecognizesGetFailedForCorrectIdentifier()
178         throws InterruptedException, ExecutionException, IOException {
179                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
180                 connectNode();
181                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
182                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
183                 String identifier = extractIdentifier(lines);
184                 fcpServer.writeLine(
185                         "GetFailed",
186                         "Identifier=not-test",
187                         "Code=3",
188                         "EndMessage"
189                 );
190                 fcpServer.writeLine(
191                         "GetFailed",
192                         "Identifier=" + identifier,
193                         "Code=3",
194                         "EndMessage"
195                 );
196                 Optional<Data> data = dataFuture.get();
197                 assertThat(data.isPresent(), is(false));
198         }
199
200         @Test
201         public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
202                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
203                 connectNode();
204                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
205                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
206                 fcpServer.close();
207                 Optional<Data> data = dataFuture.get();
208                 assertThat(data.isPresent(), is(false));
209         }
210
211         @Test
212         public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands()
213         throws InterruptedException, ExecutionException, IOException {
214                 fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt");
215                 connectNode();
216                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
217                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
218         }
219
220         @Test
221         public void clientGetWithDataStoreOnlySettingSendsCorrectCommands()
222         throws InterruptedException, ExecutionException, IOException {
223                 fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt");
224                 connectNode();
225                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
226                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
227         }
228
229         @Test
230         public void clientGetWithMaxSizeSettingSendsCorrectCommands()
231         throws InterruptedException, ExecutionException, IOException {
232                 fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt");
233                 connectNode();
234                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
235                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
236         }
237
238         @Test
239         public void clientGetWithPrioritySettingSendsCorrectCommands()
240         throws InterruptedException, ExecutionException, IOException {
241                 fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt");
242                 connectNode();
243                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
244                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
245         }
246
247         @Test
248         public void clientGetWithRealTimeSettingSendsCorrectCommands()
249         throws InterruptedException, ExecutionException, IOException {
250                 fcpClient.clientGet().realTime().uri("KSK@foo.txt");
251                 connectNode();
252                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
253                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
254         }
255
256         @Test
257         public void clientGetWithGlobalSettingSendsCorrectCommands()
258         throws InterruptedException, ExecutionException, IOException {
259                 fcpClient.clientGet().global().uri("KSK@foo.txt");
260                 connectNode();
261                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
262                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
263         }
264
265         private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
266                 return new TypeSafeDiagnosingMatcher<List<String>>() {
267                         @Override
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));
271                                         return false;
272                                 }
273                                 for (String requiredLine : requiredLines) {
274                                         if (item.indexOf(requiredLine) < 1) {
275                                                 mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
276                                                 return false;
277                                         }
278                                 }
279                                 return true;
280                         }
281
282                         @Override
283                         public void describeTo(Description description) {
284                                 description.appendText("FCP message named ").appendValue(name);
285                                 description.appendValueList(", containing the lines ", ", ", "", requiredLines);
286                         }
287                 };
288         }
289
290         @Test
291         public void clientPutWithDirectDataSendsCorrectCommand()
292         throws IOException, ExecutionException, InterruptedException {
293                 fcpClient.clientPut()
294                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
295                         .length(6)
296                         .key(new Key("KSK@foo.txt"));
297                 connectNode();
298                 List<String> lines = fcpServer.collectUntil(is("Hello"));
299                 assertThat(lines, matchesFcpMessage("ClientPut", "UploadFrom=direct", "DataLength=6", "URI=KSK@foo.txt"));
300         }
301
302         @Test
303         public void clientPutWithDirectDataSucceedsOnCorrectIdentifier()
304         throws InterruptedException, ExecutionException, IOException {
305                 Future<Optional<Key>> key = fcpClient.clientPut()
306                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
307                         .length(6)
308                         .key(new Key("KSK@foo.txt"));
309                 connectNode();
310                 List<String> lines = fcpServer.collectUntil(is("Hello"));
311                 String identifier = extractIdentifier(lines);
312                 fcpServer.writeLine(
313                         "PutFailed",
314                         "Identifier=not-the-right-one",
315                         "EndMessage"
316                 );
317                 fcpServer.writeLine(
318                         "PutSuccessful",
319                         "URI=KSK@foo.txt",
320                         "Identifier=" + identifier,
321                         "EndMessage"
322                 );
323                 assertThat(key.get().get().getKey(), is("KSK@foo.txt"));
324         }
325
326         @Test
327         public void clientPutWithDirectDataFailsOnCorrectIdentifier()
328         throws InterruptedException, ExecutionException, IOException {
329                 Future<Optional<Key>> key = fcpClient.clientPut()
330                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
331                         .length(6)
332                         .key(new Key("KSK@foo.txt"));
333                 connectNode();
334                 List<String> lines = fcpServer.collectUntil(is("Hello"));
335                 String identifier = extractIdentifier(lines);
336                 fcpServer.writeLine(
337                         "PutSuccessful",
338                         "Identifier=not-the-right-one",
339                         "URI=KSK@foo.txt",
340                         "EndMessage"
341                 );
342                 fcpServer.writeLine(
343                         "PutFailed",
344                         "Identifier=" + identifier,
345                         "EndMessage"
346                 );
347                 assertThat(key.get().isPresent(), is(false));
348         }
349
350         @Test
351         public void clientPutWithRenamedDirectDataSendsCorrectCommand()
352         throws InterruptedException, ExecutionException, IOException {
353                 fcpClient.clientPut()
354                         .named("otherName.txt")
355                         .from(new ByteArrayInputStream("Hello\n".getBytes()))
356                         .length(6)
357                         .key(new Key("KSK@foo.txt"));
358                 connectNode();
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"));
362         }
363
364         @Test
365         public void clientPutWithRedirectSendsCorrectCommand()
366         throws IOException, ExecutionException, InterruptedException {
367                 fcpClient.clientPut().redirectTo(new Key("KSK@bar.txt")).key(new Key("KSK@foo.txt"));
368                 connectNode();
369                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
370                 assertThat(lines,
371                         matchesFcpMessage("ClientPut", "UploadFrom=redirect", "URI=KSK@foo.txt", "TargetURI=KSK@bar.txt"));
372         }
373
374         @Test
375         public void clientPutWithFileSendsCorrectCommand() throws InterruptedException, ExecutionException, IOException {
376                 fcpClient.clientPut().from(new File("/tmp/data.txt")).key(new Key("KSK@foo.txt"));
377                 connectNode();
378                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
379                 assertThat(lines,
380                         matchesFcpMessage("ClientPut", "UploadFrom=disk", "URI=KSK@foo.txt", "Filename=/tmp/data.txt"));
381         }
382
383 }