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