blob: 2949a145ef1f72ee3687f4a2d9b749b0aca19279 [file] [log] [blame]
chrismair00dc7bd2014-05-11 21:21:28 +00001/*
2 * Copyright 2007 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.stub;
17
18import org.apache.commons.net.ftp.FTP;
19import org.apache.commons.net.ftp.FTPClient;
20import org.apache.commons.net.ftp.FTPFile;
21import org.slf4j.Logger;
22import org.slf4j.LoggerFactory;
23import org.mockftpserver.core.command.CommandHandler;
24import org.mockftpserver.core.command.CommandNames;
25import org.mockftpserver.core.command.InvocationRecord;
26import org.mockftpserver.core.command.SimpleCompositeCommandHandler;
27import org.mockftpserver.core.command.StaticReplyCommandHandler;
28import org.mockftpserver.stub.command.*;
29import org.mockftpserver.test.*;
30import org.mockftpserver.test.AbstractTestCase;
31
32import java.io.ByteArrayInputStream;
33import java.io.ByteArrayOutputStream;
34import java.io.IOException;
35
36/**
37 * Tests for StubFtpServer using the Apache Jakarta Commons Net FTP client.
38 *
39 * @author Chris Mair
40 * @version $Revision$ - $Date$
41 */
42public final class StubFtpServerIntegrationTest extends AbstractTestCase implements IntegrationTest {
43
44 private static final Logger LOG = LoggerFactory.getLogger(StubFtpServerIntegrationTest.class);
45 private static final String SERVER = "localhost";
46 private static final String USERNAME = "user123";
47 private static final String PASSWORD = "password";
48 private static final String FILENAME = "abc.txt";
49 private static final String ASCII_CONTENTS = "abcdef\tghijklmnopqr";
50 private static final byte[] BINARY_CONTENTS = new byte[256];
51
52 private StubFtpServer stubFtpServer;
53 private FTPClient ftpClient;
54 private RetrCommandHandler retrCommandHandler;
55 private StorCommandHandler storCommandHandler;
56
57 //-------------------------------------------------------------------------
58 // Tests
59 //-------------------------------------------------------------------------
60
61 public void testLogin() throws Exception {
62 // Connect
63 LOG.info("Conecting to " + SERVER);
64 ftpClientConnect();
65 verifyReplyCode("connect", 220);
66
67 // Login
68 String userAndPassword = USERNAME + "/" + PASSWORD;
69 LOG.info("Logging in as " + userAndPassword);
70 boolean success = ftpClient.login(USERNAME, PASSWORD);
71 assertTrue("Unable to login with " + userAndPassword, success);
72 verifyReplyCode("login with " + userAndPassword, 230);
73
74 assertTrue("isStarted", stubFtpServer.isStarted());
75 assertFalse("isShutdown", stubFtpServer.isShutdown());
76
77 // Quit
78 LOG.info("Quit");
79 ftpClient.quit();
80 verifyReplyCode("quit", 221);
81 }
82
83 public void testAcct() throws Exception {
84 ftpClientConnect();
85
86 // ACCT
87 int replyCode = ftpClient.acct("123456");
88 assertEquals("acct", 230, replyCode);
89 }
90
91 /**
92 * Test the stop() method when no session has ever been started
93 */
94 public void testStop_NoSessionEverStarted() throws Exception {
95 LOG.info("Testing a stop() when no session has ever been started");
96 }
97
98 public void testHelp() throws Exception {
99 // Modify HELP CommandHandler to return a predefined help message
100 final String HELP = "help message";
101 HelpCommandHandler helpCommandHandler = (HelpCommandHandler) stubFtpServer.getCommandHandler(CommandNames.HELP);
102 helpCommandHandler.setHelpMessage(HELP);
103
104 ftpClientConnect();
105
106 // HELP
107 String help = ftpClient.listHelp();
108 assertTrue("Wrong response", help.indexOf(HELP) != -1);
109 verifyReplyCode("listHelp", 214);
110 }
111
112 /**
113 * Test the LIST and SYST commands.
114 */
115 public void testList() throws Exception {
116 ftpClientConnect();
117
118 // Set directory listing
119 ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST);
120 listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log\n"
121 + "11-01-01 1:30PM <DIR> archive");
122
123 // LIST
124 FTPFile[] files = ftpClient.listFiles();
125 assertEquals("number of files", 2, files.length);
126 verifyFTPFile(files[0], FTPFile.FILE_TYPE, "File2350.log", 406348L);
127 verifyFTPFile(files[1], FTPFile.DIRECTORY_TYPE, "archive", 0L);
128 verifyReplyCode("list", 226);
129 }
130
131 /**
132 * Test the LIST, PASV and SYST commands, transferring a directory listing in passive mode
133 */
134 public void testList_PassiveMode() throws Exception {
135 ftpClientConnect();
136
137 ftpClient.enterLocalPassiveMode();
138
139 // Set directory listing
140 ListCommandHandler listCommandHandler = (ListCommandHandler) stubFtpServer.getCommandHandler(CommandNames.LIST);
141 listCommandHandler.setDirectoryListing("11-09-01 12:30PM 406348 File2350.log");
142
143 // LIST
144 FTPFile[] files = ftpClient.listFiles();
145 assertEquals("number of files", 1, files.length);
146 verifyReplyCode("list", 226);
147 }
148
149 public void testNlst() throws Exception {
150 ftpClientConnect();
151
152 // Set directory listing
153 NlstCommandHandler nlstCommandHandler = (NlstCommandHandler) stubFtpServer.getCommandHandler(CommandNames.NLST);
154 nlstCommandHandler.setDirectoryListing("File1.txt\nfile2.data");
155
156 // NLST
157 String[] filenames = ftpClient.listNames();
158 assertEquals("number of files", 2, filenames.length);
159 assertEquals(filenames[0], "File1.txt");
160 assertEquals(filenames[1], "file2.data");
161 verifyReplyCode("listNames", 226);
162 }
163
164 /**
165 * Test printing the current working directory (PWD)
166 */
167 public void testPwd() throws Exception {
168 // Modify PWD CommandHandler to return a predefined directory
169 final String DIR = "some/dir";
170 PwdCommandHandler pwdCommandHandler = (PwdCommandHandler) stubFtpServer.getCommandHandler(CommandNames.PWD);
171 pwdCommandHandler.setDirectory(DIR);
172
173 ftpClientConnect();
174
175 // PWD
176 String dir = ftpClient.printWorkingDirectory();
177 assertEquals("Unable to PWD", DIR, dir);
178 verifyReplyCode("printWorkingDirectory", 257);
179 }
180
181 public void testStat() throws Exception {
182 // Modify Stat CommandHandler to return predefined text
183 final String STATUS = "some information 123";
184 StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT);
185 statCommandHandler.setStatus(STATUS);
186
187 ftpClientConnect();
188
189 // STAT
190 String status = ftpClient.getStatus();
191 assertEquals("STAT reply", "211 " + STATUS + ".", status.trim());
192 verifyReplyCode("getStatus", 211);
193 }
194
195 /**
196 * Test getting the status (STAT), when the reply text contains multiple lines
197 */
198 public void testStat_MultilineReplyText() throws Exception {
199 // Modify Stat CommandHandler to return predefined text
200 final String STATUS = "System name: abc.def\nVersion 3.5.7\nNumber of failed logins: 2";
201 final String FORMATTED_REPLY_STATUS = "211-System name: abc.def\r\nVersion 3.5.7\r\n211 Number of failed logins: 2.";
202 StatCommandHandler statCommandHandler = (StatCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STAT);
203 statCommandHandler.setStatus(STATUS);
204
205 ftpClientConnect();
206
207 // STAT
208 String status = ftpClient.getStatus();
209 assertEquals("STAT reply", FORMATTED_REPLY_STATUS, status.trim());
210 verifyReplyCode("getStatus", 211);
211 }
212
213 public void testSyst() throws Exception {
214 ftpClientConnect();
215
216 // SYST
217 assertEquals("getSystemName()", "\"WINDOWS\" system type.", ftpClient.getSystemName());
218 verifyReplyCode("syst", 215);
219 }
220
221 public void testCwd() throws Exception {
222 // Connect
223 LOG.info("Conecting to " + SERVER);
224 ftpClientConnect();
225 verifyReplyCode("connect", 220);
226
227 // CWD
228 boolean success = ftpClient.changeWorkingDirectory("dir1/dir2");
229 assertTrue("Unable to CWD", success);
230 verifyReplyCode("changeWorkingDirectory", 250);
231 }
232
233 /**
234 * Test changing the current working directory (CWD), when it causes a remote error
235 */
236 public void testCwd_Error() throws Exception {
237 // Override CWD CommandHandler to return error reply code
238 final int REPLY_CODE = 500;
239 StaticReplyCommandHandler cwdCommandHandler = new StaticReplyCommandHandler(REPLY_CODE);
240 stubFtpServer.setCommandHandler("CWD", cwdCommandHandler);
241
242 ftpClientConnect();
243
244 // CWD
245 boolean success = ftpClient.changeWorkingDirectory("dir1/dir2");
246 assertFalse("Expected failure", success);
247 verifyReplyCode("changeWorkingDirectory", REPLY_CODE);
248 }
249
250 public void testCdup() throws Exception {
251 ftpClientConnect();
252
253 // CDUP
254 boolean success = ftpClient.changeToParentDirectory();
255 assertTrue("Unable to CDUP", success);
256 verifyReplyCode("changeToParentDirectory", 200);
257 }
258
259 public void testDele() throws Exception {
260 ftpClientConnect();
261
262 // DELE
263 boolean success = ftpClient.deleteFile(FILENAME);
264 assertTrue("Unable to DELE", success);
265 verifyReplyCode("deleteFile", 250);
266 }
267
268 public void testEprt() throws Exception {
269 LOG.info("Skipping...");
270// ftpClientConnect();
271// ftpClient.sendCommand("EPRT", "|2|1080::8:800:200C:417A|5282|");
272// verifyReplyCode("EPRT", 200);
273 }
274
275 public void testEpsv() throws Exception {
276 ftpClientConnect();
277 ftpClient.sendCommand("EPSV");
278 verifyReplyCode("EPSV", 229);
279 }
280
281 public void testFeat_UseStaticReplyCommandHandler() throws IOException {
282 // The FEAT command is not supported out of the box
283 final String FEAT_TEXT = "Extensions supported:\n" +
284 "MLST size*;create;modify*;perm;media-type\n" +
285 "SIZE\n" +
286 "COMPRESSION\n" +
287 "END";
288 StaticReplyCommandHandler featCommandHandler = new StaticReplyCommandHandler(211, FEAT_TEXT);
289 stubFtpServer.setCommandHandler("FEAT", featCommandHandler);
290
291 ftpClientConnect();
292 assertEquals(ftpClient.sendCommand("FEAT"), 211);
293 LOG.info(ftpClient.getReplyString());
294 }
295
296 public void testMkd() throws Exception {
297 ftpClientConnect();
298
299 // MKD
300 boolean success = ftpClient.makeDirectory("dir1/dir2");
301 assertTrue("Unable to CWD", success);
302 verifyReplyCode("makeDirectory", 257);
303 }
304
305 public void testNoop() throws Exception {
306 ftpClientConnect();
307
308 // NOOP
309 boolean success = ftpClient.sendNoOp();
310 assertTrue("Unable to NOOP", success);
311 verifyReplyCode("NOOP", 200);
312 }
313
314 public void testRest() throws Exception {
315 ftpClientConnect();
316
317 // REST
318 int replyCode = ftpClient.rest("marker");
319 assertEquals("Unable to REST", 350, replyCode);
320 }
321
322 public void testRmd() throws Exception {
323 ftpClientConnect();
324
325 // RMD
326 boolean success = ftpClient.removeDirectory("dir1/dir2");
327 assertTrue("Unable to RMD", success);
328 verifyReplyCode("removeDirectory", 250);
329 }
330
331 public void testRename() throws Exception {
332 ftpClientConnect();
333
334 // Rename (RNFR, RNTO)
335 boolean success = ftpClient.rename(FILENAME, "new_" + FILENAME);
336 assertTrue("Unable to RENAME", success);
337 verifyReplyCode("rename", 250);
338 }
339
340 public void testAllo() throws Exception {
341 ftpClientConnect();
342
343 // ALLO
344 assertTrue("ALLO", ftpClient.allocate(1024));
345 assertTrue("ALLO with recordSize", ftpClient.allocate(1024, 64));
346 }
347
348 /**
349 * Test GET and PUT of ASCII files
350 */
351 public void testTransferAsciiFile() throws Exception {
352 retrCommandHandler.setFileContents(ASCII_CONTENTS);
353
354 ftpClientConnect();
355
356 // Get File
357 LOG.info("Get File for remotePath [" + FILENAME + "]");
358 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
359 assertTrue(ftpClient.retrieveFile(FILENAME, outputStream));
360 LOG.info("File contents=[" + outputStream.toString());
361 assertEquals("File contents", ASCII_CONTENTS, outputStream.toString());
362
363 // Put File
364 LOG.info("Put File for local path [" + FILENAME + "]");
365 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
366 assertTrue(ftpClient.storeFile(FILENAME, inputStream));
367 InvocationRecord invocationRecord = storCommandHandler.getInvocation(0);
368 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
369 LOG.info("File contents=[" + contents + "]");
370 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
371 }
372
373 /**
374 * Test GET and PUT of binary files
375 */
376 public void testTransferBinaryFiles() throws Exception {
377 retrCommandHandler.setFileContents(BINARY_CONTENTS);
378
379 ftpClientConnect();
380 ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
381
382 // Get File
383 LOG.info("Get File for remotePath [" + FILENAME + "]");
384 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
385 assertTrue("GET", ftpClient.retrieveFile(FILENAME, outputStream));
386 LOG.info("GET File length=" + outputStream.size());
387 assertEquals("File contents", BINARY_CONTENTS, outputStream.toByteArray());
388
389 // Put File
390 LOG.info("Put File for local path [" + FILENAME + "]");
391 ByteArrayInputStream inputStream = new ByteArrayInputStream(BINARY_CONTENTS);
392 assertTrue("PUT", ftpClient.storeFile(FILENAME, inputStream));
393 InvocationRecord invocationRecord = storCommandHandler.getInvocation(0);
394 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
395 LOG.info("PUT File length=" + contents.length);
396 assertEquals("File contents", BINARY_CONTENTS, contents);
397 }
398
399 public void testStou() throws Exception {
400 StouCommandHandler stouCommandHandler = (StouCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOU);
401 stouCommandHandler.setFilename(FILENAME);
402
403 ftpClientConnect();
404
405 // Stor a File (STOU)
406 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
407 assertTrue(ftpClient.storeUniqueFile(FILENAME, inputStream));
408 InvocationRecord invocationRecord = stouCommandHandler.getInvocation(0);
409 byte[] contents = (byte[]) invocationRecord.getObject(StorCommandHandler.FILE_CONTENTS_KEY);
410 LOG.info("File contents=[" + contents + "]");
411 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
412 }
413
414 public void testAppe() throws Exception {
415 AppeCommandHandler appeCommandHandler = (AppeCommandHandler) stubFtpServer.getCommandHandler(CommandNames.APPE);
416
417 ftpClientConnect();
418
419 // Append a File (APPE)
420 ByteArrayInputStream inputStream = new ByteArrayInputStream(ASCII_CONTENTS.getBytes());
421 assertTrue(ftpClient.appendFile(FILENAME, inputStream));
422 InvocationRecord invocationRecord = appeCommandHandler.getInvocation(0);
423 byte[] contents = (byte[]) invocationRecord.getObject(AppeCommandHandler.FILE_CONTENTS_KEY);
424 LOG.info("File contents=[" + contents + "]");
425 assertEquals("File contents", ASCII_CONTENTS.getBytes(), contents);
426 }
427
428 public void testAbor() throws Exception {
429 ftpClientConnect();
430
431 // ABOR
432 assertTrue("ABOR", ftpClient.abort());
433 }
434
435 public void testPasv() throws Exception {
436 ftpClientConnect();
437
438 // PASV
439 ftpClient.enterLocalPassiveMode();
440 // no reply code; the PASV command is sent only when the data connection is opened
441 }
442
443 public void testMode() throws Exception {
444 ftpClientConnect();
445
446 // MODE
447 boolean success = ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
448 assertTrue("Unable to MODE", success);
449 verifyReplyCode("setFileTransferMode", 200);
450 }
451
452 public void testStru() throws Exception {
453 ftpClientConnect();
454
455 // STRU
456 boolean success = ftpClient.setFileStructure(FTP.FILE_STRUCTURE);
457 assertTrue("Unable to STRU", success);
458 verifyReplyCode("setFileStructure", 200);
459 }
460
461 public void testSimpleCompositeCommandHandler() throws Exception {
462 // Replace CWD CommandHandler with a SimpleCompositeCommandHandler
463 CommandHandler commandHandler1 = new StaticReplyCommandHandler(500);
464 CommandHandler commandHandler2 = new CwdCommandHandler();
465 SimpleCompositeCommandHandler simpleCompositeCommandHandler = new SimpleCompositeCommandHandler();
466 simpleCompositeCommandHandler.addCommandHandler(commandHandler1);
467 simpleCompositeCommandHandler.addCommandHandler(commandHandler2);
468 stubFtpServer.setCommandHandler("CWD", simpleCompositeCommandHandler);
469
470 // Connect
471 ftpClientConnect();
472
473 // CWD
474 assertFalse("first", ftpClient.changeWorkingDirectory("dir1/dir2"));
475 assertTrue("first", ftpClient.changeWorkingDirectory("dir1/dir2"));
476 }
477
478 public void testSite() throws Exception {
479 ftpClientConnect();
480
481 // SITE
482 int replyCode = ftpClient.site("parameters,1,2,3");
483 assertEquals("SITE", 200, replyCode);
484 }
485
486 public void testSmnt() throws Exception {
487 ftpClientConnect();
488
489 // SMNT
490 assertTrue("SMNT", ftpClient.structureMount("dir1/dir2"));
491 verifyReplyCode("structureMount", 250);
492 }
493
494 public void testRein() throws Exception {
495 ftpClientConnect();
496
497 // REIN
498 assertEquals("REIN", 220, ftpClient.rein());
499 }
500
501 /**
502 * Test that command names in lowercase or mixed upper/lower case are accepted
503 */
504 public void testCommandNamesInLowerOrMixedCase() throws Exception {
505 ftpClientConnect();
506
507 assertEquals("rein", 220, ftpClient.sendCommand("rein"));
508 assertEquals("rEIn", 220, ftpClient.sendCommand("rEIn"));
509 assertEquals("reiN", 220, ftpClient.sendCommand("reiN"));
510 assertEquals("Rein", 220, ftpClient.sendCommand("Rein"));
511 }
512
513 public void testUnrecognizedCommand() throws Exception {
514 ftpClientConnect();
515
516 assertEquals("Unrecognized:XXXX", 502, ftpClient.sendCommand("XXXX"));
517 }
518
519 // -------------------------------------------------------------------------
520 // Test setup and tear-down
521 // -------------------------------------------------------------------------
522
523 /**
524 * Perform initialization before each test
525 *
526 * @see org.mockftpserver.test.AbstractTestCase#setUp()
527 */
528 protected void setUp() throws Exception {
529 super.setUp();
530
531 for (int i = 0; i < BINARY_CONTENTS.length; i++) {
532 BINARY_CONTENTS[i] = (byte) i;
533 }
534
535 stubFtpServer = new StubFtpServer();
536 stubFtpServer.setServerControlPort(PortTestUtil.getFtpServerControlPort());
537 stubFtpServer.start();
538 ftpClient = new FTPClient();
539 retrCommandHandler = (RetrCommandHandler) stubFtpServer.getCommandHandler(CommandNames.RETR);
540 storCommandHandler = (StorCommandHandler) stubFtpServer.getCommandHandler(CommandNames.STOR);
541 }
542
543 /**
544 * Perform cleanup after each test
545 *
546 * @see org.mockftpserver.test.AbstractTestCase#tearDown()
547 */
548 protected void tearDown() throws Exception {
549 super.tearDown();
550 stubFtpServer.stop();
551 }
552
553 // -------------------------------------------------------------------------
554 // Internal Helper Methods
555 // -------------------------------------------------------------------------
556
557 /**
558 * Connect to the server from the FTPClient
559 */
560 private void ftpClientConnect() throws IOException {
561 ftpClient.connect(SERVER, PortTestUtil.getFtpServerControlPort());
562 }
563
564 /**
565 * Assert that the FtpClient reply code is equal to the expected value
566 *
567 * @param operation - the description of the operation performed; used in the error message
568 * @param expectedReplyCode - the expected FtpClient reply code
569 */
570 private void verifyReplyCode(String operation, int expectedReplyCode) {
571 int replyCode = ftpClient.getReplyCode();
572 LOG.info("Reply: operation=\"" + operation + "\" replyCode=" + replyCode);
573 assertEquals("Unexpected replyCode for " + operation, expectedReplyCode, replyCode);
574 }
575
576 /**
577 * Verify that the FTPFile has the specified properties
578 *
579 * @param ftpFile - the FTPFile to verify
580 * @param type - the expected file type
581 * @param name - the expected file name
582 * @param size - the expected file size (will be zero for a directory)
583 */
584 private void verifyFTPFile(FTPFile ftpFile, int type, String name, long size) {
585 LOG.info(ftpFile.toString());
586 assertEquals("type: " + ftpFile, type, ftpFile.getType());
587 assertEquals("name: " + ftpFile, name, ftpFile.getName());
588 assertEquals("size: " + ftpFile, size, ftpFile.getSize());
589 }
590
591}