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