Extract class for supplying commands, improve tests for FCP interface
[Sone.git] / src / test / java / net / pterodactylus / sone / fcp / FcpInterfaceTest.kt
1 @file:Suppress("DEPRECATION")
2
3 package net.pterodactylus.sone.fcp
4
5 import com.google.inject.Guice
6 import freenet.pluginmanager.PluginNotFoundException
7 import freenet.pluginmanager.PluginReplySender
8 import freenet.support.SimpleFieldSet
9 import net.pterodactylus.sone.core.Core
10 import net.pterodactylus.sone.fcp.FcpInterface.CommandSupplier
11 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired
12 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS
13 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO
14 import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING
15 import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent
16 import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent
17 import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged
18 import net.pterodactylus.sone.freenet.fcp.Command.AccessType.FULL_FCP
19 import net.pterodactylus.sone.freenet.fcp.Command.AccessType.RESTRICTED_FCP
20 import net.pterodactylus.sone.freenet.fcp.Command.Response
21 import net.pterodactylus.sone.test.bindAs
22 import net.pterodactylus.sone.test.capture
23 import net.pterodactylus.sone.test.mock
24 import net.pterodactylus.sone.test.whenever
25 import org.hamcrest.MatcherAssert.assertThat
26 import org.hamcrest.Matchers.containsInAnyOrder
27 import org.hamcrest.Matchers.equalTo
28 import org.hamcrest.Matchers.sameInstance
29 import org.junit.Test
30 import org.mockito.ArgumentMatchers
31 import org.mockito.Mockito.any
32 import org.mockito.Mockito.verify
33
34 /**
35  * Unit test for [FcpInterface] and its subclasses.
36  */
37 class FcpInterfaceTest {
38
39         private val core = mock<Core>()
40         private val workingCommand = mock<AbstractSoneCommand>().apply {
41                 whenever(execute(any(), any(), any())).thenReturn(Response("Working", SimpleFieldSet(true).apply {
42                         putSingle("ReallyWorking", "true")
43                 }))
44         }
45         private val brokenCommand = mock<AbstractSoneCommand>().apply {
46                 whenever(execute(any(), any(), any())).thenThrow(RuntimeException::class.java)
47         }
48         private val commandSupplier = object : CommandSupplier() {
49                 override fun supplyCommands(core: Core): Map<String, AbstractSoneCommand> {
50                         return mapOf(
51                                         "Working" to workingCommand,
52                                         "Broken" to brokenCommand
53                         )
54                 }
55         }
56         private val fcpInterface = FcpInterface(core, commandSupplier)
57         private val pluginReplySender = mock<PluginReplySender>()
58         private val parameters = SimpleFieldSet(true)
59         private val replyParameters = capture<SimpleFieldSet>()
60
61         @Test
62         fun `fcp interface is instantiated as singleton`() {
63                 val injector = Guice.createInjector(core.bindAs(Core::class))
64                 assertThat(injector.getInstance(FcpInterface::class.java), sameInstance(injector.getInstance(FcpInterface::class.java)))
65         }
66
67         @Test
68         fun `fcp interface can be activated`() {
69                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
70                 assertThat(fcpInterface.isActive, equalTo(true))
71         }
72
73         @Test
74         fun `fcp interface can be deactivated`() {
75                 fcpInterface.fcpInterfaceDeactivated(FcpInterfaceDeactivatedEvent())
76                 assertThat(fcpInterface.isActive, equalTo(false))
77         }
78
79         private fun setAndVerifyAccessRequired(fullAccessRequired: FullAccessRequired) {
80                 fcpInterface.fullAccessRequiredChanged(FullAccessRequiredChanged(fullAccessRequired))
81                 assertThat(fcpInterface.fullAccessRequired, equalTo(fullAccessRequired))
82         }
83
84         @Test
85         fun `set full access required can set access to no`() {
86                 setAndVerifyAccessRequired(NO)
87         }
88
89         @Test
90         fun `set full access required can set access to writing`() {
91                 setAndVerifyAccessRequired(WRITING)
92         }
93
94         @Test
95         fun `set full access required can set access to always`() {
96                 setAndVerifyAccessRequired(ALWAYS)
97         }
98
99         @Test
100         fun `sending command to inactive fcp interface results in 400 error reply`() {
101                 fcpInterface.fcpInterfaceDeactivated(FcpInterfaceDeactivatedEvent())
102                 fcpInterface.handle(pluginReplySender, parameters, null, 0)
103                 verify(pluginReplySender).send(replyParameters.capture())
104                 assertThat(replyParameters.value["Message"], equalTo("Error"))
105                 assertThat(replyParameters.value["ErrorCode"], equalTo("503"))
106         }
107
108         @Test
109         fun `exception while sending reply does not result in exception`() {
110                 fcpInterface.fcpInterfaceDeactivated(FcpInterfaceDeactivatedEvent())
111                 whenever(pluginReplySender.send(ArgumentMatchers.any())).thenThrow(PluginNotFoundException::class.java)
112                 fcpInterface.handle(pluginReplySender, parameters, null, 0)
113         }
114
115         @Test
116         fun `sending command over restricted fcp connection results in 401 error reply`() {
117                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
118                 fcpInterface.handle(pluginReplySender, parameters, null, RESTRICTED_FCP.ordinal)
119                 verify(pluginReplySender).send(replyParameters.capture())
120                 assertThat(replyParameters.value["Message"], equalTo("Error"))
121                 assertThat(replyParameters.value["ErrorCode"], equalTo("401"))
122         }
123
124         @Test
125         fun `sending unknown command over full access connection results in 404 error reply`() {
126                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
127                 fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
128                 verify(pluginReplySender).send(replyParameters.capture())
129                 assertThat(replyParameters.value["Message"], equalTo("Error"))
130                 assertThat(replyParameters.value["ErrorCode"], equalTo("404"))
131         }
132
133         @Test
134         fun `sending working command without identifier results in 400 error code`() {
135                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
136                 parameters.putSingle("Message", "Working")
137                 fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
138                 verify(pluginReplySender).send(replyParameters.capture())
139                 assertThat(replyParameters.value["Message"], equalTo("Error"))
140                 assertThat(replyParameters.value["ErrorCode"], equalTo("400"))
141         }
142
143         @Test
144         fun `sending working command with empty identifier results in 400 error code`() {
145                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
146                 parameters.putSingle("Message", "Working")
147                 parameters.putSingle("Identifier", "")
148                 fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
149                 verify(pluginReplySender).send(replyParameters.capture())
150                 assertThat(replyParameters.value["Message"], equalTo("Error"))
151                 assertThat(replyParameters.value["ErrorCode"], equalTo("400"))
152         }
153
154         @Test
155         fun `sending working command with identifier results in working reply`() {
156                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
157                 parameters.putSingle("Message", "Working")
158                 parameters.putSingle("Identifier", "Test")
159                 fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
160                 verify(pluginReplySender).send(replyParameters.capture())
161                 assertThat(replyParameters.value["Message"], equalTo("Working"))
162                 assertThat(replyParameters.value["ReallyWorking"], equalTo("true"))
163         }
164
165         @Test
166         fun `sending broken  command with identifier results in 500 error reply`() {
167                 fcpInterface.fcpInterfaceActivated(FcpInterfaceActivatedEvent())
168                 parameters.putSingle("Message", "Broken")
169                 parameters.putSingle("Identifier", "Test")
170                 fcpInterface.handle(pluginReplySender, parameters, null, FULL_FCP.ordinal)
171                 verify(pluginReplySender).send(replyParameters.capture())
172                 assertThat(replyParameters.value["Message"], equalTo("Error"))
173                 assertThat(replyParameters.value["ErrorCode"], equalTo("500"))
174         }
175
176 }
177
178 class CommandSupplierTest {
179
180         private val core = mock<Core>()
181         private val commandSupplier = CommandSupplier()
182
183         @Test
184         fun `command supplier supplies all commands`() {
185                 val commands = commandSupplier.supplyCommands(core)
186                 assertThat(commands.keys, containsInAnyOrder(
187                                 "CreatePost",
188                                 "CreateReply",
189                                 "DeletePost",
190                                 "DeleteReply",
191                                 "GetLocalSones",
192                                 "GetPost",
193                                 "GetPostFeed",
194                                 "GetPosts",
195                                 "GetSone",
196                                 "GetSones",
197                                 "LikePost",
198                                 "LikeReply",
199                                 "LockSone",
200                                 "UnlockSone",
201                                 "Version"
202                 ))
203         }
204
205         @Test
206         fun `command supplier is instantiated as singleton`() {
207                 val injector = Guice.createInjector()
208                 assertThat(injector.getInstance(CommandSupplier::class.java), sameInstance(injector.getInstance(CommandSupplier::class.java)))
209         }
210
211 }