Add unit test for FCP interface.
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 26 Oct 2013 12:56:03 +0000 (14:56 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 28 Feb 2014 21:25:46 +0000 (22:25 +0100)
src/test/java/net/pterodactylus/sone/fcp/FcpInterfaceTest.java [new file with mode: 0644]

diff --git a/src/test/java/net/pterodactylus/sone/fcp/FcpInterfaceTest.java b/src/test/java/net/pterodactylus/sone/fcp/FcpInterfaceTest.java
new file mode 100644 (file)
index 0000000..6648d12
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Sone - FcpInterfaceTest.java - Copyright © 2013 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import static freenet.pluginmanager.FredPluginFCP.ACCESS_DIRECT;
+import static freenet.pluginmanager.FredPluginFCP.ACCESS_FCP_FULL;
+import static freenet.pluginmanager.FredPluginFCP.ACCESS_FCP_RESTRICTED;
+import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO;
+import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.Mockito.mock;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+
+import freenet.pluginmanager.PluginNotFoundException;
+import freenet.pluginmanager.PluginReplySender;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+import freenet.support.io.ArrayBucket;
+
+import com.google.common.collect.Lists;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FcpInterface}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FcpInterfaceTest {
+
+       private final Core core = mock(Core.class);
+       private final FcpInterface fcpInterface = new FcpInterface(core);
+       private final CapturingPluginReplySender pluginReplySender = new CapturingPluginReplySender();
+
+       @Test
+       public void testThatAnInactiveFcpInterfaceReturnsAnErrorForDirectAccess() throws PluginNotFoundException {
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyErrorWithCode("400");
+       }
+
+       @Test
+       public void testThatAnInactiveFcpInterfaceReturnsAnErrorForRestrictedFcpAccess() throws PluginNotFoundException {
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyErrorWithCode("400");
+       }
+
+       @Test
+       public void testThatAnInactiveFcpInterfaceReturnsAnErrorForFullFcpAccess() throws PluginNotFoundException {
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyErrorWithCode("400");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceReturnsAnErrorForAnUnknownMessage() {
+               fcpInterface.setActive(true);
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyError();
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceReturnsAnErrorForAMessageWithoutIdentifier() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyError();
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsDirectFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsDirectFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_DIRECT);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       private void verifyReplyWithMessage(String messageName) {
+               assertThat(pluginReplySender.results, hasSize(1));
+               assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
+               assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is(messageName));
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsFullFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsFullFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyErrorWithCode("401");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesAllowsRestrictedFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadOnlyCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadOnlyPing", new ReadOnlyPing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadOnlyPing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyReplyWithMessage("ReadOnlyPong");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyErrorWithCode("401");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceRequiringFullAccessForWritesDoesNotAllowRestrictedFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(WRITING);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyErrorWithCode("401");
+       }
+
+       @Test
+       public void testThatAnActiveFcpInterfaceNotRequiringFullAccessAllowsRestrictedFcpAccessForReadWriteCommand() {
+               fcpInterface.setActive(true);
+               fcpInterface.setFullAccessRequired(NO);
+               fcpInterface.addCommand("ReadWritePing", new ReadWritePing());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "ReadWritePing").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_RESTRICTED);
+               verifyReplyWithMessage("ReadWritePong");
+       }
+
+       @Test
+       public void testThatAFaultyCommandResultsInAnError() {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("Faulty", new FaultyCommand());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyError();
+       }
+
+       @Test
+       public void testThatAFaultyPluginReplySenderIsHandled() {
+               fcpInterface.setActive(true);
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "Faulty").put("Identifier", "foo").get();
+               fcpInterface.handle(new FaultyPluginReplySender(), fieldSet, null, ACCESS_FCP_FULL);
+       }
+
+       @Test
+       public void testThatACommandWithDataIsHandledCorrectly() throws IOException {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("CommandWithData", new CommandWithData());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithData").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReturnedData");
+               assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
+               assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
+               assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 1, 2, 3 }));
+       }
+
+       @Test
+       public void testThatACommandWithABucketIsHandledCorrectly() throws IOException {
+               fcpInterface.setActive(true);
+               fcpInterface.addCommand("CommandWithBucket", new CommandWithBucket());
+               SimpleFieldSet fieldSet = new SimpleFieldSetBuilder().put("Message", "CommandWithBucket").put("Identifier", "foo").get();
+               fcpInterface.handle(pluginReplySender, fieldSet, null, ACCESS_FCP_FULL);
+               verifyReplyWithMessage("ReturnedBucket");
+               assertThat(pluginReplySender.results.get(0).bucket, notNullValue());
+               assertThat(pluginReplySender.results.get(0).bucket.size(), is(3L));
+               assertThat(pluginReplySender.results.get(0).bucket.getInputStream(), delivers(new byte[] { 4, 5, 6 }));
+       }
+
+       private Matcher<InputStream> delivers(final byte[] data) {
+               return new TypeSafeMatcher<InputStream>() {
+                       byte[] readData = new byte[data.length];
+
+                       @Override
+                       protected boolean matchesSafely(InputStream inputStream) {
+                               int offset = 0;
+                               try {
+                                       while (true) {
+                                               int r = inputStream.read();
+                                               if (r == -1) {
+                                                       return offset == data.length;
+                                               }
+                                               readData[offset] = (byte) r;
+                                               if (data[offset++] != r) {
+                                                       return false;
+                                               }
+                                       }
+                               } catch (IOException ioe1) {
+                                       return false;
+                               }
+                       }
+
+                       @Override
+                       public void describeTo(Description description) {
+                               description.appendValue(data);
+                       }
+
+                       @Override
+                       protected void describeMismatchSafely(InputStream item, Description mismatchDescription) {
+                               mismatchDescription.appendValue(readData);
+                       }
+               };
+       }
+
+       private void verifyError() {
+               assertThat(pluginReplySender.results, hasSize(1));
+               assertThat(pluginReplySender.results.get(0).fieldSet, notNullValue());
+               assertThat(pluginReplySender.results.get(0).fieldSet.get("Message"), is("Error"));
+       }
+
+       private void verifyErrorWithCode(String errorCode) {
+               verifyError();
+               assertThat(pluginReplySender.results.get(0).fieldSet.get("ErrorCode"), is(errorCode));
+       }
+
+       private static class CapturingPluginReplySender extends PluginReplySender {
+
+               public final List<PluginReplySenderResult> results = Lists.newArrayList();
+
+               public CapturingPluginReplySender() {
+                       super(null, null);
+               }
+
+               @Override
+               public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
+                       results.add(new PluginReplySenderResult(params, bucket));
+               }
+
+       }
+
+       private static class PluginReplySenderResult {
+
+               public final SimpleFieldSet fieldSet;
+               public final Bucket bucket;
+
+               public PluginReplySenderResult(SimpleFieldSet fieldSet, Bucket bucket) {
+                       this.fieldSet = fieldSet;
+                       this.bucket = bucket;
+               }
+
+       }
+
+       private static class ReadOnlyPing extends AbstractSoneCommand {
+
+               public ReadOnlyPing() {
+                       super(null, false);
+               }
+
+               @Override
+               public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+                       return new Response("ReadOnlyPong", new SimpleFieldSetBuilder().get());
+               }
+
+       }
+
+       private static class ReadWritePing extends AbstractSoneCommand {
+
+               public ReadWritePing() {
+                       super(null, true);
+               }
+
+               @Override
+               public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+                       return new Response("ReadWritePong", new SimpleFieldSetBuilder().get());
+               }
+
+       }
+
+       private static class FaultyCommand extends AbstractSoneCommand {
+
+               public FaultyCommand() {
+                       super(null, false);
+               }
+
+               @Override
+               public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+                       throw new RuntimeException("I’m faulty!");
+               }
+
+       }
+
+       private static class FaultyPluginReplySender extends PluginReplySender {
+
+               public FaultyPluginReplySender() {
+                       super(null, null);
+               }
+
+               @Override
+               public void send(SimpleFieldSet params, Bucket bucket) throws PluginNotFoundException {
+                       throw new PluginNotFoundException();
+               }
+
+       }
+
+       private static class CommandWithData extends AbstractSoneCommand {
+
+               protected CommandWithData() {
+                       super(null);
+               }
+
+               @Override
+               public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+                       return new Response("ReturnedData", new SimpleFieldSetBuilder().get(), new byte[] { 1, 2, 3 });
+               }
+
+       }
+
+       private static class CommandWithBucket extends AbstractSoneCommand {
+
+               protected CommandWithBucket() {
+                       super(null);
+               }
+
+               @Override
+               public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+                       return new Response("ReturnedBucket", new SimpleFieldSetBuilder().get(), new ArrayBucket(new byte[] { 4, 5, 6 }));
+               }
+
+       }
+
+}