FakeFtpServerIntegrationTest.groovy revision 0da0f84ad9831bea470896e79b5ec207c83f258e
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.StaticReplyCommandHandler 22import org.mockftpserver.fake.FakeFtpServer 23import org.mockftpserver.fake.UserAccount 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.AbstractGroovyTest 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 AbstractGroovyTest { 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 void testCwd_UseStaticReplyCommandHandler() { 111 final int REPLY_CODE = 500; 112 StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE); 113 ftpServer.setCommandHandler("CWD", cwdCommandHandler); 114 115 ftpClientConnectAndLogin() 116 assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME) 117 verifyReplyCode("changeWorkingDirectory", REPLY_CODE) 118 } 119 120 void testCwd_UseStubCommandHandler() { 121 final int REPLY_CODE = 502; 122 CwdCommandHandler cwdCommandHandler = new CwdCommandHandler(); // Stub command handler 123 cwdCommandHandler.setReplyCode(REPLY_CODE); 124 ftpServer.setCommandHandler("CWD", cwdCommandHandler); 125 126 ftpClientConnectAndLogin() 127 assert !ftpClient.changeWorkingDirectory(SUBDIR_NAME) 128 verifyReplyCode("changeWorkingDirectory", REPLY_CODE) 129 assert cwdCommandHandler.getInvocation(0) 130 } 131 132 void testDele() { 133 fileSystem.add(new FileEntry(FILE1)) 134 135 ftpClientConnectAndLogin() 136 assert ftpClient.deleteFile(FILENAME1) 137 verifyReplyCode("deleteFile", 250) 138 assert !fileSystem.exists(FILENAME1) 139 } 140 141 void testHelp() { 142 ftpServer.helpText = [a: 'aaa', '': 'default'] 143 ftpClientConnect() 144 145 String help = ftpClient.listHelp() 146 assert help.contains('default') 147 verifyReplyCode("listHelp", 214) 148 149 help = ftpClient.listHelp('a') 150 assert help.contains('aaa') 151 verifyReplyCode("listHelp", 214) 152 153 help = ftpClient.listHelp('bad') 154 assert help.contains('bad') 155 verifyReplyCode("listHelp", 214) 156 } 157 158 void testList() { 159 def LAST_MODIFIED = new Date() 160 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA)) 161 fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2), lastModified: LAST_MODIFIED)) 162 163 ftpClientConnectAndLogin() 164 165 FTPFile[] files = ftpClient.listFiles(SUBDIR) 166 assert files.length == 2 167 verifyFTPFile(files[0], FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size()) 168 verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0) 169 verifyReplyCode("list", 226) 170 } 171 172 void testList_Unix() { 173 ftpServer.systemName = 'UNIX' 174 userAccount.homeDirectory = '/' 175 176 def unixFileSystem = new UnixFakeFileSystem() 177 unixFileSystem.createParentDirectoriesAutomatically = true 178 unixFileSystem.add(new DirectoryEntry('/')) 179 ftpServer.fileSystem = unixFileSystem 180 181 def LAST_MODIFIED = new Date() 182 unixFileSystem.add(new FileEntry(path: p('/', FILENAME1), lastModified: LAST_MODIFIED, contents: ASCII_DATA)) 183 unixFileSystem.add(new DirectoryEntry(path: p('/', SUBDIR_NAME2), lastModified: LAST_MODIFIED)) 184 185 ftpClientConnectAndLogin() 186 187 FTPFile[] files = ftpClient.listFiles('/') 188 assert files.length == 2 189 verifyFTPFile(files[0], FTPFile.DIRECTORY_TYPE, SUBDIR_NAME2, 0) 190 verifyFTPFile(files[1], FTPFile.FILE_TYPE, FILENAME1, ASCII_DATA.size()) 191 verifyReplyCode("list", 226) 192 } 193 194 void testLogin() { 195 ftpClientConnect() 196 LOG.info("Logging in as $USERNAME/$PASSWORD") 197 assert ftpClient.login(USERNAME, PASSWORD) 198 verifyReplyCode("login with $USERNAME/$PASSWORD", 230) 199 } 200 201 void testLogin_WithAccount() { 202 userAccount.accountRequiredForLogin = true 203 ftpClientConnect() 204 LOG.info("Logging in as $USERNAME/$PASSWORD with $ACCOUNT") 205 assert ftpClient.login(USERNAME, PASSWORD, ACCOUNT) 206 verifyReplyCode("login with $USERNAME/$PASSWORD with $ACCOUNT", 230) 207 } 208 209 void testMkd() { 210 ftpClientConnectAndLogin() 211 212 def DIR = p(HOME_DIR, 'NewDir') 213 assert ftpClient.makeDirectory(DIR) 214 verifyReplyCode("makeDirectory", 257) 215 assert fileSystem.isDirectory(DIR) 216 } 217 218 void testMode() { 219 ftpClientConnectAndLogin() 220 assert ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE); 221 verifyReplyCode("MODE", 200) 222 } 223 224 void testNlst() { 225 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1))) 226 fileSystem.add(new DirectoryEntry(path: p(SUBDIR, SUBDIR_NAME2))) 227 228 ftpClientConnectAndLogin() 229 230 String[] filenames = ftpClient.listNames(SUBDIR) 231 assert filenames == [FILENAME1, SUBDIR_NAME2] 232 verifyReplyCode("listNames", 226) 233 } 234 235 void testNoop() { 236 ftpClientConnectAndLogin() 237 assert ftpClient.sendNoOp() 238 verifyReplyCode("NOOP", 200) 239 } 240 241 void testPasv_Nlst() { 242 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME1))) 243 fileSystem.add(new FileEntry(path: p(SUBDIR, FILENAME2))) 244 245 ftpClientConnectAndLogin() 246 ftpClient.enterLocalPassiveMode(); 247 248 String[] filenames = ftpClient.listNames(SUBDIR) 249 assert filenames == [FILENAME1, FILENAME2] 250 verifyReplyCode("listNames", 226) 251 } 252 253 void testPwd() { 254 ftpClientConnectAndLogin() 255 assert ftpClient.printWorkingDirectory() == HOME_DIR 256 verifyReplyCode("printWorkingDirectory", 257) 257 } 258 259 void testQuit() { 260 ftpClientConnect() 261 ftpClient.quit() 262 verifyReplyCode("quit", 221) 263 } 264 265 void testRein() { 266 ftpClientConnectAndLogin() 267 assert ftpClient.rein() == 220 268 assert ftpClient.cdup() == 530 // now logged out 269 } 270 271 void testRest() { 272 ftpClientConnectAndLogin() 273 assert ftpClient.rest("marker") == 350 274 } 275 276 void testRetr() { 277 fileSystem.add(new FileEntry(path: FILE1, contents: ASCII_DATA)) 278 279 ftpClientConnectAndLogin() 280 281 LOG.info("Get File for remotePath [$FILE1]") 282 def outputStream = new ByteArrayOutputStream() 283 assert ftpClient.retrieveFile(FILE1, outputStream) 284 LOG.info("File contents=[${outputStream.toString()}]") 285 assert outputStream.toString() == ASCII_DATA 286 } 287 288 void testRmd() { 289 ftpClientConnectAndLogin() 290 291 assert ftpClient.removeDirectory(SUBDIR) 292 verifyReplyCode("removeDirectory", 250) 293 assert !fileSystem.exists(SUBDIR) 294 } 295 296 void testRename() { // RNFR and RNTO 297 fileSystem.add(new FileEntry(FILE1)) 298 299 ftpClientConnectAndLogin() 300 301 assert ftpClient.rename(FILE1, FILE1 + "NEW") 302 verifyReplyCode("rename", 250) 303 assert !fileSystem.exists(FILE1) 304 assert fileSystem.exists(FILE1 + "NEW") 305 } 306 307 void testSite() { 308 ftpClientConnectAndLogin() 309 assert ftpClient.site("parameters,1,2,3") == 200 310 } 311 312 void testStor() { 313 ftpClientConnectAndLogin() 314 315 LOG.info("Put File for local path [$FILE1]") 316 def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes()) 317 assert ftpClient.storeFile(FILE1, inputStream) 318 def contents = fileSystem.getEntry(FILE1).createInputStream().text 319 LOG.info("File contents=[" + contents + "]") 320 assert contents == ASCII_DATA 321 } 322 323 void testStou() { 324 ftpClientConnectAndLogin() 325 326 def inputStream = new ByteArrayInputStream(ASCII_DATA.getBytes()) 327 assert ftpClient.storeUniqueFile(FILENAME1, inputStream) 328 329 def names = fileSystem.listNames(HOME_DIR) 330 def filename = names.find {name -> name.startsWith(FILENAME1) } 331 assert filename 332 333 def contents = fileSystem.getEntry(p(HOME_DIR, filename)).createInputStream().text 334 LOG.info("File contents=[" + contents + "]") 335 assert contents == ASCII_DATA 336 } 337 338 void testStru() { 339 ftpClientConnectAndLogin() 340 assert ftpClient.setFileStructure(FTP.FILE_STRUCTURE); 341 verifyReplyCode("STRU", 200) 342 } 343 344 void testSyst() { 345 ftpClientConnectAndLogin() 346 347 def systemName = ftpClient.getSystemName() 348 LOG.info("system name = [$systemName]") 349 assert systemName.contains('"' + SYSTEM_NAME + '"') 350 verifyReplyCode("getSystemName", 215) 351 } 352 353 void testType() { 354 ftpClientConnectAndLogin() 355 assert ftpClient.type(FTP.ASCII_FILE_TYPE) 356 verifyReplyCode("TYPE", 200) 357 } 358 359 // ------------------------------------------------------------------------- 360 // Test setup and tear-down 361 // ------------------------------------------------------------------------- 362 363 /** 364 * Perform initialization before each test 365 * @see org.mockftpserver.test.AbstractTest#setUp() 366 */ 367 void setUp() { 368 super.setUp() 369 370 for (int i = 0; i < BINARY_DATA.length; i++) { 371 BINARY_DATA[i] = (byte) i 372 } 373 374 ftpServer = new FakeFtpServer() 375 ftpServer.serverControlPort = PortTestUtil.getFtpServerControlPort() 376 ftpServer.systemName = SYSTEM_NAME 377 378 fileSystem = new WindowsFakeFileSystem() 379 fileSystem.createParentDirectoriesAutomatically = true 380 fileSystem.add(new DirectoryEntry(SUBDIR)) 381 ftpServer.fileSystem = fileSystem 382 383 userAccount = new UserAccount(USERNAME, PASSWORD, HOME_DIR) 384 ftpServer.userAccounts[USERNAME] = userAccount 385 386 ftpServer.start() 387 ftpClient = new FTPClient() 388 } 389 390 /** 391 * Perform cleanup after each test 392 * @see org.mockftpserver.test.AbstractTest#tearDown() 393 */ 394 void tearDown() { 395 super.tearDown() 396 ftpServer.stop() 397 } 398 399 // ------------------------------------------------------------------------- 400 // Internal Helper Methods 401 // ------------------------------------------------------------------------- 402 403 private ftpClientConnectAndLogin() { 404 ftpClientConnect() 405 assert ftpClient.login(USERNAME, PASSWORD) 406 } 407 408 /** 409 * Connect to the server from the FTPClient 410 */ 411 private void ftpClientConnect() { 412 def port = PortTestUtil.getFtpServerControlPort() 413 LOG.info("Conecting to $SERVER on port $port") 414 ftpClient.connect(SERVER, port) 415 verifyReplyCode("connect", 220) 416 } 417 418 /** 419 * Assert that the FtpClient reply code is equal to the expected value 420 * 421 * @param operation - the description of the operation performed used in the error message 422 * @param expectedReplyCode - the expected FtpClient reply code 423 */ 424 private void verifyReplyCode(String operation, int expectedReplyCode) { 425 int replyCode = ftpClient.getReplyCode() 426 LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode) 427 assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode) 428 } 429 430 private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) { 431 LOG.info(ftpFile) 432 assertEquals("type: " + ftpFile, type, ftpFile.getType()) 433 assertEquals("name: " + ftpFile, name, ftpFile.getName()) 434 assertEquals("size: " + ftpFile, size, ftpFile.getSize()) 435 } 436 437}