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