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.NO;
24 import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING;
25 import static org.hamcrest.MatcherAssert.assertThat;
26 import static org.hamcrest.Matchers.hasSize;
27 import static org.hamcrest.Matchers.is;
28 import static org.hamcrest.Matchers.notNullValue;
29 import static org.mockito.Mockito.mock;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.List;
35 import net.pterodactylus.sone.core.Core;
36 import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
37 import net.pterodactylus.sone.freenet.fcp.FcpException;
39 import freenet.pluginmanager.PluginNotFoundException;
40 import freenet.pluginmanager.PluginReplySender;
41 import freenet.support.SimpleFieldSet;
42 import freenet.support.api.Bucket;
43 import freenet.support.io.ArrayBucket;
45 import com.google.common.collect.Lists;
46 import org.hamcrest.Description;
47 import org.hamcrest.Matcher;
48 import org.hamcrest.TypeSafeMatcher;
49 import org.junit.Test;
52 * Unit test for {@link FcpInterface}.
54 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
56 public class FcpInterfaceTest {
58 private final Core core = mock(Core.class);
59 private final FcpInterface fcpInterface = new FcpInterface(core);
60 private final CapturingPluginReplySender pluginReplySender = new CapturingPluginReplySender();
63 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForDirectAccess() throws PluginNotFoundException {
64 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
65 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
66 verifyErrorWithCode("400");
70 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForRestrictedFcpAccess() throws PluginNotFoundException {
71 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
72 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
73 verifyErrorWithCode("400");
77 public void testThatAnInactiveFcpInterfaceReturnsAnErrorForFullFcpAccess() throws PluginNotFoundException {
78 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
79 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
80 verifyErrorWithCode("400");
84 public void testThatAnActiveFcpInterfaceReturnsAnErrorForAnUnknownMessage() {
85 fcpInterface.setActive(true);
86 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Foo").get();
87 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
92 public void testThatAnActiveFcpInterfaceReturnsAnErrorForAMessageWithoutIdentifier() {
93 fcpInterface.setActive(true);
94 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
95 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").get();
96 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
101 public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
102 fcpInterface.setActive(true);
103 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
104 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
105 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
106 verifyReplyWithMessage("ReadOnlyPong");
110 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadOnlyCommand() {
111 fcpInterface.setActive(true);
112 fcpInterface.setFullAccessRequired(WRITING);
113 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
114 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
115 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
116 verifyReplyWithMessage("ReadOnlyPong");
120 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
121 fcpInterface.setActive(true);
122 fcpInterface.setFullAccessRequired(NO);
123 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
124 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
125 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
126 verifyReplyWithMessage("ReadOnlyPong");
130 public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
131 fcpInterface.setActive(true);
132 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
133 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
134 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
135 verifyReplyWithMessage("ReadWritePong");
139 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadWriteCommand() {
140 fcpInterface.setActive(true);
141 fcpInterface.setFullAccessRequired(WRITING);
142 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
143 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
144 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
145 verifyReplyWithMessage("ReadWritePong");
149 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
150 fcpInterface.setActive(true);
151 fcpInterface.setFullAccessRequired(NO);
152 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
153 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
154 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
155 verifyReplyWithMessage("ReadWritePong");
159 public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
160 fcpInterface.setActive(true);
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");
168 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadOnlyCommand() {
169 fcpInterface.setActive(true);
170 fcpInterface.setFullAccessRequired(WRITING);
171 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
172 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
173 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
174 verifyReplyWithMessage("ReadOnlyPong");
178 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
179 fcpInterface.setActive(true);
180 fcpInterface.setFullAccessRequired(NO);
181 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
182 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
183 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
184 verifyReplyWithMessage("ReadOnlyPong");
187 private void verifyReplyWithMessage(String messageName) {
188 assertThat(pluginReplySender.results, hasSize(1));
189 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
190 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is(messageName));
194 public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
195 fcpInterface.setActive(true);
196 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
197 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
198 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
199 verifyReplyWithMessage("ReadWritePong");
203 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadWriteCommand() {
204 fcpInterface.setActive(true);
205 fcpInterface.setFullAccessRequired(WRITING);
206 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
207 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
208 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
209 verifyReplyWithMessage("ReadWritePong");
213 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
214 fcpInterface.setActive(true);
215 fcpInterface.setFullAccessRequired(NO);
216 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
217 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
218 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
219 verifyReplyWithMessage("ReadWritePong");
223 public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadOnlyCommand() {
224 fcpInterface.setActive(true);
225 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
226 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
227 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
228 verifyErrorWithCode("401");
232 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsRestrictedFcpAccessForReadOnlyCommand() {
233 fcpInterface.setActive(true);
234 fcpInterface.setFullAccessRequired(WRITING);
235 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
236 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
237 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
238 verifyReplyWithMessage("ReadOnlyPong");
242 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadOnlyCommand() {
243 fcpInterface.setActive(true);
244 fcpInterface.setFullAccessRequired(NO);
245 fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
246 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
247 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
248 verifyReplyWithMessage("ReadOnlyPong");
252 public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
253 fcpInterface.setActive(true);
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");
261 public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
262 fcpInterface.setActive(true);
263 fcpInterface.setFullAccessRequired(WRITING);
264 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
265 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
266 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
267 verifyErrorWithCode("401");
271 public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadWriteCommand() {
272 fcpInterface.setActive(true);
273 fcpInterface.setFullAccessRequired(NO);
274 fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
275 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
276 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
277 verifyReplyWithMessage("ReadWritePong");
281 public void testThatAFaultyCommandResultsInAnError() {
282 fcpInterface.setActive(true);
283 fcpInterface.addCommand("Faulty", new FaultyCommand());
284 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
285 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
290 public void testThatAFaultyPluginReplySenderIsHandled() {
291 fcpInterface.setActive(true);
292 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
293 fcpInterface.handle(new FaultyPluginReplySender(), fieldSet, null, ACCESS_FCP_FULL);
297 public void testThatACommandWithDataIsHandledCorrectly() throws IOException {
298 fcpInterface.setActive(true);
299 fcpInterface.addCommand("CommandWithData", new CommandWithData());
300 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithData").put("Identifier", "foo").get();
301 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
302 verifyReplyWithMessage("ReturnedData");
303 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
304 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
305 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 1, 2, 3 }));
309 public void testThatACommandWithABucketIsHandledCorrectly() throws IOException {
310 fcpInterface.setActive(true);
311 fcpInterface.addCommand("CommandWithBucket", new CommandWithBucket());
312 SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithBucket").put("Identifier", "foo").get();
313 fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
314 verifyReplyWithMessage("ReturnedBucket");
315 assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
316 assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
317 assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 4, 5, 6 }));
320 private Matcher<InputStream> delivers(final byte[] data) {
321 return new TypeSafeMatcher<InputStream>() {
322 byte[] readData = new byte[data.length];
325 protected boolean matchesSafely(InputStream inputStream) {
329 int r = inputStream.read();
331 return offset == data.length;
333 readData[offset] = (byte) r;
334 if (data[offset++] != r) {
338 } catch (IOException ioe1) {
344 public void describeTo(Description description) {
345 description.appendValue(data);
349 protected void describeMismatchSafely(InputStream item, Description mismatchDescription) {
350 mismatchDescription.appendValue(readData);
355 private void verifyError() {
356 assertThat(pluginReplySender.results, hasSize(1));
357 assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
358 assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is("Error"));
361 private void verifyErrorWithCode(String errorCode) {
363 assertThat(pluginReplySender.results.get(0).fieldSet.get("ErrorCode"), is(errorCode));
366 private static class CapturingPluginReplySender extends PluginReplySender {
368 public final List<PluginReplySenderResult> results = Lists.newArrayList();
370 public CapturingPluginReplySender() {
375 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
376 results.add(new PluginReplySenderResult(params, bucket));
381 private static class PluginReplySenderResult {
383 public final SimpleFieldSet fieldSet;
384 public final Bucket bucket;
386 public PluginReplySenderResult(SimpleFieldSet fieldSet, Bucket bucket) {
387 this.fieldSet = fieldSet;
388 this.bucket = bucket;
393 private static class ReadOnlyPing extends AbstractSoneCommand {
395 public ReadOnlyPing() {
400 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
401 return new Response("ReadOnlyPong", new SimpleFieldSetBuilder().get());
406 private static class ReadWritePing extends AbstractSoneCommand {
408 public ReadWritePing() {
413 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
414 return new Response("ReadWritePong", new SimpleFieldSetBuilder().get());
419 private static class FaultyCommand extends AbstractSoneCommand {
421 public FaultyCommand() {
426 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
427 throw new RuntimeException("I’m faulty!");
432 private static class FaultyPluginReplySender extends PluginReplySender {
434 public FaultyPluginReplySender() {
439 public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
440 throw new PluginNotFoundException();
445 private static class CommandWithData extends AbstractSoneCommand {
447 protected CommandWithData() {
452 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
453 return new Response("ReturnedData", new SimpleFieldSetBuilder().get(), new byte[] { 1, 2, 3 });
458 private static class CommandWithBucket extends AbstractSoneCommand {
460 protected CommandWithBucket() {
465 public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
466 return new Response("ReturnedBucket", new SimpleFieldSetBuilder().get(), new ArrayBucket(new byte[] { 4, 5, 6 }));