blob: 2423aec797f6aaf5b1908464138c432438b45ae8 [file] [log] [blame]
chrismair00dc7bd2014-05-11 21:21:28 +00001/*
2 * Copyright 2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockftpserver.fake.command
17
18import org.mockftpserver.core.command.Command
19import org.mockftpserver.core.command.CommandHandler
20import org.mockftpserver.core.command.ReplyCodes
21import org.mockftpserver.core.session.SessionKeys
22import org.mockftpserver.core.session.StubSession
23import org.mockftpserver.fake.StubServerConfiguration
24import org.mockftpserver.fake.UserAccount
25import org.mockftpserver.fake.filesystem.DirectoryEntry
26import org.mockftpserver.fake.filesystem.FileEntry
27import org.mockftpserver.fake.filesystem.FileSystemException
28import org.mockftpserver.fake.filesystem.TestUnixFakeFileSystem
29import org.mockftpserver.test.AbstractGroovyTestCase
30import org.mockftpserver.test.StubResourceBundle
31
32/**
33 * Abstract superclass for CommandHandler tests
34 *
35 * @version $Revision$ - $Date$
36 *
37 * @author Chris Mair
38 */
39abstract class AbstractFakeCommandHandlerTestCase extends AbstractGroovyTestCase {
40
41 protected static final ERROR_MESSAGE_KEY = 'msgkey'
42
43 protected session
44 protected serverConfiguration
45 protected replyTextBundle
46 protected commandHandler
47 protected fileSystem
48 protected userAccount
49
50 /** Set this to false to skip the test that verifies that the CommandHandler requires a logged in user */
51 boolean testNotLoggedIn = true
52
53 //-------------------------------------------------------------------------
54 // Tests (common to all subclasses)
55 //-------------------------------------------------------------------------
56
57 void testHandleCommand_ServerConfigurationIsNull() {
58 commandHandler.serverConfiguration = null
59 def command = createValidCommand()
60 shouldFailWithMessageContaining("serverConfiguration") { commandHandler.handleCommand(command, session) }
61 }
62
63 void testHandleCommand_CommandIsNull() {
64 shouldFailWithMessageContaining("command") { commandHandler.handleCommand(null, session) }
65 }
66
67 void testHandleCommand_SessionIsNull() {
68 def command = createValidCommand()
69 shouldFailWithMessageContaining("session") { commandHandler.handleCommand(command, null) }
70 }
71
72 void testHandleCommand_NotLoggedIn() {
73 if (getProperty('testNotLoggedIn')) {
74 def command = createValidCommand()
75 session.removeAttribute(SessionKeys.USER_ACCOUNT)
76 commandHandler.handleCommand(command, session)
77 assertSessionReply(ReplyCodes.NOT_LOGGED_IN)
78 }
79 }
80
81 //-------------------------------------------------------------------------
82 // Abstract Method Declarations (must be implemented by all subclasses)
83 //-------------------------------------------------------------------------
84
85 /**
86 * Create and return a new instance of the CommandHandler class under test. Concrete subclasses must implement.
87 */
88 abstract CommandHandler createCommandHandler()
89
90 /**
91 * Create and return a valid instance of the Command for the CommandHandler class
92 * under test. Concrete subclasses must implement.
93 */
94 abstract Command createValidCommand()
95
96 //-------------------------------------------------------------------------
97 // Test Setup
98 //-------------------------------------------------------------------------
99
100 void setUp() {
101 super.setUp()
102 session = new StubSession()
103 serverConfiguration = new StubServerConfiguration()
104 replyTextBundle = new StubResourceBundle()
105 fileSystem = new TestUnixFakeFileSystem()
106 fileSystem.createParentDirectoriesAutomatically = true
107 serverConfiguration.setFileSystem(fileSystem)
108
109 userAccount = new UserAccount()
110 session.setAttribute(SessionKeys.USER_ACCOUNT, userAccount)
111
112 commandHandler = createCommandHandler()
113 commandHandler.serverConfiguration = serverConfiguration
114 commandHandler.replyTextBundle = replyTextBundle
115 }
116
117 //-------------------------------------------------------------------------
118 // Helper Methods
119 //-------------------------------------------------------------------------
120
121 /**
122 * Perform a test of the handleCommand() method on the specified command
123 * parameters, which are missing a required parameter for this CommandHandler.
124 */
125 protected void testHandleCommand_MissingRequiredParameter(List commandParameters) {
126 commandHandler.handleCommand(createCommand(commandParameters), session)
127 assertSessionReply(ReplyCodes.COMMAND_SYNTAX_ERROR)
128 }
129
130 /**
131 * Perform a test of the handleCommand() method on the specified command
132 * parameters, which are missing a required parameter for this CommandHandler.
133 */
134 protected testHandleCommand_MissingRequiredSessionAttribute() {
135 def command = createValidCommand()
136 commandHandler.handleCommand(command, session)
137 assertSessionReply(ReplyCodes.ILLEGAL_STATE)
138 }
139
140 /**
141 * @return a new Command with the specified parameters for this CommandHandler
142 */
143 protected Command createCommand(List commandParameters) {
144 new Command(createValidCommand().name, commandParameters)
145 }
146
147 /**
148 * Invoke the handleCommand() method for the current CommandHandler, passing in
149 * the specified parameters
150 * @param parameters - the List of command parameters; may be empty, but not null
151 */
152 protected void handleCommand(List parameters) {
153 commandHandler.handleCommand(createCommand(parameters), session)
154 }
155
156 /**
157 * Assert that the specified reply code and message containing text was sent through the session.
158 * @param expectedReplyCode - the expected reply code
159 * @param text - the text expected within the reply message; defaults to the reply code as a String
160 */
161 protected assertSessionReply(int expectedReplyCode, text = expectedReplyCode as String) {
162 assertSessionReply(0, expectedReplyCode, text)
163 }
164
165 /**
166 * Assert that the specified reply code and message containing text was sent through the session.
167 * @param replyIndex - the index of the reply to compare
168 * @param expectedReplyCode - the expected reply code
169 * @param text - the text expected within the reply message; defaults to the reply code as a String
170 */
171 protected assertSessionReply(int replyIndex, int expectedReplyCode, text = expectedReplyCode as String) {
172 LOG.info(session.toString())
173 String actualMessage = session.getReplyMessage(replyIndex)
174 def actualReplyCode = session.getReplyCode(replyIndex)
175 assert actualReplyCode == expectedReplyCode
176 if (text instanceof List) {
177 text.each { assert actualMessage.contains(it), "[$actualMessage] does not contain [$it]" }
178 }
179 else {
180 assert actualMessage.contains(text), "[$actualMessage] does not contain [$text]"
181 }
182 }
183
184 /**
185 * Assert that the specified reply codes were sent through the session.
186 * @param replyCodes - the List of expected sent reply codes
187 */
188 protected assertSessionReplies(List replyCodes) {
189 LOG.info(session.toString())
190 replyCodes.eachWithIndex {replyCode, replyIndex ->
191 assertSessionReply(replyIndex, replyCode)
192 }
193 }
194
195 /**
196 * Assert that the specified data was sent through the session.
197 * @param expectedData - the expected data
198 */
199 protected assertSessionData(String expectedData) {
200 def actual = session.sentData[0]
201 assert actual != null, "No data for index [0] sent for $session"
202 assert actual == expectedData
203 }
204
205 /**
206 * Assert that the specified data was sent through the session, terminated by an end-of-line.
207 * @param expectedData - the expected data
208 */
209 protected assertSessionDataWithEndOfLine(String expectedData) {
210 assertSessionData(expectedData + endOfLine())
211 }
212
213 /**
214 * Assert that the data sent through the session terminated with an end-of-line.
215 */
216 protected assertSessionDataEndsWithEndOfLine() {
217 assert session.sentData[0].endsWith(endOfLine())
218 }
219
220 /**
221 * Execute the handleCommand() method with the specified parameters and
222 * assert that the standard SEND DATA replies were sent through the session.
223 * @param parameters - the command parameters to use; defaults to []
224 * @param finalReplyCode - the expected final reply code; defaults to ReplyCodes.TRANSFER_DATA_FINAL_OK
225 */
226 protected handleCommandAndVerifySendDataReplies(parameters = [], int finalReplyCode = ReplyCodes.TRANSFER_DATA_FINAL_OK) {
227 handleCommand(parameters)
228 assertSessionReplies([ReplyCodes.TRANSFER_DATA_INITIAL_OK, finalReplyCode])
229 }
230
231 /**
232 * Execute the handleCommand() method with the specified parameters and
233 * assert that the standard SEND DATA replies were sent through the session.
234 * @param parameters - the command parameters to use
235 * @param initialReplyMessageKey - the expected reply message key for the initial reply
236 * @param finalReplyMessageKey - the expected reply message key for the final reply
237 * @param finalReplyCode - the expected final reply code; defaults to ReplyCodes.TRANSFER_DATA_FINAL_OK
238 */
239 protected handleCommandAndVerifySendDataReplies(parameters, String initialReplyMessageKey, String finalReplyMessageKey, int finalReplyCode = ReplyCodes.TRANSFER_DATA_FINAL_OK) {
240 handleCommand(parameters)
241 assertSessionReply(0, ReplyCodes.TRANSFER_DATA_INITIAL_OK, initialReplyMessageKey)
242 assertSessionReply(1, finalReplyCode, finalReplyMessageKey)
243 }
244
245 /**
246 * Override the named method for the specified object instance
247 * @param object - the object instance
248 * @param methodName - the name of the method to override
249 * @param newMethod - the Closure representing the new method for this single instance
250 */
251 protected void overrideMethod(object, String methodName, Closure newMethod) {
252 LOG.info("Overriding method [$methodName] for class [${object.class}]")
253 def emc = new ExpandoMetaClass(object.class, false)
254 emc."$methodName" = newMethod
255 emc.initialize()
256 object.metaClass = emc
257 }
258
259 /**
260 * Override the named method (that takes a single String arg) of the fileSystem object to throw a (generic) FileSystemException
261 * @param methodName - the name of the fileSystem method to override
262 */
263 protected void overrideMethodToThrowFileSystemException(String methodName) {
264 def newMethod = {String path -> throw new FileSystemException("Error thrown by method [$methodName]", ERROR_MESSAGE_KEY) }
265 overrideMethod(fileSystem, methodName, newMethod)
266 }
267
268 /**
269 * Set the current directory within the session
270 * @param path - the new path value for the current directory
271 */
272 protected void setCurrentDirectory(String path) {
273 session.setAttribute(SessionKeys.CURRENT_DIRECTORY, path)
274 }
275
276 /**
277 * Convenience method to return the end-of-line character(s) for the current CommandHandler.
278 */
279 protected endOfLine() {
280 commandHandler.endOfLine()
281 }
282
283 /**
284 * Create a new directory entry with the specified path in the file system
285 * @param path - the path of the new directory entry
286 * @return the newly created DirectoryEntry
287 */
288 protected DirectoryEntry createDirectory(String path) {
289 DirectoryEntry entry = new DirectoryEntry(path)
290 fileSystem.add(entry)
291 return entry
292 }
293
294 /**
295 * Create a new file entry with the specified path in the file system
296 * @param path - the path of the new file entry
297 * @param contents - the contents for the file; defaults to null
298 * @return the newly created FileEntry
299 */
300 protected FileEntry createFile(String path, contents = null) {
301 FileEntry entry = new FileEntry(path: path, contents: contents)
302 fileSystem.add(entry)
303 return entry
304 }
305
306}