2 * Sone - FcpInterfaceTest.java - Copyright © 2013 David Roden
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.
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.
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/>.
18 package net.pterodactylus.sone.fcp;
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;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.util.List;
36 import net.pterodactylus.sone.core.Core;
37 import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
38 import net.pterodactylus.sone.freenet.fcp.FcpException;
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;
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;
53 * Unit test for {@link FcpInterface}.
55 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
57 public class FcpInterfaceTest {
59 private final Core core = mock(Core.class);
60 private final FcpInterface fcpInterface = new FcpInterface(core);
61 private final CapturingPluginReplySender pluginReplySender = new CapturingPluginReplySender();
63 public FcpInterfaceTest() {
64 fcpInterface.setActive(false);
65 fcpInterface.setFullAccessRequired(ALWAYS);
69 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForDirectAccess() throws PluginNotFoundException {
70 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
71 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
72 verifyErrorWithCode("400");
76 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForRestrictedFcpAccess() throws PluginNotFoundException {
77 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
78 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
79 verifyErrorWithCode("400");
83 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForFullFcpAccess() throws PluginNotFoundException {
84 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
85 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
86 verifyErrorWithCode("400");
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);
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);
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");
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");
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");
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");
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");
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");
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");
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");
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");
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));
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");
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");
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");
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");
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");
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");
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");
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");
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");
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);
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);
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 }));
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 }));
326 private Matcher<InputStream> delivers(final byte[] data) {
327 return new TypeSafeMatcher<InputStream>() {
328 byte[] readData = new byte[data.length];
331 protected boolean matchesSafely(InputStream inputStream) {
335 int r = inputStream.read();
337 return offset == data.length;
339 readData[offset] = (byte) r;
340 if (data[offset++] != r) {
344 } catch (IOException ioe1) {
350 public void describeTo(Description description) {
351 description.appendValue(data);
355 protected void describeMismatchSafely(InputStream item, Description mismatchDescription) {
356 mismatchDescription.appendValue(readData);
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"));
367 private void verifyErrorWithCode(String errorCode) {
369 assertThat(pluginReplySender.results.get(0).fieldSet.get("ErrorCode"), is(errorCode));
372 private static class CapturingPluginReplySender extends PluginReplySender {
374 public final List<PluginReplySenderResult> results = Lists.newArrayList();
376 public CapturingPluginReplySender() {
381 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
382 results.add(new PluginReplySenderResult(params, bucket));
387 private static class PluginReplySenderResult {
389 public final SimpleFieldSet fieldSet;
390 public final Bucket bucket;
392 public PluginReplySenderResult(SimpleFieldSet fieldSet, Bucket bucket) {
393 this.fieldSet = fieldSet;
394 this.bucket = bucket;
399 private static class ReadOnlyPing extends AbstractSoneCommand {
401 public ReadOnlyPing() {
406 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
407 return new Response("ReadOnlyPong", new SimpleFieldSetBuilder().get());
412 private static class ReadWritePing extends AbstractSoneCommand {
414 public ReadWritePing() {
419 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
420 return new Response("ReadWritePong", new SimpleFieldSetBuilder().get());
425 private static class FaultyCommand extends AbstractSoneCommand {
427 public FaultyCommand() {
432 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
433 throw new RuntimeException("I’m faulty!");
438 private static class FaultyPluginReplySender extends PluginReplySender {
440 public FaultyPluginReplySender() {
445 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
446 throw new PluginNotFoundException();
451 private static class CommandWithData extends AbstractSoneCommand {
453 protected CommandWithData() {
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 });
464 private static class CommandWithBucket extends AbstractSoneCommand {
466 protected CommandWithBucket() {
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 }));