Explicitely set initial state of FCP interface.
[Sone.git] / src / test / java / net / pterodactylus / sone / fcp / FcpInterfaceTest.java
1 /*
2  * Sone - FcpInterfaceTest.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.fcp;
19
20 import static freenet.pluginmanager.FredPluginFCP.ACCESS_DIRECT;
21 import static freenet.pluginmanager.FredPluginFCP.ACCESS_FCP_FULL;
22 import static freenet.pluginmanager.FredPluginFCP.ACCESS_FCP_RESTRICTED;
23 import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS;
24 import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO;
25 import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING;
26 import static org.hamcrest.MatcherAssert.assertThat;
27 import static org.hamcrest.Matchers.hasSize;
28 import static org.hamcrest.Matchers.is;
29 import static org.hamcrest.Matchers.notNullValue;
30 import static org.mockito.Mockito.mock;
31
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.util.List;
35
36 import net.pterodactylus.sone.core.Core;
37 import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
38 import net.pterodactylus.sone.freenet.fcp.FcpException;
39
40 import freenet.pluginmanager.PluginNotFoundException;
41 import freenet.pluginmanager.PluginReplySender;
42 import freenet.support.SimpleFieldSet;
43 import freenet.support.api.Bucket;
44 import freenet.support.io.ArrayBucket;
45
46 import com.google.common.collect.Lists;
47 import org.hamcrest.Description;
48 import org.hamcrest.Matcher;
49 import org.hamcrest.TypeSafeMatcher;
50 import org.junit.Test;
51
52 /**
53  * Unit test for {@link FcpInterface}.
54  *
55  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
56  */
57 public class FcpInterfaceTest {
58
59         private final Core core = mock(Core.class);
60         private final FcpInterface fcpInterface = new FcpInterface(core);
61         private final CapturingPluginReplySender pluginReplySender = new CapturingPluginReplySender();
62
63         public FcpInterfaceTest() {
64                 fcpInterface.setActive(false);
65                 fcpInterface.setFullAccessRequired(ALWAYS);
66         }
67
68         @Test
69         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForDirectAccess() throws PluginNotFoundException {
70                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
71                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
72                 verifyErrorWithCode("400");
73         }
74
75         @Test
76         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForRestrictedFcpAccess() throws PluginNotFoundException {
77                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
78                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
79                 verifyErrorWithCode("400");
80         }
81
82         @Test
83         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForFullFcpAccess() throws PluginNotFoundException {
84                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
85                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
86                 verifyErrorWithCode("400");
87         }
88
89         @Test
90         public void testThatAnActiveFcpInterfaceReturnsAnErrorForAnUnknownMessage() {
91                 fcpInterface.setActive(true);
92                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Foo").get();
93                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
94                 verifyError();
95         }
96
97         @Test
98         public void testThatAnActiveFcpInterfaceReturnsAnErrorForAMessageWithoutIdentifier() {
99                 fcpInterface.setActive(true);
100                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
101                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").get();
102                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
103                 verifyError();
104         }
105
106         @Test
107         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
108                 fcpInterface.setActive(true);
109                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
110                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
111                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
112                 verifyReplyWithMessage("ReadOnlyPong");
113         }
114
115         @Test
116         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadOnlyCommand() {
117                 fcpInterface.setActive(true);
118                 fcpInterface.setFullAccessRequired(WRITING);
119                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
120                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
121                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
122                 verifyReplyWithMessage("ReadOnlyPong");
123         }
124
125         @Test
126         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
127                 fcpInterface.setActive(true);
128                 fcpInterface.setFullAccessRequired(NO);
129                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
130                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
131                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
132                 verifyReplyWithMessage("ReadOnlyPong");
133         }
134
135         @Test
136         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
137                 fcpInterface.setActive(true);
138                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
139                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
140                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
141                 verifyReplyWithMessage("ReadWritePong");
142         }
143
144         @Test
145         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadWriteCommand() {
146                 fcpInterface.setActive(true);
147                 fcpInterface.setFullAccessRequired(WRITING);
148                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
149                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
150                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
151                 verifyReplyWithMessage("ReadWritePong");
152         }
153
154         @Test
155         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
156                 fcpInterface.setActive(true);
157                 fcpInterface.setFullAccessRequired(NO);
158                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
159                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
160                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
161                 verifyReplyWithMessage("ReadWritePong");
162         }
163
164         @Test
165         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
166                 fcpInterface.setActive(true);
167                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
168                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
169                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
170                 verifyReplyWithMessage("ReadOnlyPong");
171         }
172
173         @Test
174         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadOnlyCommand() {
175                 fcpInterface.setActive(true);
176                 fcpInterface.setFullAccessRequired(WRITING);
177                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
178                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
179                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
180                 verifyReplyWithMessage("ReadOnlyPong");
181         }
182
183         @Test
184         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
185                 fcpInterface.setActive(true);
186                 fcpInterface.setFullAccessRequired(NO);
187                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
188                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
189                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
190                 verifyReplyWithMessage("ReadOnlyPong");
191         }
192
193         private void verifyReplyWithMessage(String messageName) {
194                 assertThat(pluginReplySender.results, hasSize(1));
195                 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
196                 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is(messageName));
197         }
198
199         @Test
200         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
201                 fcpInterface.setActive(true);
202                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
203                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
204                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
205                 verifyReplyWithMessage("ReadWritePong");
206         }
207
208         @Test
209         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadWriteCommand() {
210                 fcpInterface.setActive(true);
211                 fcpInterface.setFullAccessRequired(WRITING);
212                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
213                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
214                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
215                 verifyReplyWithMessage("ReadWritePong");
216         }
217
218         @Test
219         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
220                 fcpInterface.setActive(true);
221                 fcpInterface.setFullAccessRequired(NO);
222                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
223                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
224                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
225                 verifyReplyWithMessage("ReadWritePong");
226         }
227
228         @Test
229         public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadOnlyCommand() {
230                 fcpInterface.setActive(true);
231                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
232                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
233                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
234                 verifyErrorWithCode("401");
235         }
236
237         @Test
238         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsRestrictedFcpAccessForReadOnlyCommand() {
239                 fcpInterface.setActive(true);
240                 fcpInterface.setFullAccessRequired(WRITING);
241                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
242                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
243                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
244                 verifyReplyWithMessage("ReadOnlyPong");
245         }
246
247         @Test
248         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadOnlyCommand() {
249                 fcpInterface.setActive(true);
250                 fcpInterface.setFullAccessRequired(NO);
251                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
252                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
253                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
254                 verifyReplyWithMessage("ReadOnlyPong");
255         }
256
257         @Test
258         public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
259                 fcpInterface.setActive(true);
260                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
261                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
262                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
263                 verifyErrorWithCode("401");
264         }
265
266         @Test
267         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
268                 fcpInterface.setActive(true);
269                 fcpInterface.setFullAccessRequired(WRITING);
270                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
271                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
272                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
273                 verifyErrorWithCode("401");
274         }
275
276         @Test
277         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadWriteCommand() {
278                 fcpInterface.setActive(true);
279                 fcpInterface.setFullAccessRequired(NO);
280                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
281                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
282                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
283                 verifyReplyWithMessage("ReadWritePong");
284         }
285
286         @Test
287         public void testThatAFaultyCommandResultsInAnError() {
288                 fcpInterface.setActive(true);
289                 fcpInterface.addCommand("Faulty", new FaultyCommand());
290                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
291                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
292                 verifyError();
293         }
294
295         @Test
296         public void testThatAFaultyPluginReplySenderIsHandled() {
297                 fcpInterface.setActive(true);
298                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
299                 fcpInterface.handle(new FaultyPluginReplySender(), fieldSet, null, ACCESS_FCP_FULL);
300         }
301
302         @Test
303         public void testThatACommandWithDataIsHandledCorrectly() throws IOException {
304                 fcpInterface.setActive(true);
305                 fcpInterface.addCommand("CommandWithData", new CommandWithData());
306                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithData").put("Identifier", "foo").get();
307                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
308                 verifyReplyWithMessage("ReturnedData");
309                 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
310                 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
311                 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 1, 2, 3 }));
312         }
313
314         @Test
315         public void testThatACommandWithABucketIsHandledCorrectly() throws IOException {
316                 fcpInterface.setActive(true);
317                 fcpInterface.addCommand("CommandWithBucket", new CommandWithBucket());
318                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithBucket").put("Identifier", "foo").get();
319                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
320                 verifyReplyWithMessage("ReturnedBucket");
321                 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
322                 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
323                 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 4, 5, 6 }));
324         }
325
326         private Matcher<InputStream> delivers(final byte[] data) {
327                 return new TypeSafeMatcher<InputStream>() {
328                         byte[] readData = new byte[data.length];
329
330                         @Override
331                         protected boolean matchesSafely(InputStream inputStream) {
332                                 int offset = 0;
333                                 try {
334                                         while (true) {
335                                                 int r = inputStream.read();
336                                                 if (r == -1) {
337                                                         return offset == data.length;
338                                                 }
339                                                 readData[offset] = (byte) r;
340                                                 if (data[offset++] != r) {
341                                                         return false;
342                                                 }
343                                         }
344                                 } catch (IOException ioe1) {
345                                         return false;
346                                 }
347                         }
348
349                         @Override
350                         public void describeTo(Description description) {
351                                 description.appendValue(data);
352                         }
353
354                         @Override
355                         protected void describeMismatchSafely(InputStream item, Description mismatchDescription) {
356                                 mismatchDescription.appendValue(readData);
357                         }
358                 };
359         }
360
361         private void verifyError() {
362                 assertThat(pluginReplySender.results, hasSize(1));
363                 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
364                 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is("Error"));
365         }
366
367         private void verifyErrorWithCode(String errorCode) {
368                 verifyError();
369                 assertThat(pluginReplySender.results.get(0).fieldSet.get("ErrorCode"), is(errorCode));
370         }
371
372         private static class CapturingPluginReplySender extends PluginReplySender {
373
374                 public final List<PluginReplySenderResult> results = Lists.newArrayList();
375
376                 public CapturingPluginReplySender() {
377                         super(null, null);
378                 }
379
380                 @Override
381                 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
382                         results.add(new PluginReplySenderResult(params, bucket));
383                 }
384
385         }
386
387         private static class PluginReplySenderResult {
388
389                 public final SimpleFieldSet fieldSet;
390                 public final Bucket bucket;
391
392                 public PluginReplySenderResult(SimpleFieldSet fieldSet, Bucket bucket) {
393                         this.fieldSet = fieldSet;
394                         this.bucket = bucket;
395                 }
396
397         }
398
399         private static class ReadOnlyPing extends AbstractSoneCommand {
400
401                 public ReadOnlyPing() {
402                         super(null, false);
403                 }
404
405                 @Override
406                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
407                         return new Response("ReadOnlyPong", new SimpleFieldSetBuilder().get());
408                 }
409
410         }
411
412         private static class ReadWritePing extends AbstractSoneCommand {
413
414                 public ReadWritePing() {
415                         super(null, true);
416                 }
417
418                 @Override
419                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
420                         return new Response("ReadWritePong", new SimpleFieldSetBuilder().get());
421                 }
422
423         }
424
425         private static class FaultyCommand extends AbstractSoneCommand {
426
427                 public FaultyCommand() {
428                         super(null, false);
429                 }
430
431                 @Override
432                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
433                         throw new RuntimeException("I’m faulty!");
434                 }
435
436         }
437
438         private static class FaultyPluginReplySender extends PluginReplySender {
439
440                 public FaultyPluginReplySender() {
441                         super(null, null);
442                 }
443
444                 @Override
445                 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
446                         throw new PluginNotFoundException();
447                 }
448
449         }
450
451         private static class CommandWithData extends AbstractSoneCommand {
452
453                 protected CommandWithData() {
454                         super(null);
455                 }
456
457                 @Override
458                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
459                         return new Response("ReturnedData", new SimpleFieldSetBuilder().get(), new byte[] { 1, 2, 3 });
460                 }
461
462         }
463
464         private static class CommandWithBucket extends AbstractSoneCommand {
465
466                 protected CommandWithBucket() {
467                         super(null);
468                 }
469
470                 @Override
471                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
472                         return new Response("ReturnedBucket", new SimpleFieldSetBuilder().get(), new ArrayBucket(new byte[] { 4, 5, 6 }));
473                 }
474
475         }
476
477 }