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