Home | History | Annotate | Download | only in command
      1 /*
      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  */
     16 package org.mockftpserver.fake.command
     17 
     18 import org.mockftpserver.core.command.Command
     19 import org.mockftpserver.core.command.CommandHandler
     20 import org.mockftpserver.core.command.ReplyCodes
     21 import org.mockftpserver.core.session.SessionKeys
     22 import org.mockftpserver.core.session.StubSession
     23 import org.mockftpserver.fake.StubServerConfiguration
     24 import org.mockftpserver.fake.UserAccount
     25 import org.mockftpserver.fake.filesystem.DirectoryEntry
     26 import org.mockftpserver.fake.filesystem.FileEntry
     27 import org.mockftpserver.fake.filesystem.FileSystemException
     28 import org.mockftpserver.fake.filesystem.TestUnixFakeFileSystem
     29 import org.mockftpserver.test.AbstractGroovyTestCase
     30 import org.mockftpserver.test.StubResourceBundle
     31 
     32 /**
     33  * Abstract superclass for CommandHandler tests
     34  *
     35  * @version $Revision$ - $Date$
     36  *
     37  * @author Chris Mair
     38  */
     39 abstract 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 }