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