Add missing space
[jFCPlib.git] / src / test / java / net / pterodactylus / fcp / quelaton / DefaultFcpClientTest.java
1 package net.pterodactylus.fcp.quelaton;
2
3 import static org.hamcrest.MatcherAssert.assertThat;
4 import static org.hamcrest.Matchers.containsInAnyOrder;
5 import static org.hamcrest.Matchers.is;
6
7 import java.io.IOException;
8 import java.nio.charset.StandardCharsets;
9 import java.util.List;
10 import java.util.Optional;
11 import java.util.concurrent.ExecutionException;
12 import java.util.concurrent.ExecutorService;
13 import java.util.concurrent.Executors;
14 import java.util.concurrent.Future;
15
16 import net.pterodactylus.fcp.FcpKeyPair;
17 import net.pterodactylus.fcp.Priority;
18 import net.pterodactylus.fcp.fake.FakeTcpServer;
19 import net.pterodactylus.fcp.quelaton.ClientGetCommand.Data;
20
21 import com.google.common.io.ByteStreams;
22 import org.hamcrest.Description;
23 import org.hamcrest.Matcher;
24 import org.hamcrest.TypeSafeDiagnosingMatcher;
25 import org.junit.After;
26 import org.junit.Test;
27
28 /**
29  * Unit test for {@link DefaultFcpClient}.
30  *
31  * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
32  */
33 public class DefaultFcpClientTest {
34
35         private static final String INSERT_URI = "SSK@RVCHbJdkkyTCeNN9AYukEg76eyqmiosSaNKgE3U9zUw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQECAAE/";
36         private static final String REQUEST_URI = "SSK@wtbgd2loNcJCXvtQVOftl2tuWBomDQHfqS6ytpPRhfw,7SHH53gletBVb9JD7nBsyClbLQsBubDPEIcwg908r7Y,AQACAAE/";
37
38         private static int threadCounter = 0;
39         private final ExecutorService threadPool = Executors.newCachedThreadPool((r) -> new Thread(r, "Test-Thread-" + threadCounter++));
40         private final FakeTcpServer fcpServer;
41         private final DefaultFcpClient fcpClient;
42
43         public DefaultFcpClientTest() throws IOException {
44                 fcpServer = new FakeTcpServer(threadPool);
45                 fcpClient = new DefaultFcpClient(threadPool, "localhost", fcpServer.getPort(), () -> "Test", () -> "2.0");
46         }
47
48         @After
49         public void tearDown() throws IOException {
50                 fcpServer.close();
51         }
52
53         @Test
54         public void defaultFcpClientCanGenerateKeypair() throws ExecutionException, InterruptedException, IOException {
55                 Future<FcpKeyPair> keyPairFuture = fcpClient.generateKeypair().execute();
56                 connectNode();
57                 fcpServer.collectUntil(is("EndMessage"));
58                 fcpServer.writeLine("SSKKeypair",
59                         "InsertURI=" + INSERT_URI + "",
60                         "RequestURI=" + REQUEST_URI + "",
61                         "Identifier=My Identifier from GenerateSSK",
62                         "EndMessage");
63                 FcpKeyPair keyPair = keyPairFuture.get();
64                 assertThat(keyPair.getPublicKey(), is(REQUEST_URI));
65                 assertThat(keyPair.getPrivateKey(), is(INSERT_URI));
66         }
67
68         private void connectNode() throws InterruptedException, ExecutionException, IOException {
69                 fcpServer.connect().get();
70                 fcpServer.collectUntil(is("EndMessage"));
71                 fcpServer.writeLine("NodeHello",
72                         "CompressionCodecs=4 - GZIP(0), BZIP2(1), LZMA(2), LZMA_NEW(3)",
73                         "Revision=build01466",
74                         "Testnet=false",
75                         "Version=Fred,0.7,1.0,1466",
76                         "Build=1466",
77                         "ConnectionIdentifier=14318898267048452a81b36e7f13a3f0",
78                         "Node=Fred",
79                         "ExtBuild=29",
80                         "FCPVersion=2.0",
81                         "NodeLanguage=ENGLISH",
82                         "ExtRevision=v29",
83                         "EndMessage"
84                 );
85         }
86
87         @Test
88         public void clientGetCanDownloadData() throws InterruptedException, ExecutionException, IOException {
89                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
90                 connectNode();
91                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
92                 assertThat(lines, matchesFcpMessage("ClientGet", "ReturnType=direct", "URI=KSK@foo.txt"));
93                 String identifier = extractIdentifier(lines);
94                 fcpServer.writeLine(
95                         "AllData",
96                         "Identifier=" + identifier,
97                         "DataLength=6",
98                         "StartupTime=1435610539000",
99                         "CompletionTime=1435610540000",
100                         "Metadata.ContentType=text/plain;charset=utf-8",
101                         "Data",
102                         "Hello"
103                 );
104                 Optional<Data> data = dataFuture.get();
105                 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
106                 assertThat(data.get().size(), is(6L));
107                 assertThat(ByteStreams.toByteArray(data.get().getInputStream()), is("Hello\n".getBytes(StandardCharsets.UTF_8)));
108         }
109
110         private String extractIdentifier(List<String> lines) {
111                 return lines.stream().filter(s -> s.startsWith("Identifier=")).map(s -> s.substring(s.indexOf('=') + 1)).findFirst().orElse("");
112         }
113
114         @Test
115         public void clientGetDownloadsDataForCorrectIdentifier() throws InterruptedException, ExecutionException, IOException {
116                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
117                 connectNode();
118                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
119                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
120                 String identifier = extractIdentifier(lines);
121                 fcpServer.writeLine(
122                         "AllData",
123                         "Identifier=not-test",
124                         "DataLength=12",
125                         "StartupTime=1435610539000",
126                         "CompletionTime=1435610540000",
127                         "Metadata.ContentType=text/plain;charset=latin-9",
128                         "Data",
129                         "Hello World"
130                 );
131                 fcpServer.writeLine(
132                         "AllData",
133                         "Identifier=" + identifier,
134                         "DataLength=6",
135                         "StartupTime=1435610539000",
136                         "CompletionTime=1435610540000",
137                         "Metadata.ContentType=text/plain;charset=utf-8",
138                         "Data",
139                         "Hello"
140                 );
141                 Optional<Data> data = dataFuture.get();
142                 assertThat(data.get().getMimeType(), is("text/plain;charset=utf-8"));
143                 assertThat(data.get().size(), is(6L));
144                 assertThat(ByteStreams.toByteArray(data.get().getInputStream()), is("Hello\n".getBytes(StandardCharsets.UTF_8)));
145         }
146
147         @Test
148         public void clientGetRecognizesGetFailed() throws InterruptedException, ExecutionException, IOException {
149                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
150                 connectNode();
151                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
152                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
153                 String identifier = extractIdentifier(lines);
154                 fcpServer.writeLine(
155                         "GetFailed",
156                         "Identifier=" + identifier,
157                         "Code=3",
158                         "EndMessage"
159                 );
160                 Optional<Data> data = dataFuture.get();
161                 assertThat(data.isPresent(), is(false));
162         }
163
164         @Test
165         public void clientGetRecognizesGetFailedForCorrectIdentifier() throws InterruptedException, ExecutionException, IOException {
166                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
167                 connectNode();
168                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
169                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
170                 String identifier = extractIdentifier(lines);
171                 fcpServer.writeLine(
172                         "GetFailed",
173                         "Identifier=not-test",
174                         "Code=3",
175                         "EndMessage"
176                 );
177                 fcpServer.writeLine(
178                         "GetFailed",
179                         "Identifier=" + identifier,
180                         "Code=3",
181                         "EndMessage"
182                 );
183                 Optional<Data> data = dataFuture.get();
184                 assertThat(data.isPresent(), is(false));
185         }
186
187         @Test
188         public void clientGetRecognizesConnectionClosed() throws InterruptedException, ExecutionException, IOException {
189                 Future<Optional<Data>> dataFuture = fcpClient.clientGet().uri("KSK@foo.txt");
190                 connectNode();
191                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
192                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt"));
193                 fcpServer.close();
194                 Optional<Data> data = dataFuture.get();
195                 assertThat(data.isPresent(), is(false));
196         }
197
198         @Test
199         public void clientGetWithIgnoreDataStoreSettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
200                 fcpClient.clientGet().ignoreDataStore().uri("KSK@foo.txt");
201                 connectNode();
202                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
203                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "IgnoreDS=true"));
204         }
205
206         @Test
207         public void clientGetWithDataStoreOnlySettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
208                 fcpClient.clientGet().dataStoreOnly().uri("KSK@foo.txt");
209                 connectNode();
210                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
211                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "DSonly=true"));
212         }
213
214         @Test
215         public void clientGetWithMaxSizeSettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
216                 fcpClient.clientGet().maxSize(1048576).uri("KSK@foo.txt");
217                 connectNode();
218                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
219                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "MaxSize=1048576"));
220         }
221
222         @Test
223         public void clientGetWithPrioritySettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
224                 fcpClient.clientGet().priority(Priority.interactive).uri("KSK@foo.txt");
225                 connectNode();
226                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
227                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "PriorityClass=1"));
228         }
229
230         @Test
231         public void clientGetWithRealTimeSettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
232                 fcpClient.clientGet().realTime().uri("KSK@foo.txt");
233                 connectNode();
234                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
235                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "RealTimeFlag=true"));
236         }
237
238         @Test
239         public void clientGetWithGlobalSettingSendsCorrectCommands() throws InterruptedException, ExecutionException, IOException {
240                 fcpClient.clientGet().global().uri("KSK@foo.txt");
241                 connectNode();
242                 List<String> lines = fcpServer.collectUntil(is("EndMessage"));
243                 assertThat(lines, matchesFcpMessage("ClientGet", "URI=KSK@foo.txt", "Global=true"));
244         }
245
246         private Matcher<List<String>> matchesFcpMessage(String name, String... requiredLines) {
247                 return new TypeSafeDiagnosingMatcher<List<String>>() {
248                         @Override
249                         protected boolean matchesSafely(List<String> item, Description mismatchDescription) {
250                                 if (!item.get(0).equals(name)) {
251                                         mismatchDescription.appendText("FCP message is named ").appendValue(item.get(0));
252                                         return false;
253                                 }
254                                 for (String requiredLine : requiredLines) {
255                                         if (item.indexOf(requiredLine) < 1) {
256                                                 mismatchDescription.appendText("FCP message does not contain ").appendValue(requiredLine);
257                                                 return false;
258                                         }
259                                 }
260                                 return true;
261                         }
262
263                         @Override
264                         public void describeTo(Description description) {
265                                 description.appendText("FCP message named ").appendValue(name);
266                                 description.appendValueList(", containing the lines ", ", ", "", requiredLines);
267                         }
268                 };
269         }
270
271 }