FTP Examples
This section provides complete examples of using MockForge FTP for various testing scenarios.
Basic FTP Server
Starting a Simple Server
# Start FTP server on default port 2121
mockforge ftp serve
# Start on custom port
mockforge ftp serve --port 2122
# Start with custom host
mockforge ftp serve --host 0.0.0.0 --port 2121
Connecting with FTP Clients
Using lftp
# Connect to the server
lftp localhost:2121
# List files
lftp localhost:2121:~> ls
# Download a file
lftp localhost:2121:~> get test.txt
# Upload a file
lftp localhost:2121:~> put localfile.txt
# Exit
lftp localhost:2121:~> quit
Using curl
# List directory
curl ftp://localhost:2121/
# Download file
curl ftp://localhost:2121/test.txt -o downloaded.txt
# Upload file
curl -T localfile.txt ftp://localhost:2121/
Using Python
import ftplib
# Connect to FTP server
ftp = ftplib.FTP('localhost', 'anonymous', '')
# List files
files = ftp.nlst()
print("Files:", files)
# Download file
with open('downloaded.txt', 'wb') as f:
ftp.retrbinary('RETR test.txt', f.write)
# Upload file
with open('localfile.txt', 'rb') as f:
ftp.storbinary('STOR uploaded.txt', f)
ftp.quit()
File Management Examples
Adding Static Files
# Add a simple text file
mockforge ftp vfs add /hello.txt --content "Hello, FTP World!"
# Add a JSON file
mockforge ftp vfs add /config.json --content '{"server": "mockforge", "port": 2121}'
# Add a larger file
echo "This is a test file with multiple lines." > test.txt
mockforge ftp vfs add /multiline.txt --content "$(cat test.txt)"
Adding Template Files
# Add a dynamic JSON response
mockforge ftp vfs add /user.json --template '{"id": "{{uuid}}", "name": "{{faker.name}}", "created": "{{now}}"}'
# Add a log file with timestamps
mockforge ftp vfs add /server.log --template '[{{timestamp}}] Server started at {{time}}'
# Add a status file
mockforge ftp vfs add /status.xml --template '<?xml version="1.0"?><status><server>MockForge</server><time>{{now}}</time></status>'
Adding Generated Files
# Add a random binary file (1KB)
mockforge ftp vfs add /random.bin --generate random --size 1024
# Add a file filled with zeros (512 bytes)
mockforge ftp vfs add /zeros.dat --generate zeros --size 512
# Add an incremental pattern file
mockforge ftp vfs add /pattern.bin --generate incremental --size 256
Managing Files
# List all files
mockforge ftp vfs list /
# Get file information
mockforge ftp vfs info /hello.txt
# Remove a file
mockforge ftp vfs remove /old-file.txt
Configuration Examples
Basic Configuration File
# ftp-config.yaml
ftp:
host: "127.0.0.1"
port: 2121
virtual_root: "/"
fixtures:
- name: "basic_files"
description: "Basic test files"
virtual_files:
- path: "/readme.txt"
content:
type: "static"
content: "Welcome to MockForge FTP Server"
permissions: "644"
owner: "ftp"
group: "ftp"
upload_rules:
- path_pattern: "/uploads/.*"
auto_accept: true
storage:
type: "memory"
Advanced Configuration
# advanced-ftp-config.yaml
ftp:
host: "0.0.0.0"
port: 2121
virtual_root: "/ftp"
fixtures:
- name: "api_test_files"
description: "Files for API testing"
virtual_files:
# Static files
- path: "/api/v1/users"
content:
type: "static"
content: '[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]'
permissions: "644"
owner: "api"
group: "users"
# Template files
- path: "/api/v1/status"
content:
type: "template"
template: '{"status": "ok", "timestamp": "{{now}}", "version": "1.0.0"}'
permissions: "644"
owner: "api"
group: "system"
# Generated test data
- path: "/test/data.bin"
content:
type: "generated"
size: 1048576 # 1MB
pattern: "random"
permissions: "644"
owner: "test"
group: "data"
upload_rules:
# General uploads
- path_pattern: "/uploads/.*"
auto_accept: true
validation:
max_size_bytes: 10485760 # 10MB
storage:
type: "memory"
# Image uploads
- path_pattern: "/images/.*"
auto_accept: true
validation:
max_size_bytes: 5242880 # 5MB
allowed_extensions: ["jpg", "png", "gif"]
storage:
type: "file"
path: "/tmp/ftp/images"
# Log files (accepted but discarded)
- path_pattern: "/logs/.*"
auto_accept: true
storage:
type: "discard"
Testing Scenarios
File Upload Testing
# Start server with upload configuration
mockforge ftp serve --config upload-config.yaml
# Test file upload with curl
echo "Test file content" > test.txt
curl -T test.txt ftp://localhost:2121/uploads/
# Test large file upload
dd if=/dev/zero of=large.bin bs=1M count=5
curl -T large.bin ftp://localhost:2121/uploads/
# Test invalid file type
echo "invalid content" > invalid.exe
curl -T invalid.exe ftp://localhost:2121/uploads/ # Should fail
Load Testing
# Start server
mockforge ftp serve --port 2121 &
# Simple load test with parallel uploads
for i in {1..10}; do
echo "File $i content" > "file$i.txt"
curl -T "file$i.txt" "ftp://localhost:2121/uploads/file$i.txt" &
done
wait
Integration Testing
With pytest
# test_ftp_integration.py
import ftplib
import pytest
import tempfile
import os
class TestFTPIntegration:
@pytest.fixture(scope="class")
def ftp_client(self):
# Connect to MockForge FTP server
ftp = ftplib.FTP('localhost', 'anonymous', '')
yield ftp
ftp.quit()
def test_list_files(self, ftp_client):
files = ftp_client.nlst()
assert len(files) >= 0 # At least empty directory
def test_download_file(self, ftp_client):
# Assuming server has a test file
with tempfile.NamedTemporaryFile(delete=False) as tmp:
try:
ftp_client.retrbinary('RETR test.txt', tmp.write)
assert os.path.getsize(tmp.name) > 0
finally:
os.unlink(tmp.name)
def test_upload_file(self, ftp_client):
# Create test file
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
tmp.write("Test upload content")
tmp_path = tmp.name
try:
# Upload file
with open(tmp_path, 'rb') as f:
ftp_client.storbinary('STOR uploaded.txt', f)
# Verify upload (if server supports listing uploads)
files = ftp_client.nlst()
assert 'uploaded.txt' in [os.path.basename(f) for f in files]
finally:
os.unlink(tmp_path)
With Java
// FtpIntegrationTest.java
import org.apache.commons.net.ftp.FTPClient;
import org.junit.jupiter.api.*;
import java.io.*;
class FtpIntegrationTest {
private FTPClient ftpClient;
@BeforeEach
void setup() throws IOException {
ftpClient = new FTPClient();
ftpClient.connect("localhost", 2121);
ftpClient.login("anonymous", "");
ftpClient.enterLocalPassiveMode();
}
@AfterEach
void teardown() throws IOException {
if (ftpClient.isConnected()) {
ftpClient.disconnect();
}
}
@Test
void testFileDownload() throws IOException {
// Download a file
File tempFile = File.createTempFile("downloaded", ".txt");
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
boolean success = ftpClient.retrieveFile("test.txt", fos);
Assertions.assertTrue(success, "File download should succeed");
Assertions.assertTrue(tempFile.length() > 0, "Downloaded file should not be empty");
} finally {
tempFile.delete();
}
}
@Test
void testFileUpload() throws IOException {
// Create test file
File tempFile = File.createTempFile("upload", ".txt");
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write("Test upload content");
}
// Upload file
try (FileInputStream fis = new FileInputStream(tempFile)) {
boolean success = ftpClient.storeFile("uploaded.txt", fis);
Assertions.assertTrue(success, "File upload should succeed");
} finally {
tempFile.delete();
}
}
@Test
void testDirectoryListing() throws IOException {
FTPFile[] files = ftpClient.listFiles();
Assertions.assertNotNull(files, "Directory listing should not be null");
// Additional assertions based on expected files
}
}
Docker Integration
Running in Docker
# Dockerfile
FROM mockforge:latest
# Copy FTP configuration
COPY ftp-config.yaml /app/config/
# Expose FTP port
EXPOSE 2121
# Start FTP server
CMD ["mockforge", "ftp", "serve", "--config", "/app/config/ftp-config.yaml"]
# Build and run
docker build -t mockforge-ftp .
docker run -p 2121:2121 mockforge-ftp
Docker Compose
# docker-compose.yml
version: '3.8'
services:
ftp-server:
image: mockforge:latest
command: ["mockforge", "ftp", "serve", "--host", "0.0.0.0"]
ports:
- "2121:2121"
volumes:
- ./ftp-config.yaml:/app/config/ftp-config.yaml
- ./uploads:/tmp/uploads
environment:
- RUST_LOG=info
CI/CD Integration
GitHub Actions Example
# .github/workflows/ftp-test.yml
name: FTP Integration Tests
on: [push, pull_request]
jobs:
ftp-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Build MockForge
run: cargo build --release
- name: Start FTP Server
run: |
./target/release/mockforge ftp serve --port 2121 &
sleep 2
- name: Run FTP Tests
run: |
# Test with lftp
sudo apt-get update && sudo apt-get install -y lftp
echo "Test file content" > test.txt
lftp -c "open localhost:2121; put test.txt; ls; get test.txt -o downloaded.txt; quit"
# Verify files
test -f downloaded.txt
grep -q "Test file content" downloaded.txt
Jenkins Pipeline
// Jenkinsfile
pipeline {
agent any
stages {
stage('FTP Integration Test') {
steps {
sh 'cargo build --release'
// Start FTP server in background
sh './target/release/mockforge ftp serve --port 2121 &'
sh 'sleep 3'
// Run tests
sh '''
# Install FTP client
apt-get update && apt-get install -y lftp
# Create test file
echo "Integration test content" > test.txt
# Test FTP operations
lftp -c "
open localhost:2121
put test.txt
ls
get test.txt -o downloaded.txt
quit
"
# Verify
grep -q "Integration test content" downloaded.txt
'''
}
}
}
}
Troubleshooting
Common Issues
Connection Refused
# Check if server is running
netstat -tlnp | grep 2121
# Check server logs
mockforge ftp serve --port 2121 2>&1
Passive Mode Issues
# FTP clients may need passive mode
curl --ftp-pasv ftp://localhost:2121/
File Permission Issues
# Check file permissions in VFS
mockforge ftp vfs info /problematic-file.txt
# Check upload rules
mockforge ftp fixtures validate config.yaml
Memory Issues
# Monitor memory usage
ps aux | grep mockforge
# Use file storage for large files
# Configure storage type in upload rules
This completes the FTP implementation for MockForge. The server provides comprehensive FTP mocking capabilities with virtual file systems, template rendering, and configurable upload handling.