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