StubFtpServerIntegrationTest.java revision 3e469b93fd10bc09ea2c088516168bf6a5cbaa43
1/* 2 * Copyright 2007 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.stub; 17 18import org.apache.commons.net.ftp.FTP; 19import org.apache.commons.net.ftp.FTPClient; 20import org.apache.commons.net.ftp.FTPFile; 21import org.apache.log4j.Logger; 22import org.mockftpserver.core.command.CommandHandler; 23import org.mockftpserver.core.command.CommandNames; 24import org.mockftpserver.core.command.InvocationRecord; 25import org.mockftpserver.core.command.SimpleCompositeCommandHandler; 26import org.mockftpserver.core.command.StaticReplyCommandHandler; 27import org.mockftpserver.stub.command.*; 28import org.mockftpserver.test.AbstractTest; 29import org.mockftpserver.test.IntegrationTest; 30import org.mockftpserver.test.PortTestUtil; 31 32import java.io.ByteArrayInputStream; 33import java.io.ByteArrayOutputStream; 34import java.io.IOException; 35 36/** 37 * Tests for StubFtpServer using the Apache Jakarta Commons Net FTP client. 38 * 39 * @author Chris Mair 40 * @version $Revision$ - $Date$ 41 */ 42public final class StubFtpServerIntegrationTest extends AbstractTest implements IntegrationTest { 43 44 private static final Logger LOG = Logger.getLogger(StubFtpServerIntegrationTest.class); 45 private static final String SERVER = "localhost"; 46 private static final String USERNAME = "user123"; 47 private static final String PASSWORD = "password"; 48 private static final String FILENAME = "abc.txt"; 49 private static final String ASCII_CONTENTS = "abcdef\tghijklmnopqr"; 50 private static final byte[] BINARY_CONTENTS = new byte[256]; 51 52 private StubFtpServer stubFtpServer; 53 private FTPClient ftpClient; 54 private RetrCommandHandler retrCommandHandler; 55 private StorCommandHandler storCommandHandler; 56 57 //------------------------------------------------------------------------- 58 // Tests 59 //------------------------------------------------------------------------- 60 61 public void testLogin() throws Exception { 62 // Connect 63 LOG.info("Conecting to " + SERVER); 64 ftpClientConnect(); 65 verifyReplyCode("connect", 220); 66 67 // Login 68 String userAndPassword = USERNAME + "/" + PASSWORD; 69 LOG.info("Logging in as " + userAndPassword); 70 boolean success = ftpClient.login(USERNAME, PASSWORD); 71 assertTrue("Unable to login with " + userAndPassword, success); 72 verifyReplyCode("login with " + userAndPassword, 230); 73 74 assertTrue("isStarted", stubFtpServer.isStarted()); 75 assertFalse("isShutdown", stubFtpServer.isShutdown()); 76 77 // Quit 78 LOG.info("Quit"); 79 ftpClient.quit(); 80 verifyReplyCode("quit", 221); 81 } 82 83 public void testAcct() throws Exception { 84 ftpClientConnect(); 85 86 // ACCT 87 int replyCode = ftpClient.acct("123456"); 88 assertEquals("acct", 230, replyCode); 89 } 90 91 /** 92 * Test the stop() method when no session has ever been started 93 */ 94 public void testStop_NoSessionEverStarted() throws Exception { 95 LOG.info("Testing a stop() when no session has ever been started"); 96 } 97 98 public void testHelp() throws Exception { 99 // Modify HELP CommandHandler to return a predefined help message 100 final String HELP = "help message"; 101 HelpCommandHandler helpCommandHandler = (HelpCommandHandler) stubFtpServer.getCommandHandler(CommandNames.HELP); 102 helpCommandHandler.setHelpMessage(HELP); 103 104 ftpClientConnect(); 105 106 // HELP 107 String help = ftpClient.listHelp(); 108 assertTrue("Wrong response", help.indexOf(HELP) != -1); 109 verifyReplyCode("listHelp", 214); 110 } 111 112 /** 113 * Test the LIST and SYST commands. 114 */ 115 public void testList() throws Exception { 116 ftpClientConnect(); 117 118 // Set directory listing 119 ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST); 120 listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log\n" 121 + "11-01-01 1:30PM <DIR> 0 archive"); 122 123 // LIST 124 FTPFile[] files = ftpClient.listFiles(); 125 assertEquals("number of files", 2, files.length); 126 verifyFTPFile(files[0], FTPFile.FILE_TYPE, "File2350.log", 406348L); 127 verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, "archive", 0L); 128 verifyReplyCode("list", 226); 129 } 130 131 /** 132 * Test the LIST, PASV and SYST commands, transferring a directory listing in passive mode 133 */ 134 public void testList_PassiveMode() throws Exception { 135 ftpClientConnect(); 136 137 ftpClient.enterLocalPassiveMode(); 138 139 // Set directory listing 140 ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST); 141 listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log"); 142 143 // LIST 144 FTPFile[] files = ftpClient.listFiles(); 145 assertEquals("number of files", 1, files.length); 146 verifyReplyCode("list", 226); 147 } 148 149 public void testNlst() throws Exception { 150 ftpClientConnect(); 151 152 // Set directory listing 153 NlstCommandHandler nlstCommandHandler = (NlstCommandHandler) stubFtpServer.getCommandHandler(CommandNames.NLST); 154 nlstCommandHandler.setDirectoryListing("File1.txt\nfile2.data"); 155 156 // NLST 157 String[] filenames = ftpClient.listNames(); 158 assertEquals("number of files", 2, filenames.length); 159 assertEquals(filenames[0], "File1.txt"); 160 assertEquals(filenames[1], "file2.data"); 161 verifyReplyCode("listNames", 226); 162 } 163 164 /** 165 * Test printing the current working directory (PWD) 166 */ 167 public void testPwd() throws Exception { 168 // Modify PWD CommandHandler to return a predefined directory 169 final String DIR = "some/dir"; 170 PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler(CommandNames.PWD); 171 pwdCommandHandler.setDirectory(DIR); 172 173 ftpClientConnect(); 174 175 // PWD 176 String dir = ftpClient.printWorkingDirectory(); 177 assertEquals("Unable to PWD", DIR, dir); 178 verifyReplyCode("printWorkingDirectory", 257); 179 } 180 181 public void testStat() throws Exception { 182 // Modify Stat CommandHandler to return predefined text 183 final String STATUS = "some information 123"; 184 StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT); 185 statCommandHandler.setStatus(STATUS); 186 187 ftpClientConnect(); 188 189 // STAT 190 String status = ftpClient.getStatus(); 191 assertEquals("STAT reply", "211 " + STATUS + ".", status.trim()); 192 verifyReplyCode("getStatus", 211); 193 } 194 195 /** 196 * Test getting the status (STAT), when the reply text contains multiple lines 197 */ 198 public void testStat_MultilineReplyText() throws Exception { 199 // Modify Stat CommandHandler to return predefined text 200 final String STATUS = "System name: abc.def\nVersion 3.5.7\nNumber of failed logins: 2"; 201 final String FORMATTED_REPLY_STATUS = "211-System name: abc.def\r\nVersion 3.5.7\r\n211 Number of failed logins: 2."; 202 StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT); 203 statCommandHandler.setStatus(STATUS); 204 205 ftpClientConnect(); 206 207 // STAT 208 String status = ftpClient.getStatus(); 209 assertEquals("STAT reply", FORMATTED_REPLY_STATUS, status.trim()); 210 verifyReplyCode("getStatus", 211); 211 } 212 213 public void testSyst() throws Exception { 214 ftpClientConnect(); 215 216 // SYST 217 assertEquals("getSystemName()", "\"WINDOWS\" system type.", ftpClient.getSystemName()); 218 verifyReplyCode("syst", 215); 219 } 220 221 public void testCwd() throws Exception { 222 // Connect 223 LOG.info("Conecting to " + SERVER); 224 ftpClientConnect(); 225 verifyReplyCode("connect", 220); 226 227 // CWD 228 boolean success = ftpClient.changeWorkingDirectory("dir1/dir2"); 229 assertTrue("Unable to CWD", success); 230 verifyReplyCode("changeWorkingDirectory", 250); 231 } 232 233 /** 234 * Test changing the current working directory (CWD), when it causes a remote error 235 */ 236 public void testCwd_Error() throws Exception { 237 // Override CWD CommandHandler to return error reply code 238 final int REPLY_CODE = 500; 239 StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE); 240 stubFtpServer.setCommandHandler("CWD", cwdCommandHandler); 241 242 ftpClientConnect(); 243 244 // CWD 245 boolean success = ftpClient.changeWorkingDirectory("dir1/dir2"); 246 assertFalse("Expected failure", success); 247 verifyReplyCode("changeWorkingDirectory", REPLY_CODE); 248 } 249 250 public void testCdup() throws Exception { 251 ftpClientConnect(); 252 253 // CDUP 254 boolean success = ftpClient.changeToParentDirectory(); 255 assertTrue("Unable to CDUP", success); 256 verifyReplyCode("changeToParentDirectory", 200); 257 } 258 259 public void testDele() throws Exception { 260 ftpClientConnect(); 261 262 // DELE 263 boolean success = ftpClient.deleteFile(FILENAME); 264 assertTrue("Unable to DELE", success); 265 verifyReplyCode("deleteFile", 250); 266 } 267 268 public void testEprt() throws Exception { 269 ftpClientConnect(); 270 ftpClient.sendCommand("EPRT", "|2|1080::8:800:200C:417A|5282|"); 271 verifyReplyCode("EPRT", 200); 272 } 273 274 public void testEpsv() throws Exception { 275 ftpClientConnect(); 276 ftpClient.sendCommand("EPSV"); 277 verifyReplyCode("EPSV", 229); 278 } 279 280 public void testFeat_UseStaticReplyCommandHandler() throws IOException { 281 // The FEAT command is not supported out of the box 282 final String FEAT_TEXT = "Extensions supported:\n" + 283 "MLST size*;create;modify*;perm;media-type\n" + 284 "SIZE\n" + 285 "COMPRESSION\n" + 286 "END"; 287 StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, FEAT_TEXT); 288 stubFtpServer.setCommandHandler("FEAT", featCommandHandler); 289 290 ftpClientConnect(); 291 assertEquals(ftpClient.sendCommand("FEAT"), 211); 292 LOG.info(ftpClient.getReplyString()); 293 } 294 295 public void testMkd() throws Exception { 296 ftpClientConnect(); 297 298 // MKD 299 boolean success = ftpClient.makeDirectory("dir1/dir2"); 300 assertTrue("Unable to CWD", success); 301 verifyReplyCode("makeDirectory", 257); 302 } 303 304 public void testNoop() throws Exception { 305 ftpClientConnect(); 306 307 // NOOP 308 boolean success = ftpClient.sendNoOp(); 309 assertTrue("Unable to NOOP", success); 310 verifyReplyCode("NOOP", 200); 311 } 312 313 public void testRest() throws Exception { 314 ftpClientConnect(); 315 316 // REST 317 int replyCode = ftpClient.rest("marker"); 318 assertEquals("Unable to REST", 350, replyCode); 319 } 320 321 public void testRmd() throws Exception { 322 ftpClientConnect(); 323 324 // RMD 325 boolean success = ftpClient.removeDirectory("dir1/dir2"); 326 assertTrue("Unable to RMD", success); 327 verifyReplyCode("removeDirectory", 250); 328 } 329 330 public void testRename() throws Exception { 331 ftpClientConnect(); 332 333 // Rename (RNFR, RNTO) 334 boolean success = ftpClient.rename(FILENAME, "new_" + FILENAME); 335 assertTrue("Unable to RENAME", success); 336 verifyReplyCode("rename", 250); 337 } 338 339 public void testAllo() throws Exception { 340 ftpClientConnect(); 341 342 // ALLO 343 assertTrue("ALLO", ftpClient.allocate(1024)); 344 assertTrue("ALLO with recordSize", ftpClient.allocate(1024, 64)); 345 } 346 347 /** 348 * Test GET and PUT of ASCII files 349 */ 350 public void testTransferAsciiFile() throws Exception { 351 retrCommandHandler.setFileContents(ASCII_CONTENTS); 352 353 ftpClientConnect(); 354 355 // Get File 356 LOG.info("Get File for remotePath [" + FILENAME + "]"); 357 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 358 assertTrue(ftpClient.retrieveFile(FILENAME, outputStream)); 359 LOG.info("File contents=[" + outputStream.toString()); 360 assertEquals("File contents", ASCII_CONTENTS, outputStream.toString()); 361 362 // Put File 363 LOG.info("Put File for local path [" + FILENAME + "]"); 364 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); 365 assertTrue(ftpClient.storeFile(FILENAME, inputStream)); 366 InvocationRecord invocationRecord = storCommandHandler.getInvocation(0); 367 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); 368 LOG.info("File contents=[" + contents + "]"); 369 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); 370 } 371 372 /** 373 * Test GET and PUT of binary files 374 */ 375 public void testTransferBinaryFiles() throws Exception { 376 retrCommandHandler.setFileContents(BINARY_CONTENTS); 377 378 ftpClientConnect(); 379 ftpClient.setFileType(FTP.BINARY_FILE_TYPE); 380 381 // Get File 382 LOG.info("Get File for remotePath [" + FILENAME + "]"); 383 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 384 assertTrue("GET", ftpClient.retrieveFile(FILENAME, outputStream)); 385 LOG.info("GET File length=" + outputStream.size()); 386 assertEquals("File contents", BINARY_CONTENTS, outputStream.toByteArray()); 387 388 // Put File 389 LOG.info("Put File for local path [" + FILENAME + "]"); 390 ByteArrayInputStream inputStream = new ByteArrayInputStream(BINARY_CONTENTS); 391 assertTrue("PUT", ftpClient.storeFile(FILENAME, inputStream)); 392 InvocationRecord invocationRecord = storCommandHandler.getInvocation(0); 393 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); 394 LOG.info("PUT File length=" + contents.length); 395 assertEquals("File contents", BINARY_CONTENTS, contents); 396 } 397 398 public void testStou() throws Exception { 399 StouCommandHandler stouCommandHandler = (StouCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOU); 400 stouCommandHandler.setFilename(FILENAME); 401 402 ftpClientConnect(); 403 404 // Stor a File (STOU) 405 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); 406 assertTrue(ftpClient.storeUniqueFile(FILENAME, inputStream)); 407 InvocationRecord invocationRecord = stouCommandHandler.getInvocation(0); 408 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); 409 LOG.info("File contents=[" + contents + "]"); 410 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); 411 } 412 413 public void testAppe() throws Exception { 414 AppeCommandHandler appeCommandHandler = (AppeCommandHandler) stubFtpServer.getCommandHandler(CommandNames.APPE); 415 416 ftpClientConnect(); 417 418 // Append a File (APPE) 419 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); 420 assertTrue(ftpClient.appendFile(FILENAME, inputStream)); 421 InvocationRecord invocationRecord = appeCommandHandler.getInvocation(0); 422 byte[] contents = (byte[]) invocationRecord.getObject(AppeCommandHandler.FILE_CONTENTS_KEY); 423 LOG.info("File contents=[" + contents + "]"); 424 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); 425 } 426 427 public void testAbor() throws Exception { 428 ftpClientConnect(); 429 430 // ABOR 431 assertTrue("ABOR", ftpClient.abort()); 432 } 433 434 public void testPasv() throws Exception { 435 ftpClientConnect(); 436 437 // PASV 438 ftpClient.enterLocalPassiveMode(); 439 // no reply code; the PASV command is sent only when the data connection is opened 440 } 441 442 public void testMode() throws Exception { 443 ftpClientConnect(); 444 445 // MODE 446 boolean success = ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); 447 assertTrue("Unable to MODE", success); 448 verifyReplyCode("setFileTransferMode", 200); 449 } 450 451 public void testStru() throws Exception { 452 ftpClientConnect(); 453 454 // STRU 455 boolean success = ftpClient.setFileStructure(FTP.FILE_STRUCTURE); 456 assertTrue("Unable to STRU", success); 457 verifyReplyCode("setFileStructure", 200); 458 } 459 460 public void testSimpleCompositeCommandHandler() throws Exception { 461 // Replace CWD CommandHandler with a SimpleCompositeCommandHandler 462 CommandHandler commandHandler1 = new StaticReplyCommandHandler(500); 463 CommandHandler commandHandler2 = new CwdCommandHandler(); 464 SimpleCompositeCommandHandler simpleCompositeCommandHandler = new SimpleCompositeCommandHandler(); 465 simpleCompositeCommandHandler.addCommandHandler(commandHandler1); 466 simpleCompositeCommandHandler.addCommandHandler(commandHandler2); 467 stubFtpServer.setCommandHandler("CWD", simpleCompositeCommandHandler); 468 469 // Connect 470 ftpClientConnect(); 471 472 // CWD 473 assertFalse("first", ftpClient.changeWorkingDirectory("dir1/dir2")); 474 assertTrue("first", ftpClient.changeWorkingDirectory("dir1/dir2")); 475 } 476 477 public void testSite() throws Exception { 478 ftpClientConnect(); 479 480 // SITE 481 int replyCode = ftpClient.site("parameters,1,2,3"); 482 assertEquals("SITE", 200, replyCode); 483 } 484 485 public void testSmnt() throws Exception { 486 ftpClientConnect(); 487 488 // SMNT 489 assertTrue("SMNT", ftpClient.structureMount("dir1/dir2")); 490 verifyReplyCode("structureMount", 250); 491 } 492 493 public void testRein() throws Exception { 494 ftpClientConnect(); 495 496 // REIN 497 assertEquals("REIN", 220, ftpClient.rein()); 498 } 499 500 /** 501 * Test that command names in lowercase or mixed upper/lower case are accepted 502 */ 503 public void testCommandNamesInLowerOrMixedCase() throws Exception { 504 ftpClientConnect(); 505 506 assertEquals("rein", 220, ftpClient.sendCommand("rein")); 507 assertEquals("rEIn", 220, ftpClient.sendCommand("rEIn")); 508 assertEquals("reiN", 220, ftpClient.sendCommand("reiN")); 509 assertEquals("Rein", 220, ftpClient.sendCommand("Rein")); 510 } 511 512 public void testUnrecognizedCommand() throws Exception { 513 ftpClientConnect(); 514 515 assertEquals("Unrecognized:XXXX", 502, ftpClient.sendCommand("XXXX")); 516 } 517 518 // ------------------------------------------------------------------------- 519 // Test setup and tear-down 520 // ------------------------------------------------------------------------- 521 522 /** 523 * Perform initialization before each test 524 * 525 * @see org.mockftpserver.test.AbstractTest#setUp() 526 */ 527 protected void setUp() throws Exception { 528 super.setUp(); 529 530 for (int i = 0; i < BINARY_CONTENTS.length; i++) { 531 BINARY_CONTENTS[i] = (byte) i; 532 } 533 534 stubFtpServer = new StubFtpServer(); 535 stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort()); 536 stubFtpServer.start(); 537 ftpClient = new FTPClient(); 538 retrCommandHandler = (RetrCommandHandler) stubFtpServer.getCommandHandler(CommandNames.RETR); 539 storCommandHandler = (StorCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOR); 540 } 541 542 /** 543 * Perform cleanup after each test 544 * 545 * @see org.mockftpserver.test.AbstractTest#tearDown() 546 */ 547 protected void tearDown() throws Exception { 548 super.tearDown(); 549 stubFtpServer.stop(); 550 } 551 552 // ------------------------------------------------------------------------- 553 // Internal Helper Methods 554 // ------------------------------------------------------------------------- 555 556 /** 557 * Connect to the server from the FTPClient 558 */ 559 private void ftpClientConnect() throws IOException { 560 ftpClient.connect(SERVER, PortTestUtil.getFtpServerControlPort()); 561 } 562 563 /** 564 * Assert that the FtpClient reply code is equal to the expected value 565 * 566 * @param operation - the description of the operation performed; used in the error message 567 * @param expectedReplyCode - the expected FtpClient reply code 568 */ 569 private void verifyReplyCode(String operation, int expectedReplyCode) { 570 int replyCode = ftpClient.getReplyCode(); 571 LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode); 572 assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode); 573 } 574 575 /** 576 * Verify that the FTPFile has the specified properties 577 * 578 * @param ftpFile - the FTPFile to verify 579 * @param type - the expected file type 580 * @param name - the expected file name 581 * @param size - the expected file size (will be zero for a directory) 582 */ 583 private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) { 584 LOG.info(ftpFile); 585 assertEquals("type: " + ftpFile, type, ftpFile.getType()); 586 assertEquals("name: " + ftpFile, name, ftpFile.getName()); 587 assertEquals("size: " + ftpFile, size, ftpFile.getSize()); 588 } 589 590} 591