fake_ftp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/README.markdown +81 -0
- data/Rakefile +10 -0
- data/fake_ftp.gemspec +26 -0
- data/lib/fake_ftp/server.rb +155 -0
- data/lib/fake_ftp/version.rb +3 -0
- data/lib/fake_ftp.rb +4 -0
- data/spec/fixtures/invisible_bike.jpg +0 -0
- data/spec/fixtures/text_file.txt +1 -0
- data/spec/models/fake_ftp/server_spec.rb +353 -0
- data/spec/spec_helper.rb +13 -0
- metadata +132 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ree@fake_ftp --create
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fake_ftp (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.2)
|
10
|
+
rake (0.8.7)
|
11
|
+
rspec (2.5.0)
|
12
|
+
rspec-core (~> 2.5.0)
|
13
|
+
rspec-expectations (~> 2.5.0)
|
14
|
+
rspec-mocks (~> 2.5.0)
|
15
|
+
rspec-core (2.5.1)
|
16
|
+
rspec-expectations (2.5.0)
|
17
|
+
diff-lcs (~> 1.1.2)
|
18
|
+
rspec-mocks (2.5.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
bundler (>= 1.0.10)
|
25
|
+
fake_ftp!
|
26
|
+
rake (>= 0.8.7)
|
27
|
+
rspec (> 2)
|
data/README.markdown
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
FakeFtp
|
2
|
+
=======
|
3
|
+
|
4
|
+
This is a gem that allows you to test FTP implementations in ruby. It is a minimal single-client FTP server
|
5
|
+
that can be bound to any arbitrary port on localhost.
|
6
|
+
|
7
|
+
Why?
|
8
|
+
----
|
9
|
+
|
10
|
+
We want to ensure that our code works, in a way that is agnostic to the implementation used (unlike with stubs or mocks).
|
11
|
+
|
12
|
+
How
|
13
|
+
---
|
14
|
+
|
15
|
+
FakeFtp is a simple FTP server that fakes out enough of the protocol to get us by, allowing us to test that files get to
|
16
|
+
their intended destination rather than testing how our code does so.
|
17
|
+
|
18
|
+
Note: Only passive FTP is currently implemented
|
19
|
+
|
20
|
+
Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
require 'fake_ftp'
|
24
|
+
require 'net/ftp'
|
25
|
+
|
26
|
+
server = FakeFtp::Server.new(21212, 21213)
|
27
|
+
## 21212 is the control port, which is used by FTP for the primary connection
|
28
|
+
## 21213 is the data port, used in FTP passive mode to send file contents
|
29
|
+
server.start
|
30
|
+
|
31
|
+
ftp = Net::FTP.new
|
32
|
+
ftp.connect('127.0.0.1', 21212)
|
33
|
+
ftp.login('user', 'password')
|
34
|
+
ftp.passive = true
|
35
|
+
ftp.put('some_file.txt')
|
36
|
+
ftp.close
|
37
|
+
|
38
|
+
server.stop
|
39
|
+
|
40
|
+
TODO
|
41
|
+
----
|
42
|
+
|
43
|
+
* Active file upload
|
44
|
+
* Matchers
|
45
|
+
|
46
|
+
References
|
47
|
+
----------
|
48
|
+
|
49
|
+
* http://rubyforge.org/projects/ftpd/ - a simple ftp daemon written by Chris Wanstrath
|
50
|
+
* http://ruby-doc.org/stdlib/libdoc/gserver/rdoc/index.html - a generic server in the Ruby standard library, by John W Small
|
51
|
+
|
52
|
+
Contributors
|
53
|
+
------------
|
54
|
+
|
55
|
+
* Eric Saxby
|
56
|
+
* Colin Shield
|
57
|
+
|
58
|
+
License
|
59
|
+
-------
|
60
|
+
|
61
|
+
The MIT License
|
62
|
+
|
63
|
+
Copyright (c) 2011 Eric Saxby
|
64
|
+
|
65
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
66
|
+
of this software and associated documentation files (the "Software"), to deal
|
67
|
+
in the Software without restriction, including without limitation the rights
|
68
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
69
|
+
copies of the Software, and to permit persons to whom the Software is
|
70
|
+
furnished to do so, subject to the following conditions:
|
71
|
+
|
72
|
+
The above copyright notice and this permission notice shall be included in
|
73
|
+
all copies or substantial portions of the Software.
|
74
|
+
|
75
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
76
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
77
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
78
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
79
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
80
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
81
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
data/fake_ftp.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fake_ftp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fake_ftp"
|
7
|
+
s.version = FakeFtp::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Colin Shield", "Eric Saxby"]
|
10
|
+
s.email = ["sax+github@livinginthepast.org"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/fake_ftp"
|
12
|
+
s.summary = %q{Creates a fake FTP server for use in testing}
|
13
|
+
s.description = %q{Testing FTP? Use this!}
|
14
|
+
|
15
|
+
s.required_rubygems_version = ">= 1.3.6"
|
16
|
+
s.rubyforge_project = "fake_ftp"
|
17
|
+
|
18
|
+
s.add_development_dependency "bundler", '>=1.0.10'
|
19
|
+
s.add_development_dependency "rspec", '>2'
|
20
|
+
s.add_development_dependency "rake", '>=0.8.7'
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module FakeFtp
|
5
|
+
class Server
|
6
|
+
|
7
|
+
attr_accessor :directory, :port, :passive_port
|
8
|
+
attr_reader :files
|
9
|
+
|
10
|
+
CMDS = %w[acct cwd cdup pass pasv port pwd quit stor type user]
|
11
|
+
LNBK = "\r\n"
|
12
|
+
|
13
|
+
def initialize(control_port = 21, data_port = nil, options = {})
|
14
|
+
self.port = control_port
|
15
|
+
self.passive_port = data_port
|
16
|
+
raise(Errno::EADDRINUSE, "#{port}") if is_running?
|
17
|
+
raise(Errno::EADDRINUSE, "#{passive_port}") if passive_port && is_running?(passive_port)
|
18
|
+
@connection = nil
|
19
|
+
@data = nil
|
20
|
+
@options = options
|
21
|
+
@files = []
|
22
|
+
self.directory = "#{Rails.root}/tmp/ftp" rescue '/tmp'
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
@started = true
|
27
|
+
@server = ::TCPServer.new('127.0.0.1', port)
|
28
|
+
@thread = Thread.new do
|
29
|
+
while @started
|
30
|
+
@client = @server.accept
|
31
|
+
respond_with('200 Can has FTP?')
|
32
|
+
@connection = Thread.new(@client) do |socket|
|
33
|
+
while @started && !socket.nil? && !socket.closed?
|
34
|
+
respond_with parse(socket.gets)
|
35
|
+
end
|
36
|
+
@client.close
|
37
|
+
@client = nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@server.close
|
41
|
+
@server = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
if passive_port
|
45
|
+
@data_server = ::TCPserver.new('127.0.0.1', passive_port)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
@started = false
|
51
|
+
@client.close if @client
|
52
|
+
@server.close if @server
|
53
|
+
@server = nil
|
54
|
+
@data_server.close if @data_server
|
55
|
+
@data_server = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def is_running?(tcp_port = nil)
|
59
|
+
service = `lsof -w -n -i tcp:#{tcp_port || port}`
|
60
|
+
!service.nil? && service != ''
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def respond_with(stuff)
|
66
|
+
@client.print stuff << LNBK unless stuff.nil? or @client.nil? or @client.closed?
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse(request)
|
70
|
+
return if request.nil?
|
71
|
+
puts request if @options[:debug]
|
72
|
+
command = request[0, 4].downcase.strip
|
73
|
+
contents = request.split
|
74
|
+
message = contents[1..contents.length]
|
75
|
+
case command
|
76
|
+
when *CMDS
|
77
|
+
__send__ "_#{command}", message
|
78
|
+
else
|
79
|
+
'500 Unknown command'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def _acct(*args)
|
84
|
+
'230 WHATEVER!'
|
85
|
+
end
|
86
|
+
|
87
|
+
def _cwd(*args)
|
88
|
+
'250 OK!'
|
89
|
+
end
|
90
|
+
alias :_cdup :_cwd
|
91
|
+
|
92
|
+
def _pass(*args)
|
93
|
+
'230 logged in'
|
94
|
+
end
|
95
|
+
|
96
|
+
def _pasv(*args)
|
97
|
+
if passive_port
|
98
|
+
p1 = (passive_port / 256).to_i
|
99
|
+
p2 = passive_port % 256
|
100
|
+
"227 Entering Passive Mode (127,0,0,1,#{p1},#{p2})"
|
101
|
+
else
|
102
|
+
'502 Aww hell no, use Active'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def _port(remote)
|
107
|
+
# remote = remote.split(',')
|
108
|
+
# remote_port = remote[4].to_i * 256 + remote[5].to_i
|
109
|
+
# unless @data_connection.nil?
|
110
|
+
# @data_connection.close
|
111
|
+
# @data_connection = nil
|
112
|
+
# end
|
113
|
+
# puts remote_port
|
114
|
+
# @data_connection = ::TCPSocket.open('127.0.0.1', remote_port)
|
115
|
+
# '200 Okay'
|
116
|
+
'500 Not implemented yet'
|
117
|
+
end
|
118
|
+
|
119
|
+
def _pwd(*args)
|
120
|
+
"257 \"#{self.directory}\" is current directory"
|
121
|
+
end
|
122
|
+
|
123
|
+
def _quit(*args)
|
124
|
+
respond_with '221 OMG bye!'
|
125
|
+
@client.close if @client
|
126
|
+
@client = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def _stor(filename)
|
130
|
+
@files << File.basename(filename.to_s)
|
131
|
+
respond_with('125 Do it!')
|
132
|
+
|
133
|
+
data_client = @data_server.accept
|
134
|
+
@data = data_client.recv(1024)
|
135
|
+
|
136
|
+
data_client.close
|
137
|
+
'226 Did it!'
|
138
|
+
end
|
139
|
+
|
140
|
+
def _type(type = 'A')
|
141
|
+
case type.to_s
|
142
|
+
when 'A'
|
143
|
+
'200 Type set to A.'
|
144
|
+
when 'I'
|
145
|
+
'200 Type set to I.'
|
146
|
+
else
|
147
|
+
'504 We don\'t allow those'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def _user(name = '')
|
152
|
+
(name.to_s == 'anonymous') ? '230 logged in' : '331 send your password'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/fake_ftp.rb
ADDED
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Hello im a text file
|
@@ -0,0 +1,353 @@
|
|
1
|
+
require "spec_helper.rb"
|
2
|
+
require 'net/ftp'
|
3
|
+
|
4
|
+
describe FakeFtp::Server do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@directory = File.expand_path("../fixtures/destination", File.dirname(__FILE__))
|
8
|
+
@text_filename = File.expand_path("../../fixtures/text_file.txt", File.dirname(__FILE__))
|
9
|
+
@bin_filename = File.expand_path("../../fixtures/invisible_bike.jpg", File.dirname(__FILE__))
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
FileUtils.rm_rf(@directory+"/*")
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'setup' do
|
17
|
+
it "starts a server on port n" do
|
18
|
+
server = FakeFtp::Server.new(21212)
|
19
|
+
server.port.should == 21212
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should defaults to port 21" do
|
23
|
+
server = FakeFtp::Server.new
|
24
|
+
server.port.should == 21
|
25
|
+
end
|
26
|
+
|
27
|
+
it "starts a passive server on port p" do
|
28
|
+
server = FakeFtp::Server.new(21212, 21213)
|
29
|
+
server.passive_port.should == 21213
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should start and stop" do
|
33
|
+
server = FakeFtp::Server.new(21212)
|
34
|
+
server.is_running?.should be_false
|
35
|
+
server.start
|
36
|
+
server.is_running?.should be_true
|
37
|
+
server.stop
|
38
|
+
server.is_running?.should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should start and stop passive port" do
|
42
|
+
server = FakeFtp::Server.new(21212, 21213)
|
43
|
+
server.is_running?(21213).should be_false
|
44
|
+
server.start
|
45
|
+
server.is_running?(21213).should be_true
|
46
|
+
server.stop
|
47
|
+
server.is_running?(21213).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should raise if attempting to use a bound port" do
|
51
|
+
server = FakeFtp::Server.new(21212)
|
52
|
+
server.start
|
53
|
+
proc { FakeFtp::Server.new(21212) }.should raise_error(Errno::EADDRINUSE, "Address already in use - 21212")
|
54
|
+
server.stop
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should raise if attempting to use a bound passive_port" do
|
58
|
+
server = FakeFtp::Server.new(21212, 21213)
|
59
|
+
server.start
|
60
|
+
proc { FakeFtp::Server.new(21214, 21213) }.should raise_error(Errno::EADDRINUSE, "Address already in use - 21213")
|
61
|
+
server.stop
|
62
|
+
end
|
63
|
+
|
64
|
+
it "can be configured with a directory store" do
|
65
|
+
server = FakeFtp::Server.new
|
66
|
+
server.directory = @directory
|
67
|
+
server.directory.should == @directory
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should default directory to /tmp if Rails.root does not exist" do
|
71
|
+
server = FakeFtp::Server.new
|
72
|
+
server.directory.should == '/tmp'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should default directory to Rails.root/tmp/ftp if exists" do
|
76
|
+
module Rails; end
|
77
|
+
Rails.stub!(:root).and_return('/somewhere')
|
78
|
+
server = FakeFtp::Server.new
|
79
|
+
server.directory.should == '/somewhere/tmp/ftp'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should clean up directory after itself"
|
83
|
+
|
84
|
+
it "should raise if attempting to delete a directory with contents other than its own"
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'socket' do
|
88
|
+
before :each do
|
89
|
+
@server = FakeFtp::Server.new(21212, 21213)
|
90
|
+
@server.start
|
91
|
+
end
|
92
|
+
|
93
|
+
after :each do
|
94
|
+
@server.stop
|
95
|
+
@server = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'FTP commands' do
|
99
|
+
context 'general' do
|
100
|
+
before :each do
|
101
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
102
|
+
end
|
103
|
+
|
104
|
+
after :each do
|
105
|
+
@client.close
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should accept connections" do
|
109
|
+
@client.gets.should == "200 Can has FTP?\r\n"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should get unknown command response when nothing is sent" do
|
113
|
+
@client.gets
|
114
|
+
@client.puts
|
115
|
+
@client.gets.should == "500 Unknown command\r\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "accepts QUIT" do
|
119
|
+
@client.gets
|
120
|
+
@client.puts "QUIT"
|
121
|
+
@client.gets.should == "221 OMG bye!\r\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should accept multiple commands in one session" do
|
125
|
+
@client.gets
|
126
|
+
@client.puts "USER thing"
|
127
|
+
@client.gets
|
128
|
+
@client.puts "PASS thing"
|
129
|
+
@client.gets
|
130
|
+
@client.puts "ACCT thing"
|
131
|
+
@client.gets
|
132
|
+
@client.puts "USER thing"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'passive' do
|
137
|
+
after :each do
|
138
|
+
@client.close
|
139
|
+
end
|
140
|
+
|
141
|
+
it "accepts PASV" do
|
142
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
143
|
+
@client.gets
|
144
|
+
@client.puts "PASV"
|
145
|
+
@client.gets.should == "227 Entering Passive Mode (127,0,0,1,82,221)\r\n"
|
146
|
+
end
|
147
|
+
|
148
|
+
it "responds with correct PASV port" do
|
149
|
+
@server.stop
|
150
|
+
@server.passive_port = 21111
|
151
|
+
@server.start
|
152
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
153
|
+
@client.gets
|
154
|
+
@client.puts "PASV"
|
155
|
+
@client.gets.should == "227 Entering Passive Mode (127,0,0,1,82,119)\r\n"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "does not accept PASV if no port set" do
|
159
|
+
@server.stop
|
160
|
+
@server.passive_port = nil
|
161
|
+
@server.start
|
162
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
163
|
+
@client.gets
|
164
|
+
@client.puts "PASV"
|
165
|
+
@client.gets.should == "502 Aww hell no, use Active\r\n"
|
166
|
+
end
|
167
|
+
|
168
|
+
it "does not accept PORT (yet)" do
|
169
|
+
## TODO this test can go away once the following pending test succeeds
|
170
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
171
|
+
@client.gets
|
172
|
+
@client.puts "PORT 127,0,0,1,82,224"
|
173
|
+
@client.gets.should == "500 Not implemented yet\r\n"
|
174
|
+
end
|
175
|
+
|
176
|
+
xit "accepts PORT and connects to port" do
|
177
|
+
# @testing = true
|
178
|
+
# @data_server = ::TCPServer.new('127.0.0.1', 21216)
|
179
|
+
# @data_connection = Thread.new do
|
180
|
+
# while @testing
|
181
|
+
# @server_client = @data_connection.accept
|
182
|
+
# @server_connection = Thread.new(@server_client) do |socket|
|
183
|
+
# @connected = true
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
# end
|
187
|
+
# @client.gets
|
188
|
+
# @client.puts "PORT 127,0,0,1,82,224"
|
189
|
+
# @client.gets.should == "200 Okay\r\n"
|
190
|
+
# @connected.should be_true
|
191
|
+
# @testing = false
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'authentication commands' do
|
196
|
+
before :each do
|
197
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
198
|
+
@client.gets ## connection successful response
|
199
|
+
end
|
200
|
+
|
201
|
+
after :each do
|
202
|
+
@client.close
|
203
|
+
end
|
204
|
+
|
205
|
+
it "accepts USER" do
|
206
|
+
@client.puts "USER some_dude"
|
207
|
+
@client.gets.should == "331 send your password\r\n"
|
208
|
+
end
|
209
|
+
|
210
|
+
it "accepts anonymous USER" do
|
211
|
+
@client.puts "USER anonymous"
|
212
|
+
@client.gets.should == "230 logged in\r\n"
|
213
|
+
end
|
214
|
+
|
215
|
+
it "accepts PASS" do
|
216
|
+
@client.puts "PASS password"
|
217
|
+
@client.gets.should == "230 logged in\r\n"
|
218
|
+
end
|
219
|
+
|
220
|
+
it "accepts ACCT" do
|
221
|
+
@client.puts "ACCT"
|
222
|
+
@client.gets.should == "230 WHATEVER!\r\n"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'directory commands' do
|
227
|
+
before :each do
|
228
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
229
|
+
@client.gets ## connection successful response
|
230
|
+
end
|
231
|
+
|
232
|
+
after :each do
|
233
|
+
@client.close
|
234
|
+
end
|
235
|
+
|
236
|
+
it "returns directory on PWD" do
|
237
|
+
@server.directory = @directory
|
238
|
+
@client.puts "PWD"
|
239
|
+
@client.gets.should == "257 \"#{@directory}\" is current directory\r\n"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "returns default directory on PWD" do
|
243
|
+
@server.directory = '/tmp'
|
244
|
+
@client.puts "PWD"
|
245
|
+
@client.gets.should == "257 \"/tmp\" is current directory\r\n"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "says OK to any CWD, CDUP, without doing anything" do
|
249
|
+
directory = @server.directory
|
250
|
+
@client.puts "CWD somewhere/else"
|
251
|
+
@client.gets.should == "250 OK!\r\n"
|
252
|
+
@server.directory.should == directory
|
253
|
+
@client.puts "CDUP"
|
254
|
+
@client.gets.should == "250 OK!\r\n"
|
255
|
+
@server.directory.should == directory
|
256
|
+
end
|
257
|
+
|
258
|
+
it "does not respond to MKD" do
|
259
|
+
@client.puts "MKD some_dir"
|
260
|
+
@client.gets.should == "500 Unknown command\r\n"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'file commands' do
|
265
|
+
before :each do
|
266
|
+
@client = TCPSocket.open('127.0.0.1', 21212)
|
267
|
+
@client.gets ## connection successful response
|
268
|
+
end
|
269
|
+
|
270
|
+
after :each do
|
271
|
+
@client.close
|
272
|
+
end
|
273
|
+
|
274
|
+
it "accepts TYPE ascii" do
|
275
|
+
@client.puts "TYPE A"
|
276
|
+
@client.gets.should == "200 Type set to A.\r\n"
|
277
|
+
end
|
278
|
+
|
279
|
+
it "accepts TYPE image" do
|
280
|
+
@client.puts "TYPE I"
|
281
|
+
@client.gets.should == "200 Type set to I.\r\n"
|
282
|
+
end
|
283
|
+
|
284
|
+
it "does not accept TYPEs other than ascii or image" do
|
285
|
+
@client.puts "TYPE E"
|
286
|
+
@client.gets.should == "504 We don't allow those\r\n"
|
287
|
+
@client.puts "TYPE N"
|
288
|
+
@client.gets.should == "504 We don't allow those\r\n"
|
289
|
+
@client.puts "TYPE T"
|
290
|
+
@client.gets.should == "504 We don't allow those\r\n"
|
291
|
+
@client.puts "TYPE C"
|
292
|
+
@client.gets.should == "504 We don't allow those\r\n"
|
293
|
+
@client.puts "TYPE L"
|
294
|
+
@client.gets.should == "504 We don't allow those\r\n"
|
295
|
+
end
|
296
|
+
|
297
|
+
it "accepts STOR with filename" do
|
298
|
+
@client.puts "STOR some_file"
|
299
|
+
@client.gets.should == "125 Do it!\r\n"
|
300
|
+
@data_client = TCPSocket.open('127.0.0.1', 21213)
|
301
|
+
@data_client.puts "1234567890"
|
302
|
+
@data_client.close
|
303
|
+
@client.gets.should == "226 Did it!\r\n"
|
304
|
+
@server.files.should include('some_file')
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'ftp client' do
|
310
|
+
before :each do
|
311
|
+
@ftp = Net::FTP.new
|
312
|
+
end
|
313
|
+
it 'should accept ftp connections' do
|
314
|
+
proc { @ftp.connect('127.0.0.1', 21212) }.should_not raise_error
|
315
|
+
proc { @ftp.close }.should_not raise_error
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should allow anonymous authentication" do
|
319
|
+
@ftp.connect('127.0.0.1', 21212)
|
320
|
+
proc { @ftp.login }.should_not raise_error
|
321
|
+
@ftp.close
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should allow named authentication" do
|
325
|
+
@ftp.connect('127.0.0.1', 21212)
|
326
|
+
proc { @ftp.login('someone', 'password') }.should_not raise_error
|
327
|
+
@ftp.close
|
328
|
+
end
|
329
|
+
|
330
|
+
it "should allow client to quit" do
|
331
|
+
@ftp.connect('127.0.0.1', 21212)
|
332
|
+
proc { @ftp.login('someone', 'password') }.should_not raise_error
|
333
|
+
proc { @ftp.quit }.should_not raise_error
|
334
|
+
@ftp.close
|
335
|
+
end
|
336
|
+
|
337
|
+
it "should put files to directory store using PASV" do
|
338
|
+
@ftp.connect('127.0.0.1', 21212)
|
339
|
+
@ftp.passive = true
|
340
|
+
proc { @ftp.put(@text_filename) }.should_not raise_error
|
341
|
+
@server.files.should include('text_file.txt')
|
342
|
+
end
|
343
|
+
|
344
|
+
xit "should disconnect clients on close" do
|
345
|
+
# TODO: when this succeeds, we can care less about manually closing clients
|
346
|
+
# otherwise we get a CLOSE_WAIT process hanging around that blocks our port
|
347
|
+
@ftp.connect('127.0.0.1', 21212)
|
348
|
+
@server.stop
|
349
|
+
@ftp.closed?.should be_true
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__), '**','*')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
|
8
|
+
require 'rspec'
|
9
|
+
require 'fake_ftp' # and any other gems you need
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fake_ftp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Colin Shield
|
14
|
+
- Eric Saxby
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-02-28 00:00:00 -08:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: bundler
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 3
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
- 10
|
35
|
+
version: 1.0.10
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 7
|
47
|
+
segments:
|
48
|
+
- 2
|
49
|
+
version: "2"
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rake
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 49
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 8
|
64
|
+
- 7
|
65
|
+
version: 0.8.7
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
description: Testing FTP? Use this!
|
69
|
+
email:
|
70
|
+
- sax+github@livinginthepast.org
|
71
|
+
executables: []
|
72
|
+
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files: []
|
76
|
+
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- .rvmrc
|
80
|
+
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- README.markdown
|
83
|
+
- Rakefile
|
84
|
+
- fake_ftp.gemspec
|
85
|
+
- lib/fake_ftp.rb
|
86
|
+
- lib/fake_ftp/server.rb
|
87
|
+
- lib/fake_ftp/version.rb
|
88
|
+
- spec/fixtures/invisible_bike.jpg
|
89
|
+
- spec/fixtures/text_file.txt
|
90
|
+
- spec/models/fake_ftp/server_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
has_rdoc: true
|
93
|
+
homepage: http://rubygems.org/gems/fake_ftp
|
94
|
+
licenses: []
|
95
|
+
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 23
|
116
|
+
segments:
|
117
|
+
- 1
|
118
|
+
- 3
|
119
|
+
- 6
|
120
|
+
version: 1.3.6
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project: fake_ftp
|
124
|
+
rubygems_version: 1.3.7
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Creates a fake FTP server for use in testing
|
128
|
+
test_files:
|
129
|
+
- spec/fixtures/invisible_bike.jpg
|
130
|
+
- spec/fixtures/text_file.txt
|
131
|
+
- spec/models/fake_ftp/server_spec.rb
|
132
|
+
- spec/spec_helper.rb
|