Add unit test for version command
[Sone.git] / src / test / java / net / pterodactylus / sone / fcp / FcpInterfaceTest.kt
index 1ec9473..e9137b0 100644 (file)
@@ -1,6 +1,14 @@
+@file:Suppress("DEPRECATION")
+
 package net.pterodactylus.sone.fcp
 
+import com.google.inject.Guice
+import freenet.pluginmanager.PluginNotFoundException
+import freenet.pluginmanager.PluginReplySender
+import freenet.support.SimpleFieldSet
 import net.pterodactylus.sone.core.Core
+import net.pterodactylus.sone.fcp.FcpInterface.AccessAuthorizer
+import net.pterodactylus.sone.fcp.FcpInterface.CommandSupplier
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS
 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO
@@ -8,10 +16,23 @@ import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING
 import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent
 import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent
 import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged
+import net.pterodactylus.sone.freenet.fcp.Command.AccessType
+import net.pterodactylus.sone.freenet.fcp.Command.AccessType.FULL_FCP
+import net.pterodactylus.sone.freenet.fcp.Command.AccessType.RESTRICTED_FCP
+import net.pterodactylus.sone.freenet.fcp.Command.Response
+import net.pterodactylus.sone.test.bindAs
+import net.pterodactylus.sone.test.capture
 import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.whenever
 import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.containsInAnyOrder
 import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.sameInstance
 import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.verify
 
 /**
  * Unit test for [FcpInterface] and its subclasses.
@@ -19,7 +40,33 @@ import org.junit.Test
 class FcpInterfaceTest {
 
        private val core = mock<Core>()
-       private val fcpInterface = FcpInterface(core)
+       private val workingCommand = mock<AbstractSoneCommand>().apply {
+               whenever(execute(any())).thenReturn(Response("Working", SimpleFieldSet(true).apply {
+                       putSingle("ReallyWorking", "true")
+               }))
+       }
+       private val brokenCommand = mock<AbstractSoneCommand>().apply {
+               whenever(execute(any())).thenThrow(RuntimeException::class.java)
+       }
+       private val commandSupplier = object : CommandSupplier() {
+               override fun supplyCommands(core: Core): Map<String, AbstractSoneCommand> {
+                       return mapOf(
+                                       "Working" to workingCommand,
+                                       "Broken" to brokenCommand
+                       )
+               }
+       }
+       private val accessAuthorizer = mock<AccessAuthorizer>()
+       private val fcpInterface = FcpInterface(core, commandSupplier, accessAuthorizer)
+       private val pluginReplySender = mock<PluginReplySender>()
+       private val parameters = SimpleFieldSet(true)
+       private val replyParameters = capture<SimpleFieldSet>()
+
+       @Test
+       fun `fcp interface is instantiated as singleton`() {
+               val injector = Guice.createInjector(core.bindAs(Core::class))
+               assertThat(injector.getInstance(FcpInterface::class.java), sameInstance(injector.getInstance(FcpInterface::class.java)))
+       }
 
        @Test
        fun `fcp interface can be activated`() {
@@ -53,4 +100,148 @@ class FcpInterfaceTest {
                setAndVerifyAccessRequired(ALWAYS)
        }
 
+       @Test
+       fun `sending command to inactive fcp interface results in 400 error reply`() {
+               fcpInterface.fcpInterfaceDeactivated(FcpInterfaceDeactivatedEvent())
+               fcpInterface.handle(pluginReplySender, parameters, null, 0)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("503"))
+       }
+
+       @Test
+       fun `exception while sending reply does not result in exception`() {
+               fcpInterface.fcpInterfaceDeactivated(FcpInterfaceDeactivatedEvent())
+               whenever(pluginReplySender.send(ArgumentMatchers.any())).thenThrow(PluginNotFoundException::class.java)
+               fcpInterface.handle(pluginReplySender, parameters, null, 0)
+       }
+
+       @Test
+       fun `sending command over non-authorized connection results in 401 error reply`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               parameters.putSingle("Message", "Working")
+               fcpInterface.handle(pluginReplySender, parameters, null, RESTRICTED_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("401"))
+       }
+
+       @Test
+       fun `sending unknown command results in 404 error reply`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               fcpInterface.handle(pluginReplySender, parameters, null, RESTRICTED_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("404"))
+       }
+
+       @Test
+       fun `sending working command without identifier results in 400 error code`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               whenever(accessAuthorizer.authorized(any(), any(), anyBoolean())).thenReturn(true)
+               parameters.putSingle("Message", "Working")
+               fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("400"))
+       }
+
+       @Test
+       fun `sending working command with empty identifier results in 400 error code`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               whenever(accessAuthorizer.authorized(any(), any(), anyBoolean())).thenReturn(true)
+               parameters.putSingle("Message", "Working")
+               parameters.putSingle("Identifier", "")
+               fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("400"))
+       }
+
+       @Test
+       fun `sending working command with identifier results in working reply`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               whenever(accessAuthorizer.authorized(any(), any(), anyBoolean())).thenReturn(true)
+               parameters.putSingle("Message", "Working")
+               parameters.putSingle("Identifier", "Test")
+               fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Working"))
+               assertThat(replyParameters.value["ReallyWorking"], equalTo("true"))
+       }
+
+       @Test
+       fun `sending broken  command with identifier results in 500 error reply`() {
+               fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
+               whenever(accessAuthorizer.authorized(any(), any(), anyBoolean())).thenReturn(true)
+               parameters.putSingle("Message", "Broken")
+               parameters.putSingle("Identifier", "Test")
+               fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
+               verify(pluginReplySender).send(replyParameters.capture())
+               assertThat(replyParameters.value["Message"], equalTo("Error"))
+               assertThat(replyParameters.value["ErrorCode"], equalTo("500"))
+       }
+
+}
+
+class CommandSupplierTest {
+
+       private val core = mock<Core>()
+       private val commandSupplier = CommandSupplier()
+
+       @Test
+       fun `command supplier supplies all commands`() {
+               val commands = commandSupplier.supplyCommands(core)
+               assertThat(commands.keys, containsInAnyOrder(
+                               "CreatePost",
+                               "CreateReply",
+                               "DeletePost",
+                               "DeleteReply",
+                               "GetLocalSones",
+                               "GetPost",
+                               "GetPostFeed",
+                               "GetPosts",
+                               "GetSone",
+                               "GetSones",
+                               "LikePost",
+                               "LikeReply",
+                               "LockSone",
+                               "UnlockSone",
+                               "Version"
+               ))
+       }
+
+       @Test
+       fun `command supplier is instantiated as singleton`() {
+               val injector = Guice.createInjector()
+               assertThat(injector.getInstance(CommandSupplier::class.java), sameInstance(injector.getInstance(CommandSupplier::class.java)))
+       }
+
+}
+
+class AccessAuthorizerTest {
+
+       private val accessAuthorizer = AccessAuthorizer()
+
+       @Test
+       fun `access authorizer is instantiated as singleton`() {
+               val injector = Guice.createInjector()
+               assertThat(injector.getInstance(AccessAuthorizer::class.java), sameInstance(injector.getInstance(AccessAuthorizer::class.java)))
+       }
+
+       @Test
+       fun `access authorizer makes correct decisions`() {
+               AccessType.values().forEach { accessType ->
+                       FullAccessRequired.values().forEach { fullAccessRequired ->
+                               listOf(false, true).forEach { commandRequiresWriteAccess ->
+                                       assertThat("$accessType, $fullAccessRequired, $commandRequiresWriteAccess", accessAuthorizer.authorized(accessType, fullAccessRequired, commandRequiresWriteAccess), equalTo(
+                                                       accessType != RESTRICTED_FCP ||
+                                                                       fullAccessRequired == NO ||
+                                                                       (fullAccessRequired == WRITING && !commandRequiresWriteAccess)
+                                       ))
+                               }
+                       }
+               }
+       }
+
 }