Home | History | Annotate | Download | only in fake
      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
     17 
     18 import org.apache.commons.net.ftp.FTP
     19 import org.apache.commons.net.ftp.FTPClient
     20 import org.apache.commons.net.ftp.FTPFile
     21 import org.mockftpserver.core.command.CommandNames
     22 import org.mockftpserver.core.command.StaticReplyCommandHandler
     23 
     24 import org.mockftpserver.fake.filesystem.DirectoryEntry
     25 import org.mockftpserver.fake.filesystem.FileEntry
     26 import org.mockftpserver.fake.filesystem.FileSystem
     27 import org.mockftpserver.fake.filesystem.UnixFakeFileSystem
     28 import org.mockftpserver.fake.filesystem.WindowsFakeFileSystem
     29 import org.mockftpserver.stub.command.CwdCommandHandler
     30 import org.mockftpserver.test.AbstractGroovyTestCase
     31 import org.mockftpserver.test.PortTestUtil
     32 
     33 /**
     34  * Integration tests for FakeFtpServer.
     35  *
     36  * @version $Revision$ - $Date$
     37  *
     38  * @author Chris Mair
     39  */
     40 class FakeFtpServerIntegrationTest extends AbstractGroovyTestCase {
     41 
     42     static final SERVER = "localhost"
     43     static final USERNAME = "user123"
     44     static final PASSWORD = "password"
     45     static final ACCOUNT = "account123"
     46     static final ASCII_DATA = "abcdef\tghijklmnopqr"
     47     static final BINARY_DATA = new byte[256]
     48     static final ROOT_DIR = "c:/"
     49     static final HOME_DIR = p(ROOT_DIR, "home")
     50     static final SUBDIR_NAME = 'sub'
     51     static final SUBDIR_NAME2 = "archive"
     52     static final SUBDIR = p(HOME_DIR, SUBDIR_NAME)
     53     static final FILENAME1 = "abc.txt"
     54     static final FILENAME2 = "SomeOtherFile.xml"
     55     static final FILE1 = p(HOME_DIR, FILENAME1)
     56     static final SYSTEM_NAME = "WINDOWS"
     57 
     58     private FakeFtpServer ftpServer
     59     private FTPClient ftpClient
     60     private FileSystem fileSystem
     61     private UserAccount userAccount
     62 
     63     //-------------------------------------------------------------------------
     64     // Tests
     65     //-------------------------------------------------------------------------
     66 
     67     void testAbor() {
     68         ftpClientConnectAndLogin()
     69         assert ftpClient.abort()
     70         verifyReplyCode("ABOR", 226)
     71     }
     72 
     73     void testAcct() {
     74         ftpClientConnectAndLogin()
     75         assert ftpClient.acct(ACCOUNT) == 230
     76     }
     77 
     78     void testAllo() {
     79         ftpClientConnectAndLogin()
     80         assert ftpClient.allocate(99)
     81         verifyReplyCode("ALLO", 200)
     82     }
     83 
     84     void testAppe() {
     85         def ORIGINAL_CONTENTS = '123 456 789'
     86         fileSystem.add(new FileEntry(path: FILE1, contents: ORIGINAL_CONTENTS))
     87 
     88         ftpClientConnectAndLogin()
     89 
     90         LOG.info("Put File for local path [$FILE1]")
     91         def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes())
     92         assert ftpClient.appendFile(FILE1, inputStream)
     93         def contents = fileSystem.getEntry(FILE1).createInputStream().text
     94         LOG.info("File contents=[" + contents + "]")
     95         assert contents == ORIGINAL_CONTENTS + ASCII_DATA
     96     }
     97 
     98     void testCdup() {
     99         ftpClientConnectAndLogin()
    100         assert ftpClient.changeToParentDirectory()
    101         verifyReplyCode("changeToParentDirectory", 200)
    102     }
    103 
    104     void testCwd() {
    105         ftpClientConnectAndLogin()
    106         assert ftpClient.changeWorkingDirectory(SUBDIR_NAME)
    107         verifyReplyCode("changeWorkingDirectory", 250)
    108     }
    109 
    110     /**
    111      * Test that a CWD to ".." properly resolves the current dir (without the "..") so that PWD returns the parent
    112      */
    113     void testCwd_DotDot_Pwd() {
    114         ftpClientConnectAndLogin()
    115         assert ftpClient.changeWorkingDirectory("..")
    116         verifyReplyCode("changeWorkingDirectory", 250)
    117         assert p(ftpClient.printWorkingDirectory()) == p(ROOT_DIR)
    118         assert ftpClient.changeWorkingDirectory("home")
    119         assert p(ftpClient.printWorkingDirectory()) == p(HOME_DIR)
    120     }
    121 
    122     /**
    123      * Test that a CWD to "." properly resolves the current dir (without the ".") so that PWD returns the parent
    124      */
    125     void testCwd_Dot_Pwd() {
    126         ftpClientConnectAndLogin()
    127         assert ftpClient.changeWorkingDirectory(".")
    128         verifyReplyCode("changeWorkingDirectory", 250)
    129         assert p(ftpClient.printWorkingDirectory()) == p(HOME_DIR)
    130     }
    131 
    132     void testCwd_UseStaticReplyCommandHandler() {
    133         final int REPLY_CODE = 500;
    134         StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE);
    135         ftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler);
    136 
    137         ftpClientConnectAndLogin()
    138         assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME)
    139         verifyReplyCode("changeWorkingDirectory", REPLY_CODE)
    140     }
    141 
    142     void testCwd_UseStubCommandHandler() {
    143         final int REPLY_CODE = 502;
    144         CwdCommandHandler cwdCommandHandler = new CwdCommandHandler();     // Stub command handler
    145         cwdCommandHandler.setReplyCode(REPLY_CODE);
    146         ftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler);
    147 
    148         ftpClientConnectAndLogin()
    149         assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME)
    150         verifyReplyCode("changeWorkingDirectory", REPLY_CODE)
    151         assert cwdCommandHandler.getInvocation(0)
    152     }
    153 
    154     void testDele() {
    155         fileSystem.add(new FileEntry(FILE1))
    156 
    157         ftpClientConnectAndLogin()
    158         assert ftpClient.deleteFile(FILENAME1)
    159         verifyReplyCode("deleteFile", 250)
    160         assert !fileSystem.exists(FILENAME1)
    161     }
    162 
    163     void testEprt() {
    164         log("Skipping...")
    165 //        ftpClientConnectAndLogin()
    166 //        assert ftpClient.sendCommand("EPRT", "|2|1080::8:800:200C:417A|5282|") == 200
    167     }
    168 
    169     void testEpsv() {
    170         ftpClientConnectAndLogin()
    171         assert ftpClient.sendCommand("EPSV") == 229
    172     }
    173 
    174     void testFeat_UseStaticReplyCommandHandler() {
    175         // The FEAT command is not supported out of the box
    176         StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, "No Features");
    177         ftpServer.setCommandHandler("FEAT", featCommandHandler);
    178 
    179         ftpClientConnectAndLogin()
    180         assert ftpClient.sendCommand("FEAT") == 211
    181     }
    182 
    183     void testHelp() {
    184         ftpServer.helpText = [a: 'aaa', '': 'default']
    185         ftpClientConnect()
    186 
    187         String help = ftpClient.listHelp()
    188         assert help.contains('default')
    189         verifyReplyCode("listHelp", 214)
    190 
    191         help = ftpClient.listHelp('a')
    192         assert help.contains('aaa')
    193         verifyReplyCode("listHelp", 214)
    194 
    195         help = ftpClient.listHelp('bad')
    196         assert help.contains('bad')
    197         verifyReplyCode("listHelp", 214)
    198     }
    199 
    200     void testList() {
    201         def LAST_MODIFIED = new Date()
    202         fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA))
    203         fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2), lastModified: LAST_MODIFIED))
    204 
    205         ftpClientConnectAndLogin()
    206 
    207         FTPFile[] files = ftpClient.listFiles(SUBDIR)
    208         assert files.length == 2
    209 
    210         // Can't be sure of order
    211         FTPFile fileEntry = (files[0].getType() == FTPFile.FILE_TYPE) ? files[0] : files[1]
    212         FTPFile dirEntry = (files[0].getType() == FTPFile.FILE_TYPE) ? files[1] : files[0]
    213         verifyFTPFile(fileEntry, FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size())
    214         verifyFTPFile(dirEntry, FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0)
    215 
    216         verifyReplyCode("list", 226)
    217     }
    218 
    219     void testList_Unix() {
    220         ftpServer.systemName = 'UNIX'
    221         userAccount.homeDirectory = '/'
    222 
    223         def unixFileSystem = new UnixFakeFileSystem()
    224         unixFileSystem.createParentDirectoriesAutomatically = true
    225         unixFileSystem.add(new DirectoryEntry('/'))
    226         ftpServer.fileSystem = unixFileSystem
    227 
    228         def LAST_MODIFIED = new Date()
    229         unixFileSystem.add(new FileEntry(path: p('/', FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA))
    230         unixFileSystem.add(new DirectoryEntry(path: p('/', SUBDIR_NAME2), lastModified: LAST_MODIFIED))
    231 
    232         ftpClientConnectAndLogin()
    233 
    234         FTPFile[] files = ftpClient.listFiles('/')
    235         assert files.length == 2
    236 
    237         // Can't be sure of order
    238         FTPFile fileEntry = (files[0].getType() == FTPFile.FILE_TYPE) ? files[0] : files[1]
    239         FTPFile dirEntry = (files[0].getType() == FTPFile.FILE_TYPE) ? files[1] : files[0]
    240 
    241         verifyFTPFile(dirEntry, FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0)
    242         verifyFTPFile(fileEntry, FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size())
    243         verifyReplyCode("list", 226)
    244     }
    245 
    246     void testLogin() {
    247         ftpClientConnect()
    248         LOG.info("Logging in as $USERNAME/$PASSWORD")
    249         assert ftpClient.login(USERNAME, PASSWORD)
    250         verifyReplyCode("login with $USERNAME/$PASSWORD", 230)
    251 
    252         assertTrue("isStarted", ftpServer.isStarted());
    253         assertFalse("isShutdown", ftpServer.isShutdown());
    254     }
    255 
    256     void testLogin_WithAccount() {
    257         userAccount.accountRequiredForLogin = true
    258         ftpClientConnect()
    259         LOG.info("Logging in as $USERNAME/$PASSWORD with $ACCOUNT")
    260         assert ftpClient.login(USERNAME, PASSWORD, ACCOUNT)
    261         verifyReplyCode("login with $USERNAME/$PASSWORD with $ACCOUNT", 230)
    262     }
    263 
    264     void testMkd() {
    265         ftpClientConnectAndLogin()
    266 
    267         def DIR = p(HOME_DIR, 'NewDir')
    268         assert ftpClient.makeDirectory(DIR)
    269         verifyReplyCode("makeDirectory", 257)
    270         assert fileSystem.isDirectory(DIR)
    271     }
    272 
    273     void testMode() {
    274         ftpClientConnectAndLogin()
    275         assert ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
    276         verifyReplyCode("MODE", 200)
    277     }
    278 
    279     void testNlst() {
    280         fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1)))
    281         fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2)))
    282 
    283         ftpClientConnectAndLogin()
    284 
    285         String[] filenames = ftpClient.listNames(SUBDIR)
    286         assert filenames as Set == [FILENAME1, SUBDIR_NAME2] as Set
    287         verifyReplyCode("listNames", 226)
    288     }
    289 
    290     void testNoop() {
    291         ftpClientConnectAndLogin()
    292         assert ftpClient.sendNoOp()
    293         verifyReplyCode("NOOP", 200)
    294     }
    295 
    296     void testPasv_Nlst() {
    297         fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1)))
    298         fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME2)))
    299 
    300         ftpClientConnectAndLogin()
    301         ftpClient.enterLocalPassiveMode();
    302 
    303         String[] filenames = ftpClient.listNames(SUBDIR)
    304         assert filenames == [FILENAME1, FILENAME2]
    305         verifyReplyCode("listNames", 226)
    306     }
    307 
    308     void testPwd() {
    309         ftpClientConnectAndLogin()
    310         assert ftpClient.printWorkingDirectory() == HOME_DIR
    311         verifyReplyCode("printWorkingDirectory", 257)
    312     }
    313 
    314     void testQuit() {
    315         ftpClientConnect()
    316         ftpClient.quit()
    317         verifyReplyCode("quit", 221)
    318     }
    319 
    320     void testRein() {
    321         ftpClientConnectAndLogin()
    322         assert ftpClient.rein() == 220
    323         assert ftpClient.cdup() == 530      // now logged out
    324     }
    325 
    326     void testRest() {
    327         ftpClientConnectAndLogin()
    328         assert ftpClient.rest("marker") == 350
    329     }
    330 
    331     void testRetr() {
    332         fileSystem.add(new FileEntry(path: FILE1, contents: ASCII_DATA))
    333 
    334         ftpClientConnectAndLogin()
    335 
    336         LOG.info("Get File for remotePath [$FILE1]")
    337         def outputStream = new ByteArrayOutputStream()
    338         assert ftpClient.retrieveFile(FILE1, outputStream)
    339         LOG.info("File contents=[${outputStream.toString()}]")
    340         assert outputStream.toString() == ASCII_DATA
    341     }
    342 
    343     void testRmd() {
    344         ftpClientConnectAndLogin()
    345 
    346         assert ftpClient.removeDirectory(SUBDIR)
    347         verifyReplyCode("removeDirectory", 250)
    348         assert !fileSystem.exists(SUBDIR)
    349     }
    350 
    351     void testRename() {                 // RNFR and RNTO
    352         fileSystem.add(new FileEntry(FILE1))
    353 
    354         ftpClientConnectAndLogin()
    355 
    356         assert ftpClient.rename(FILE1, FILE1 + "NEW")
    357         verifyReplyCode("rename", 250)
    358         assert !fileSystem.exists(FILE1)
    359         assert fileSystem.exists(FILE1 + "NEW")
    360     }
    361 
    362     void testSite() {
    363         ftpClientConnectAndLogin()
    364         assert ftpClient.site("parameters,1,2,3") == 200
    365     }
    366 
    367     void testSmnt() {
    368         ftpClientConnectAndLogin()
    369         assert ftpClient.smnt("dir") == 250
    370     }
    371 
    372     void testStat() {
    373         ftpClientConnectAndLogin()
    374         def status = ftpClient.getStatus()
    375         assert status.contains('Connected')
    376         verifyReplyCode("stat", 211)
    377     }
    378 
    379     void testStor() {
    380         ftpClientConnectAndLogin()
    381 
    382         LOG.info("Put File for local path [$FILE1]")
    383         def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes())
    384         assert ftpClient.storeFile(FILENAME1, inputStream)      // relative to homeDirectory
    385         def contents = fileSystem.getEntry(FILE1).createInputStream().text
    386         LOG.info("File contents=[" + contents + "]")
    387         assert contents == ASCII_DATA
    388     }
    389 
    390     void testStou() {
    391         ftpClientConnectAndLogin()
    392 
    393         def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes())
    394         assert ftpClient.storeUniqueFile(FILENAME1, inputStream)
    395 
    396         def names = fileSystem.listNames(HOME_DIR)
    397         def filename = names.find {name -> name.startsWith(FILENAME1) }
    398         assert filename
    399 
    400         def contents = fileSystem.getEntry(p(HOME_DIR, filename)).createInputStream().text
    401         LOG.info("File contents=[" + contents + "]")
    402         assert contents == ASCII_DATA
    403     }
    404 
    405     void testStru() {
    406         ftpClientConnectAndLogin()
    407         assert ftpClient.setFileStructure(FTP.FILE_STRUCTURE);
    408         verifyReplyCode("STRU", 200)
    409     }
    410 
    411     void testSyst() {
    412         ftpClientConnectAndLogin()
    413 
    414         def systemName = ftpClient.getSystemName()
    415         LOG.info("system name = [$systemName]")
    416         assert systemName.contains('"' + SYSTEM_NAME + '"')
    417         verifyReplyCode("getSystemName", 215)
    418     }
    419 
    420     void testType() {
    421         ftpClientConnectAndLogin()
    422         assert ftpClient.type(FTP.ASCII_FILE_TYPE)
    423         verifyReplyCode("TYPE", 200)
    424     }
    425 
    426     void testUnrecognizedCommand() {
    427         ftpClientConnectAndLogin()
    428         assert ftpClient.sendCommand("XXX") == 502
    429         verifyReplyCode("XXX", 502)
    430     }
    431 
    432     // -------------------------------------------------------------------------
    433     // Test setup and tear-down
    434     // -------------------------------------------------------------------------
    435 
    436     /**
    437      * Perform initialization before each test
    438      * @see org.mockftpserver.test.AbstractTestCase#setUp()
    439      */
    440     void setUp() {
    441         super.setUp()
    442 
    443         for (int i = 0; i < BINARY_DATA.length; i++) {
    444             BINARY_DATA[i] = (byte) i
    445         }
    446 
    447         ftpServer = new FakeFtpServer()
    448         ftpServer.serverControlPort = PortTestUtil.getFtpServerControlPort()
    449         ftpServer.systemName = SYSTEM_NAME
    450 
    451         fileSystem = new WindowsFakeFileSystem()
    452         fileSystem.createParentDirectoriesAutomatically = true
    453         fileSystem.add(new DirectoryEntry(SUBDIR))
    454         ftpServer.fileSystem = fileSystem
    455 
    456         userAccount = new UserAccount(USERNAME, PASSWORD, HOME_DIR)
    457         ftpServer.addUserAccount(userAccount)
    458 
    459         ftpServer.start()
    460         ftpClient = new FTPClient()
    461     }
    462 
    463     /**
    464      * Perform cleanup after each test
    465      * @see org.mockftpserver.test.AbstractTestCase#tearDown()
    466      */
    467     void tearDown() {
    468         super.tearDown()
    469         ftpServer.stop()
    470     }
    471 
    472     // -------------------------------------------------------------------------
    473     // Internal Helper Methods
    474     // -------------------------------------------------------------------------
    475 
    476     private ftpClientConnectAndLogin() {
    477         ftpClientConnect()
    478         assert ftpClient.login(USERNAME, PASSWORD)
    479     }
    480 
    481     /**
    482      * Connect to the server from the FTPClient
    483      */
    484     private void ftpClientConnect() {
    485         def port = PortTestUtil.getFtpServerControlPort()
    486         LOG.info("Conecting to $SERVER on port $port")
    487         ftpClient.connect(SERVER, port)
    488         verifyReplyCode("connect", 220)
    489     }
    490 
    491     /**
    492      * Assert that the FtpClient reply code is equal to the expected value
    493      *
    494      * @param operation - the description of the operation performed used in the error message
    495      * @param expectedReplyCode - the expected FtpClient reply code
    496      */
    497     private void verifyReplyCode(String operation, int expectedReplyCode) {
    498         int replyCode = ftpClient.getReplyCode()
    499         LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode)
    500         assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode)
    501     }
    502 
    503     private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) {
    504         LOG.info(ftpFile.toString())
    505         assertEquals("type: " + ftpFile, type, ftpFile.getType())
    506         assertEquals("name: " + ftpFile, name, ftpFile.getName())
    507         assertEquals("size: " + ftpFile, size, ftpFile.getSize())
    508     }
    509 
    510 }