12b820defd95cee9f9185346733a688bd5232191
[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(true);
65                 fcpInterface.setFullAccessRequired(ALWAYS);
66         }
67
68         @Test
69         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForDirectAccess() throws PluginNotFoundException {
70                 fcpInterface.setActive(false);
71                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
72                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
73                 verifyErrorWithCode("400");
74         }
75
76         @Test
77         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForRestrictedFcpAccess() throws PluginNotFoundException {
78                 fcpInterface.setActive(false);
79                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
80                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
81                 verifyErrorWithCode("400");
82         }
83
84         @Test
85         public void testThatAnInactiveFcpInterfaceReturnsAnErrorForFullFcpAccess() throws PluginNotFoundException {
86                 fcpInterface.setActive(false);
87                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
88                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
89                 verifyErrorWithCode("400");
90         }
91
92         @Test
93         public void testThatAnActiveFcpInterfaceReturnsAnErrorForAnUnknownMessage() {
94                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Foo").get();
95                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
96                 verifyError();
97         }
98
99         @Test
100         public void testThatAnActiveFcpInterfaceReturnsAnErrorForAMessageWithoutIdentifier() {
101                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
102                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").get();
103                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
104                 verifyError();
105         }
106
107         @Test
108         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
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.setFullAccessRequired(WRITING);
118                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
119                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
120                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
121                 verifyReplyWithMessage("ReadOnlyPong");
122         }
123
124         @Test
125         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
126                 fcpInterface.setFullAccessRequired(NO);
127                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
128                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
129                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
130                 verifyReplyWithMessage("ReadOnlyPong");
131         }
132
133         @Test
134         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
135                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
136                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
137                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
138                 verifyReplyWithMessage("ReadWritePong");
139         }
140
141         @Test
142         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadWriteCommand() {
143                 fcpInterface.setFullAccessRequired(WRITING);
144                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
145                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
146                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
147                 verifyReplyWithMessage("ReadWritePong");
148         }
149
150         @Test
151         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
152                 fcpInterface.setFullAccessRequired(NO);
153                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
154                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
155                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
156                 verifyReplyWithMessage("ReadWritePong");
157         }
158
159         @Test
160         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
161                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
162                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
163                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
164                 verifyReplyWithMessage("ReadOnlyPong");
165         }
166
167         @Test
168         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadOnlyCommand() {
169                 fcpInterface.setFullAccessRequired(WRITING);
170                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
171                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
172                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
173                 verifyReplyWithMessage("ReadOnlyPong");
174         }
175
176         @Test
177         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
178                 fcpInterface.setFullAccessRequired(NO);
179                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
180                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
181                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
182                 verifyReplyWithMessage("ReadOnlyPong");
183         }
184
185         private void verifyReplyWithMessage(String messageName) {
186                 assertThat(pluginReplySender.results, hasSize(1));
187                 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
188                 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is(messageName));
189         }
190
191         @Test
192         public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
193                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
194                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
195                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
196                 verifyReplyWithMessage("ReadWritePong");
197         }
198
199         @Test
200         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadWriteCommand() {
201                 fcpInterface.setFullAccessRequired(WRITING);
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 testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
210                 fcpInterface.setFullAccessRequired(NO);
211                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
212                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
213                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
214                 verifyReplyWithMessage("ReadWritePong");
215         }
216
217         @Test
218         public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadOnlyCommand() {
219                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
220                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
221                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
222                 verifyErrorWithCode("401");
223         }
224
225         @Test
226         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsRestrictedFcpAccessForReadOnlyCommand() {
227                 fcpInterface.setFullAccessRequired(WRITING);
228                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
229                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
230                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
231                 verifyReplyWithMessage("ReadOnlyPong");
232         }
233
234         @Test
235         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadOnlyCommand() {
236                 fcpInterface.setFullAccessRequired(NO);
237                 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
238                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
239                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
240                 verifyReplyWithMessage("ReadOnlyPong");
241         }
242
243         @Test
244         public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
245                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
246                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
247                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
248                 verifyErrorWithCode("401");
249         }
250
251         @Test
252         public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
253                 fcpInterface.setFullAccessRequired(WRITING);
254                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
255                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
256                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
257                 verifyErrorWithCode("401");
258         }
259
260         @Test
261         public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadWriteCommand() {
262                 fcpInterface.setFullAccessRequired(NO);
263                 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
264                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
265                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
266                 verifyReplyWithMessage("ReadWritePong");
267         }
268
269         @Test
270         public void testThatAFaultyCommandResultsInAnError() {
271                 fcpInterface.addCommand("Faulty", new FaultyCommand());
272                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
273                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
274                 verifyError();
275         }
276
277         @Test
278         public void testThatAFaultyPluginReplySenderIsHandled() {
279                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
280                 fcpInterface.handle(new FaultyPluginReplySender(), fieldSet, null, ACCESS_FCP_FULL);
281         }
282
283         @Test
284         public void testThatACommandWithDataIsHandledCorrectly() throws IOException {
285                 fcpInterface.addCommand("CommandWithData", new CommandWithData());
286                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithData").put("Identifier", "foo").get();
287                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
288                 verifyReplyWithMessage("ReturnedData");
289                 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
290                 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
291                 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 1, 2, 3 }));
292         }
293
294         @Test
295         public void testThatACommandWithABucketIsHandledCorrectly() throws IOException {
296                 fcpInterface.addCommand("CommandWithBucket", new CommandWithBucket());
297                 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithBucket").put("Identifier", "foo").get();
298                 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
299                 verifyReplyWithMessage("ReturnedBucket");
300                 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
301                 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
302                 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 4, 5, 6 }));
303         }
304
305         private Matcher<InputStream> delivers(final byte[] data) {
306                 return new TypeSafeMatcher<InputStream>() {
307                         byte[] readData = new byte[data.length];
308
309                         @Override
310                         protected boolean matchesSafely(InputStream inputStream) {
311                                 int offset = 0;
312                                 try {
313                                         while (true) {
314                                                 int r = inputStream.read();
315                                                 if (r == -1) {
316                                                         return offset == data.length;
317                                                 }
318                                                 readData[offset] = (byte) r;
319                                                 if (data[offset++] != r) {
320                                                         return false;
321                                                 }
322                                         }
323                                 } catch (IOException ioe1) {
324                                         return false;
325                                 }
326                         }
327
328                         @Override
329                         public void describeTo(Description description) {
330                                 description.appendValue(data);
331                         }
332
333                         @Override
334                         protected void describeMismatchSafely(InputStream item, Description mismatchDescription) {
335                                 mismatchDescription.appendValue(readData);
336                         }
337                 };
338         }
339
340         private void verifyError() {
341                 assertThat(pluginReplySender.results, hasSize(1));
342                 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
343                 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is("Error"));
344         }
345
346         private void verifyErrorWithCode(String errorCode) {
347                 verifyError();
348                 assertThat(pluginReplySender.results.get(0).fieldSet.get("ErrorCode"), is(errorCode));
349         }
350
351         private static class CapturingPluginReplySender extends PluginReplySender {
352
353                 public final List<PluginReplySenderResult> results = Lists.newArrayList();
354
355                 public CapturingPluginReplySender() {
356                         super(null, null);
357                 }
358
359                 @Override
360                 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
361                         results.add(new PluginReplySenderResult(params, bucket));
362                 }
363
364         }
365
366         private static class PluginReplySenderResult {
367
368                 public final SimpleFieldSet fieldSet;
369                 public final Bucket bucket;
370
371                 public PluginReplySenderResult(SimpleFieldSet fieldSet, Bucket bucket) {
372                         this.fieldSet = fieldSet;
373                         this.bucket = bucket;
374                 }
375
376         }
377
378         private static class ReadOnlyPing extends AbstractSoneCommand {
379
380                 public ReadOnlyPing() {
381                         super(null, false);
382                 }
383
384                 @Override
385                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
386                         return new Response("ReadOnlyPong", new SimpleFieldSetBuilder().get());
387                 }
388
389         }
390
391         private static class ReadWritePing extends AbstractSoneCommand {
392
393                 public ReadWritePing() {
394                         super(null, true);
395                 }
396
397                 @Override
398                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
399                         return new Response("ReadWritePong", new SimpleFieldSetBuilder().get());
400                 }
401
402         }
403
404         private static class FaultyCommand extends AbstractSoneCommand {
405
406                 public FaultyCommand() {
407                         super(null, false);
408                 }
409
410                 @Override
411                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
412                         throw new RuntimeException("I’m faulty!");
413                 }
414
415         }
416
417         private static class FaultyPluginReplySender extends PluginReplySender {
418
419                 public FaultyPluginReplySender() {
420                         super(null, null);
421                 }
422
423                 @Override
424                 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
425                         throw new PluginNotFoundException();
426                 }
427
428         }
429
430         private static class CommandWithData extends AbstractSoneCommand {
431
432                 protected CommandWithData() {
433                         super(null);
434                 }
435
436                 @Override
437                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
438                         return new Response("ReturnedData", new SimpleFieldSetBuilder().get(), new byte[] { 1, 2, 3 });
439                 }
440
441         }
442
443         private static class CommandWithBucket extends AbstractSoneCommand {
444
445                 protected CommandWithBucket() {
446                         super(null);
447                 }
448
449                 @Override
450                 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
451                         return new Response("ReturnedBucket", new SimpleFieldSetBuilder().get(), new ArrayBucket(new byte[] { 4, 5, 6 }));
452                 }
453
454         }
455
456 }