blob: 7c5349fe64b79280e72ae6c14bba71e8b163925f [file] [log] [blame]
chrismair00dc7bd2014-05-11 21:21:28 +00001/*
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}