/* * Copyright 2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.mockftpserver.stub; import org.apache.commons.net.ftp.FTP; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; import org.apache.log4j.Logger; import org.mockftpserver.core.command.CommandHandler; import org.mockftpserver.core.command.CommandNames; import org.mockftpserver.core.command.InvocationRecord; import org.mockftpserver.core.command.SimpleCompositeCommandHandler; import org.mockftpserver.core.command.StaticReplyCommandHandler; import org.mockftpserver.stub.command.*; import org.mockftpserver.test.*; import org.mockftpserver.test.AbstractTestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * Tests for StubFtpServer using the Apache Jakarta Commons Net FTP client. * * @author Chris Mair * @version $Revision$ - $Date$ */ public final class StubFtpServerIntegrationTest extends AbstractTestCase implements IntegrationTest { private static final Logger LOG = Logger.getLogger(StubFtpServerIntegrationTest.class); private static final String SERVER = "localhost"; private static final String USERNAME = "user123"; private static final String PASSWORD = "password"; private static final String FILENAME = "abc.txt"; private static final String ASCII_CONTENTS = "abcdef\tghijklmnopqr"; private static final byte[] BINARY_CONTENTS = new byte[256]; private StubFtpServer stubFtpServer; private FTPClient ftpClient; private RetrCommandHandler retrCommandHandler; private StorCommandHandler storCommandHandler; //------------------------------------------------------------------------- // Tests //------------------------------------------------------------------------- public void testLogin() throws Exception { // Connect LOG.info("Conecting to " + SERVER); ftpClientConnect(); verifyReplyCode("connect", 220); // Login String userAndPassword = USERNAME + "/" + PASSWORD; LOG.info("Logging in as " + userAndPassword); boolean success = ftpClient.login(USERNAME, PASSWORD); assertTrue("Unable to login with " + userAndPassword, success); verifyReplyCode("login with " + userAndPassword, 230); assertTrue("isStarted", stubFtpServer.isStarted()); assertFalse("isShutdown", stubFtpServer.isShutdown()); // Quit LOG.info("Quit"); ftpClient.quit(); verifyReplyCode("quit", 221); } public void testAcct() throws Exception { ftpClientConnect(); // ACCT int replyCode = ftpClient.acct("123456"); assertEquals("acct", 230, replyCode); } /** * Test the stop() method when no session has ever been started */ public void testStop_NoSessionEverStarted() throws Exception { LOG.info("Testing a stop() when no session has ever been started"); } public void testHelp() throws Exception { // Modify HELP CommandHandler to return a predefined help message final String HELP = "help message"; HelpCommandHandler helpCommandHandler = (HelpCommandHandler) stubFtpServer.getCommandHandler(CommandNames.HELP); helpCommandHandler.setHelpMessage(HELP); ftpClientConnect(); // HELP String help = ftpClient.listHelp(); assertTrue("Wrong response", help.indexOf(HELP) != -1); verifyReplyCode("listHelp", 214); } /** * Test the LIST and SYST commands. */ public void testList() throws Exception { ftpClientConnect(); // Set directory listing ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST); listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log\n" + "11-01-01 1:30PM archive"); // LIST FTPFile[] files = ftpClient.listFiles(); assertEquals("number of files", 2, files.length); verifyFTPFile(files[0], FTPFile.FILE_TYPE, "File2350.log", 406348L); verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, "archive", 0L); verifyReplyCode("list", 226); } /** * Test the LIST, PASV and SYST commands, transferring a directory listing in passive mode */ public void testList_PassiveMode() throws Exception { ftpClientConnect(); ftpClient.enterLocalPassiveMode(); // Set directory listing ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST); listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log"); // LIST FTPFile[] files = ftpClient.listFiles(); assertEquals("number of files", 1, files.length); verifyReplyCode("list", 226); } public void testNlst() throws Exception { ftpClientConnect(); // Set directory listing NlstCommandHandler nlstCommandHandler = (NlstCommandHandler) stubFtpServer.getCommandHandler(CommandNames.NLST); nlstCommandHandler.setDirectoryListing("File1.txt\nfile2.data"); // NLST String[] filenames = ftpClient.listNames(); assertEquals("number of files", 2, filenames.length); assertEquals(filenames[0], "File1.txt"); assertEquals(filenames[1], "file2.data"); verifyReplyCode("listNames", 226); } /** * Test printing the current working directory (PWD) */ public void testPwd() throws Exception { // Modify PWD CommandHandler to return a predefined directory final String DIR = "some/dir"; PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler(CommandNames.PWD); pwdCommandHandler.setDirectory(DIR); ftpClientConnect(); // PWD String dir = ftpClient.printWorkingDirectory(); assertEquals("Unable to PWD", DIR, dir); verifyReplyCode("printWorkingDirectory", 257); } public void testStat() throws Exception { // Modify Stat CommandHandler to return predefined text final String STATUS = "some information 123"; StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT); statCommandHandler.setStatus(STATUS); ftpClientConnect(); // STAT String status = ftpClient.getStatus(); assertEquals("STAT reply", "211 " + STATUS + ".", status.trim()); verifyReplyCode("getStatus", 211); } /** * Test getting the status (STAT), when the reply text contains multiple lines */ public void testStat_MultilineReplyText() throws Exception { // Modify Stat CommandHandler to return predefined text final String STATUS = "System name: abc.def\nVersion 3.5.7\nNumber of failed logins: 2"; final String FORMATTED_REPLY_STATUS = "211-System name: abc.def\r\nVersion 3.5.7\r\n211 Number of failed logins: 2."; StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT); statCommandHandler.setStatus(STATUS); ftpClientConnect(); // STAT String status = ftpClient.getStatus(); assertEquals("STAT reply", FORMATTED_REPLY_STATUS, status.trim()); verifyReplyCode("getStatus", 211); } public void testSyst() throws Exception { ftpClientConnect(); // SYST assertEquals("getSystemName()", "\"WINDOWS\" system type.", ftpClient.getSystemName()); verifyReplyCode("syst", 215); } public void testCwd() throws Exception { // Connect LOG.info("Conecting to " + SERVER); ftpClientConnect(); verifyReplyCode("connect", 220); // CWD boolean success = ftpClient.changeWorkingDirectory("dir1/dir2"); assertTrue("Unable to CWD", success); verifyReplyCode("changeWorkingDirectory", 250); } /** * Test changing the current working directory (CWD), when it causes a remote error */ public void testCwd_Error() throws Exception { // Override CWD CommandHandler to return error reply code final int REPLY_CODE = 500; StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE); stubFtpServer.setCommandHandler("CWD", cwdCommandHandler); ftpClientConnect(); // CWD boolean success = ftpClient.changeWorkingDirectory("dir1/dir2"); assertFalse("Expected failure", success); verifyReplyCode("changeWorkingDirectory", REPLY_CODE); } public void testCdup() throws Exception { ftpClientConnect(); // CDUP boolean success = ftpClient.changeToParentDirectory(); assertTrue("Unable to CDUP", success); verifyReplyCode("changeToParentDirectory", 200); } public void testDele() throws Exception { ftpClientConnect(); // DELE boolean success = ftpClient.deleteFile(FILENAME); assertTrue("Unable to DELE", success); verifyReplyCode("deleteFile", 250); } public void testEprt() throws Exception { ftpClientConnect(); ftpClient.sendCommand("EPRT", "|2|1080::8:800:200C:417A|5282|"); verifyReplyCode("EPRT", 200); } public void testEpsv() throws Exception { ftpClientConnect(); ftpClient.sendCommand("EPSV"); verifyReplyCode("EPSV", 229); } public void testFeat_UseStaticReplyCommandHandler() throws IOException { // The FEAT command is not supported out of the box final String FEAT_TEXT = "Extensions supported:\n" + "MLST size*;create;modify*;perm;media-type\n" + "SIZE\n" + "COMPRESSION\n" + "END"; StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, FEAT_TEXT); stubFtpServer.setCommandHandler("FEAT", featCommandHandler); ftpClientConnect(); assertEquals(ftpClient.sendCommand("FEAT"), 211); LOG.info(ftpClient.getReplyString()); } public void testMkd() throws Exception { ftpClientConnect(); // MKD boolean success = ftpClient.makeDirectory("dir1/dir2"); assertTrue("Unable to CWD", success); verifyReplyCode("makeDirectory", 257); } public void testNoop() throws Exception { ftpClientConnect(); // NOOP boolean success = ftpClient.sendNoOp(); assertTrue("Unable to NOOP", success); verifyReplyCode("NOOP", 200); } public void testRest() throws Exception { ftpClientConnect(); // REST int replyCode = ftpClient.rest("marker"); assertEquals("Unable to REST", 350, replyCode); } public void testRmd() throws Exception { ftpClientConnect(); // RMD boolean success = ftpClient.removeDirectory("dir1/dir2"); assertTrue("Unable to RMD", success); verifyReplyCode("removeDirectory", 250); } public void testRename() throws Exception { ftpClientConnect(); // Rename (RNFR, RNTO) boolean success = ftpClient.rename(FILENAME, "new_" + FILENAME); assertTrue("Unable to RENAME", success); verifyReplyCode("rename", 250); } public void testAllo() throws Exception { ftpClientConnect(); // ALLO assertTrue("ALLO", ftpClient.allocate(1024)); assertTrue("ALLO with recordSize", ftpClient.allocate(1024, 64)); } /** * Test GET and PUT of ASCII files */ public void testTransferAsciiFile() throws Exception { retrCommandHandler.setFileContents(ASCII_CONTENTS); ftpClientConnect(); // Get File LOG.info("Get File for remotePath [" + FILENAME + "]"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); assertTrue(ftpClient.retrieveFile(FILENAME, outputStream)); LOG.info("File contents=[" + outputStream.toString()); assertEquals("File contents", ASCII_CONTENTS, outputStream.toString()); // Put File LOG.info("Put File for local path [" + FILENAME + "]"); ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); assertTrue(ftpClient.storeFile(FILENAME, inputStream)); InvocationRecord invocationRecord = storCommandHandler.getInvocation(0); byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); LOG.info("File contents=[" + contents + "]"); assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); } /** * Test GET and PUT of binary files */ public void testTransferBinaryFiles() throws Exception { retrCommandHandler.setFileContents(BINARY_CONTENTS); ftpClientConnect(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // Get File LOG.info("Get File for remotePath [" + FILENAME + "]"); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); assertTrue("GET", ftpClient.retrieveFile(FILENAME, outputStream)); LOG.info("GET File length=" + outputStream.size()); assertEquals("File contents", BINARY_CONTENTS, outputStream.toByteArray()); // Put File LOG.info("Put File for local path [" + FILENAME + "]"); ByteArrayInputStream inputStream = new ByteArrayInputStream(BINARY_CONTENTS); assertTrue("PUT", ftpClient.storeFile(FILENAME, inputStream)); InvocationRecord invocationRecord = storCommandHandler.getInvocation(0); byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); LOG.info("PUT File length=" + contents.length); assertEquals("File contents", BINARY_CONTENTS, contents); } public void testStou() throws Exception { StouCommandHandler stouCommandHandler = (StouCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOU); stouCommandHandler.setFilename(FILENAME); ftpClientConnect(); // Stor a File (STOU) ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); assertTrue(ftpClient.storeUniqueFile(FILENAME, inputStream)); InvocationRecord invocationRecord = stouCommandHandler.getInvocation(0); byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY); LOG.info("File contents=[" + contents + "]"); assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); } public void testAppe() throws Exception { AppeCommandHandler appeCommandHandler = (AppeCommandHandler) stubFtpServer.getCommandHandler(CommandNames.APPE); ftpClientConnect(); // Append a File (APPE) ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes()); assertTrue(ftpClient.appendFile(FILENAME, inputStream)); InvocationRecord invocationRecord = appeCommandHandler.getInvocation(0); byte[] contents = (byte[]) invocationRecord.getObject(AppeCommandHandler.FILE_CONTENTS_KEY); LOG.info("File contents=[" + contents + "]"); assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents); } public void testAbor() throws Exception { ftpClientConnect(); // ABOR assertTrue("ABOR", ftpClient.abort()); } public void testPasv() throws Exception { ftpClientConnect(); // PASV ftpClient.enterLocalPassiveMode(); // no reply code; the PASV command is sent only when the data connection is opened } public void testMode() throws Exception { ftpClientConnect(); // MODE boolean success = ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); assertTrue("Unable to MODE", success); verifyReplyCode("setFileTransferMode", 200); } public void testStru() throws Exception { ftpClientConnect(); // STRU boolean success = ftpClient.setFileStructure(FTP.FILE_STRUCTURE); assertTrue("Unable to STRU", success); verifyReplyCode("setFileStructure", 200); } public void testSimpleCompositeCommandHandler() throws Exception { // Replace CWD CommandHandler with a SimpleCompositeCommandHandler CommandHandler commandHandler1 = new StaticReplyCommandHandler(500); CommandHandler commandHandler2 = new CwdCommandHandler(); SimpleCompositeCommandHandler simpleCompositeCommandHandler = new SimpleCompositeCommandHandler(); simpleCompositeCommandHandler.addCommandHandler(commandHandler1); simpleCompositeCommandHandler.addCommandHandler(commandHandler2); stubFtpServer.setCommandHandler("CWD", simpleCompositeCommandHandler); // Connect ftpClientConnect(); // CWD assertFalse("first", ftpClient.changeWorkingDirectory("dir1/dir2")); assertTrue("first", ftpClient.changeWorkingDirectory("dir1/dir2")); } public void testSite() throws Exception { ftpClientConnect(); // SITE int replyCode = ftpClient.site("parameters,1,2,3"); assertEquals("SITE", 200, replyCode); } public void testSmnt() throws Exception { ftpClientConnect(); // SMNT assertTrue("SMNT", ftpClient.structureMount("dir1/dir2")); verifyReplyCode("structureMount", 250); } public void testRein() throws Exception { ftpClientConnect(); // REIN assertEquals("REIN", 220, ftpClient.rein()); } /** * Test that command names in lowercase or mixed upper/lower case are accepted */ public void testCommandNamesInLowerOrMixedCase() throws Exception { ftpClientConnect(); assertEquals("rein", 220, ftpClient.sendCommand("rein")); assertEquals("rEIn", 220, ftpClient.sendCommand("rEIn")); assertEquals("reiN", 220, ftpClient.sendCommand("reiN")); assertEquals("Rein", 220, ftpClient.sendCommand("Rein")); } public void testUnrecognizedCommand() throws Exception { ftpClientConnect(); assertEquals("Unrecognized:XXXX", 502, ftpClient.sendCommand("XXXX")); } // ------------------------------------------------------------------------- // Test setup and tear-down // ------------------------------------------------------------------------- /** * Perform initialization before each test * * @see org.mockftpserver.test.AbstractTestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); for (int i = 0; i < BINARY_CONTENTS.length; i++) { BINARY_CONTENTS[i] = (byte) i; } stubFtpServer = new StubFtpServer(); stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort()); stubFtpServer.start(); ftpClient = new FTPClient(); retrCommandHandler = (RetrCommandHandler) stubFtpServer.getCommandHandler(CommandNames.RETR); storCommandHandler = (StorCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOR); } /** * Perform cleanup after each test * * @see org.mockftpserver.test.AbstractTestCase#tearDown() */ protected void tearDown() throws Exception { super.tearDown(); stubFtpServer.stop(); } // ------------------------------------------------------------------------- // Internal Helper Methods // ------------------------------------------------------------------------- /** * Connect to the server from the FTPClient */ private void ftpClientConnect() throws IOException { ftpClient.connect(SERVER, PortTestUtil.getFtpServerControlPort()); } /** * Assert that the FtpClient reply code is equal to the expected value * * @param operation - the description of the operation performed; used in the error message * @param expectedReplyCode - the expected FtpClient reply code */ private void verifyReplyCode(String operation, int expectedReplyCode) { int replyCode = ftpClient.getReplyCode(); LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode); assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode); } /** * Verify that the FTPFile has the specified properties * * @param ftpFile - the FTPFile to verify * @param type - the expected file type * @param name - the expected file name * @param size - the expected file size (will be zero for a directory) */ private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) { LOG.info(ftpFile); assertEquals("type: " + ftpFile, type, ftpFile.getType()); assertEquals("name: " + ftpFile, name, ftpFile.getName()); assertEquals("size: " + ftpFile, size, ftpFile.getSize()); } }