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