1/* 2 * Copyright 2008 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.fake 17 18import org.apache.commons.net.ftp.FTP 19import org.apache.commons.net.ftp.FTPClient 20import org.apache.commons.net.ftp.FTPFile 21import org.mockftpserver.core.command.CommandNames 22import org.mockftpserver.core.command.StaticReplyCommandHandler 23import org.mockftpserver.fake.FakeFtpServer 24import org.mockftpserver.fake.UserAccount 25import org.mockftpserver.fake.filesystem.DirectoryEntry 26import org.mockftpserver.fake.filesystem.FileEntry 27import org.mockftpserver.fake.filesystem.FileSystem 28import org.mockftpserver.fake.filesystem.UnixFakeFileSystem 29import org.mockftpserver.fake.filesystem.WindowsFakeFileSystem 30import org.mockftpserver.stub.command.CwdCommandHandler 31import org.mockftpserver.test.AbstractGroovyTest 32import org.mockftpserver.test.PortTestUtil 33 34/** 35 * Integration tests for FakeFtpServer. 36 * 37 * @version $Revision$ - $Date$ 38 * 39 * @author Chris Mair 40 */ 41class FakeFtpServerIntegrationTest extends AbstractGroovyTest { 42 43 static final SERVER = "localhost" 44 static final USERNAME = "user123" 45 static final PASSWORD = "password" 46 static final ACCOUNT = "account123" 47 static final ASCII_DATA = "abcdef\tghijklmnopqr" 48 static final BINARY_DATA = new byte[256] 49 static final ROOT_DIR = "c:/" 50 static final HOME_DIR = p(ROOT_DIR, "home") 51 static final SUBDIR_NAME = 'sub' 52 static final SUBDIR_NAME2 = "archive" 53 static final SUBDIR = p(HOME_DIR, SUBDIR_NAME) 54 static final FILENAME1 = "abc.txt" 55 static final FILENAME2 = "SomeOtherFile.xml" 56 static final FILE1 = p(HOME_DIR, FILENAME1) 57 static final SYSTEM_NAME = "WINDOWS" 58 59 private FakeFtpServer ftpServer 60 private FTPClient ftpClient 61 private FileSystem fileSystem 62 private UserAccount userAccount 63 64 //------------------------------------------------------------------------- 65 // Tests 66 //------------------------------------------------------------------------- 67 68 void testAbor() { 69 ftpClientConnectAndLogin() 70 assert ftpClient.abort() 71 verifyReplyCode("ABOR", 226) 72 } 73 74 void testAcct() { 75 ftpClientConnectAndLogin() 76 assert ftpClient.acct(ACCOUNT) == 230 77 } 78 79 void testAllo() { 80 ftpClientConnectAndLogin() 81 assert ftpClient.allocate(99) 82 verifyReplyCode("ALLO", 200) 83 } 84 85 void testAppe() { 86 def ORIGINAL_CONTENTS = '123 456 789' 87 fileSystem.add(new FileEntry(path: FILE1, contents: ORIGINAL_CONTENTS)) 88 89 ftpClientConnectAndLogin() 90 91 LOG.info("Put File for local path [$FILE1]") 92 def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes()) 93 assert ftpClient.appendFile(FILE1, inputStream) 94 def contents = fileSystem.getEntry(FILE1).createInputStream().text 95 LOG.info("File contents=[" + contents + "]") 96 assert contents == ORIGINAL_CONTENTS + ASCII_DATA 97 } 98 99 void testCdup() { 100 ftpClientConnectAndLogin() 101 assert ftpClient.changeToParentDirectory() 102 verifyReplyCode("changeToParentDirectory", 200) 103 } 104 105 void testCwd() { 106 ftpClientConnectAndLogin() 107 assert ftpClient.changeWorkingDirectory(SUBDIR_NAME) 108 verifyReplyCode("changeWorkingDirectory", 250) 109 } 110 111 void testCwd_UseStaticReplyCommandHandler() { 112 final int REPLY_CODE = 500; 113 StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE); 114 ftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler); 115 116 ftpClientConnectAndLogin() 117 assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME) 118 verifyReplyCode("changeWorkingDirectory", REPLY_CODE) 119 } 120 121 void testCwd_UseStubCommandHandler() { 122 final int REPLY_CODE = 502; 123 CwdCommandHandler cwdCommandHandler = new CwdCommandHandler(); // Stub command handler 124 cwdCommandHandler.setReplyCode(REPLY_CODE); 125 ftpServer.setCommandHandler(CommandNames.CWD, cwdCommandHandler); 126 127 ftpClientConnectAndLogin() 128 assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME) 129 verifyReplyCode("changeWorkingDirectory", REPLY_CODE) 130 assert cwdCommandHandler.getInvocation(0) 131 } 132 133 void testDele() { 134 fileSystem.add(new FileEntry(FILE1)) 135 136 ftpClientConnectAndLogin() 137 assert ftpClient.deleteFile(FILENAME1) 138 verifyReplyCode("deleteFile", 250) 139 assert !fileSystem.exists(FILENAME1) 140 } 141 142 void testFeat_UseStaticReplyCommandHandler() { 143 // The FEAT command is not supported out of the box 144 StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, "No Features"); 145 ftpServer.setCommandHandler("FEAT", featCommandHandler); 146 147 ftpClientConnectAndLogin() 148 assert ftpClient.sendCommand("FEAT") == 211 149 } 150 151 void testHelp() { 152 ftpServer.helpText = [a: 'aaa', '': 'default'] 153 ftpClientConnect() 154 155 String help = ftpClient.listHelp() 156 assert help.contains('default') 157 verifyReplyCode("listHelp", 214) 158 159 help = ftpClient.listHelp('a') 160 assert help.contains('aaa') 161 verifyReplyCode("listHelp", 214) 162 163 help = ftpClient.listHelp('bad') 164 assert help.contains('bad') 165 verifyReplyCode("listHelp", 214) 166 } 167 168 void testList() { 169 def LAST_MODIFIED = new Date() 170 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA)) 171 fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2), lastModified: LAST_MODIFIED)) 172 173 ftpClientConnectAndLogin() 174 175 FTPFile[] files = ftpClient.listFiles(SUBDIR) 176 assert files.length == 2 177 verifyFTPFile(files[0], FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size()) 178 verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0) 179 verifyReplyCode("list", 226) 180 } 181 182 void testList_Unix() { 183 ftpServer.systemName = 'UNIX' 184 userAccount.homeDirectory = '/' 185 186 def unixFileSystem = new UnixFakeFileSystem() 187 unixFileSystem.createParentDirectoriesAutomatically = true 188 unixFileSystem.add(new DirectoryEntry('/')) 189 ftpServer.fileSystem = unixFileSystem 190 191 def LAST_MODIFIED = new Date() 192 unixFileSystem.add(new FileEntry(path: p('/', FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA)) 193 unixFileSystem.add(new DirectoryEntry(path: p('/', SUBDIR_NAME2), lastModified: LAST_MODIFIED)) 194 195 ftpClientConnectAndLogin() 196 197 FTPFile[] files = ftpClient.listFiles('/') 198 assert files.length == 2 199 verifyFTPFile(files[0], FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0) 200 verifyFTPFile(files[1], FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size()) 201 verifyReplyCode("list", 226) 202 } 203 204 void testLogin() { 205 ftpClientConnect() 206 LOG.info("Logging in as $USERNAME/$PASSWORD") 207 assert ftpClient.login(USERNAME, PASSWORD) 208 verifyReplyCode("login with $USERNAME/$PASSWORD", 230) 209 210 assertTrue("isStarted", ftpServer.isStarted()); 211 assertFalse("isShutdown", ftpServer.isShutdown()); 212 } 213 214 void testLogin_WithAccount() { 215 userAccount.accountRequiredForLogin = true 216 ftpClientConnect() 217 LOG.info("Logging in as $USERNAME/$PASSWORD with $ACCOUNT") 218 assert ftpClient.login(USERNAME, PASSWORD, ACCOUNT) 219 verifyReplyCode("login with $USERNAME/$PASSWORD with $ACCOUNT", 230) 220 } 221 222 void testMkd() { 223 ftpClientConnectAndLogin() 224 225 def DIR = p(HOME_DIR, 'NewDir') 226 assert ftpClient.makeDirectory(DIR) 227 verifyReplyCode("makeDirectory", 257) 228 assert fileSystem.isDirectory(DIR) 229 } 230 231 void testMode() { 232 ftpClientConnectAndLogin() 233 assert ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); 234 verifyReplyCode("MODE", 200) 235 } 236 237 void testNlst() { 238 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1))) 239 fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2))) 240 241 ftpClientConnectAndLogin() 242 243 String[] filenames = ftpClient.listNames(SUBDIR) 244 assert filenames == [FILENAME1, SUBDIR_NAME2] 245 verifyReplyCode("listNames", 226) 246 } 247 248 void testNoop() { 249 ftpClientConnectAndLogin() 250 assert ftpClient.sendNoOp() 251 verifyReplyCode("NOOP", 200) 252 } 253 254 void testPasv_Nlst() { 255 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1))) 256 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME2))) 257 258 ftpClientConnectAndLogin() 259 ftpClient.enterLocalPassiveMode(); 260 261 String[] filenames = ftpClient.listNames(SUBDIR) 262 assert filenames == [FILENAME1, FILENAME2] 263 verifyReplyCode("listNames", 226) 264 } 265 266 void testPwd() { 267 ftpClientConnectAndLogin() 268 assert ftpClient.printWorkingDirectory() == HOME_DIR 269 verifyReplyCode("printWorkingDirectory", 257) 270 } 271 272 void testQuit() { 273 ftpClientConnect() 274 ftpClient.quit() 275 verifyReplyCode("quit", 221) 276 } 277 278 void testRein() { 279 ftpClientConnectAndLogin() 280 assert ftpClient.rein() == 220 281 assert ftpClient.cdup() == 530 // now logged out 282 } 283 284 void testRest() { 285 ftpClientConnectAndLogin() 286 assert ftpClient.rest("marker") == 350 287 } 288 289 void testRetr() { 290 fileSystem.add(new FileEntry(path: FILE1, contents: ASCII_DATA)) 291 292 ftpClientConnectAndLogin() 293 294 LOG.info("Get File for remotePath [$FILE1]") 295 def outputStream = new ByteArrayOutputStream() 296 assert ftpClient.retrieveFile(FILE1, outputStream) 297 LOG.info("File contents=[${outputStream.toString()}]") 298 assert outputStream.toString() == ASCII_DATA 299 } 300 301 void testRmd() { 302 ftpClientConnectAndLogin() 303 304 assert ftpClient.removeDirectory(SUBDIR) 305 verifyReplyCode("removeDirectory", 250) 306 assert !fileSystem.exists(SUBDIR) 307 } 308 309 void testRename() { // RNFR and RNTO 310 fileSystem.add(new FileEntry(FILE1)) 311 312 ftpClientConnectAndLogin() 313 314 assert ftpClient.rename(FILE1, FILE1 + "NEW") 315 verifyReplyCode("rename", 250) 316 assert !fileSystem.exists(FILE1) 317 assert fileSystem.exists(FILE1 + "NEW") 318 } 319 320 void testSite() { 321 ftpClientConnectAndLogin() 322 assert ftpClient.site("parameters,1,2,3") == 200 323 } 324 325 void testSmnt() { 326 ftpClientConnectAndLogin() 327 assert ftpClient.smnt("dir") == 250 328 } 329 330 void testStat() { 331 ftpClientConnectAndLogin() 332 def status = ftpClient.getStatus() 333 assert status.contains('Connected') 334 verifyReplyCode("stat", 211) 335 } 336 337 void testStor() { 338 ftpClientConnectAndLogin() 339 340 LOG.info("Put File for local path [$FILE1]") 341 def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes()) 342 assert ftpClient.storeFile(FILE1, inputStream) 343 def contents = fileSystem.getEntry(FILE1).createInputStream().text 344 LOG.info("File contents=[" + contents + "]") 345 assert contents == ASCII_DATA 346 } 347 348 void testStou() { 349 ftpClientConnectAndLogin() 350 351 def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes()) 352 assert ftpClient.storeUniqueFile(FILENAME1, inputStream) 353 354 def names = fileSystem.listNames(HOME_DIR) 355 def filename = names.find {name -> name.startsWith(FILENAME1) } 356 assert filename 357 358 def contents = fileSystem.getEntry(p(HOME_DIR, filename)).createInputStream().text 359 LOG.info("File contents=[" + contents + "]") 360 assert contents == ASCII_DATA 361 } 362 363 void testStru() { 364 ftpClientConnectAndLogin() 365 assert ftpClient.setFileStructure(FTP.FILE_STRUCTURE); 366 verifyReplyCode("STRU", 200) 367 } 368 369 void testSyst() { 370 ftpClientConnectAndLogin() 371 372 def systemName = ftpClient.getSystemName() 373 LOG.info("system name = [$systemName]") 374 assert systemName.contains('"' + SYSTEM_NAME + '"') 375 verifyReplyCode("getSystemName", 215) 376 } 377 378 void testType() { 379 ftpClientConnectAndLogin() 380 assert ftpClient.type(FTP.ASCII_FILE_TYPE) 381 verifyReplyCode("TYPE", 200) 382 } 383 384 void testUnrecognizedCommand() { 385 ftpClientConnectAndLogin() 386 assert ftpClient.sendCommand("XXX") == 502 387 verifyReplyCode("XXX", 502) 388 } 389 390 // ------------------------------------------------------------------------- 391 // Test setup and tear-down 392 // ------------------------------------------------------------------------- 393 394 /** 395 * Perform initialization before each test 396 * @see org.mockftpserver.test.AbstractTest#setUp() 397 */ 398 void setUp() { 399 super.setUp() 400 401 for (int i = 0; i < BINARY_DATA.length; i++) { 402 BINARY_DATA[i] = (byte) i 403 } 404 405 ftpServer = new FakeFtpServer() 406 ftpServer.serverControlPort = PortTestUtil.getFtpServerControlPort() 407 ftpServer.systemName = SYSTEM_NAME 408 409 fileSystem = new WindowsFakeFileSystem() 410 fileSystem.createParentDirectoriesAutomatically = true 411 fileSystem.add(new DirectoryEntry(SUBDIR)) 412 ftpServer.fileSystem = fileSystem 413 414 userAccount = new UserAccount(USERNAME, PASSWORD, HOME_DIR) 415 ftpServer.addUserAccount(userAccount) 416 417 ftpServer.start() 418 ftpClient = new FTPClient() 419 } 420 421 /** 422 * Perform cleanup after each test 423 * @see org.mockftpserver.test.AbstractTest#tearDown() 424 */ 425 void tearDown() { 426 super.tearDown() 427 ftpServer.stop() 428 } 429 430 // ------------------------------------------------------------------------- 431 // Internal Helper Methods 432 // ------------------------------------------------------------------------- 433 434 private ftpClientConnectAndLogin() { 435 ftpClientConnect() 436 assert ftpClient.login(USERNAME, PASSWORD) 437 } 438 439 /** 440 * Connect to the server from the FTPClient 441 */ 442 private void ftpClientConnect() { 443 def port = PortTestUtil.getFtpServerControlPort() 444 LOG.info("Conecting to $SERVER on port $port") 445 ftpClient.connect(SERVER, port) 446 verifyReplyCode("connect", 220) 447 } 448 449 /** 450 * Assert that the FtpClient reply code is equal to the expected value 451 * 452 * @param operation - the description of the operation performed used in the error message 453 * @param expectedReplyCode - the expected FtpClient reply code 454 */ 455 private void verifyReplyCode(String operation, int expectedReplyCode) { 456 int replyCode = ftpClient.getReplyCode() 457 LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode) 458 assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode) 459 } 460 461 private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) { 462 LOG.info(ftpFile) 463 assertEquals("type: " + ftpFile, type, ftpFile.getType()) 464 assertEquals("name: " + ftpFile, name, ftpFile.getName()) 465 assertEquals("size: " + ftpFile, size, ftpFile.getSize()) 466 } 467 468}