fake_ftp 0.0.9 → 0.1.0
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.
- checksums.yaml +7 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +28 -11
- data/Gemfile.travis +5 -0
- data/Guardfile +10 -0
- data/README.markdown +51 -53
- data/fake_ftp.gemspec +0 -5
- data/lib/fake_ftp/file.rb +6 -3
- data/lib/fake_ftp/server.rb +119 -29
- data/lib/fake_ftp/version.rb +1 -1
- data/spec/models/fake_ftp/file_spec.rb +36 -21
- data/spec/models/fake_ftp/server_spec.rb +327 -194
- metadata +12 -44
data/lib/fake_ftp/version.rb
CHANGED
@@ -8,48 +8,63 @@ describe FakeFtp::File do
|
|
8
8
|
|
9
9
|
it "has a name attribute" do
|
10
10
|
@file.name = "some name"
|
11
|
-
@file.name.
|
11
|
+
expect(@file.name).to eql("some name")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a last_modified_time attribute" do
|
15
|
+
now = Time.now
|
16
|
+
@file.last_modified_time = now
|
17
|
+
expect(@file.last_modified_time).to eql(now)
|
12
18
|
end
|
13
19
|
|
14
20
|
it "has a bytes attribute" do
|
15
21
|
@file.bytes = 87
|
16
|
-
@file.bytes.
|
22
|
+
expect(@file.bytes).to eql(87)
|
17
23
|
end
|
18
24
|
|
19
25
|
it "has a data attribute" do
|
20
26
|
@file.data = 'some data'
|
21
|
-
@file.data.
|
22
|
-
@file.bytes.
|
27
|
+
expect(@file.data).to eql('some data')
|
28
|
+
expect(@file.bytes).to eql('some data'.length)
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
32
|
context 'setup' do
|
27
33
|
it "can be initialized without attributes" do
|
28
34
|
file = FakeFtp::File.new
|
29
|
-
file.name.
|
30
|
-
file.bytes.
|
31
|
-
file.instance_variable_get(:@type).
|
35
|
+
expect(file.name).to be_nil
|
36
|
+
expect(file.bytes).to be_nil
|
37
|
+
expect(file.instance_variable_get(:@type)).to be_nil
|
32
38
|
end
|
33
39
|
|
34
40
|
it "can be initialized with name" do
|
35
41
|
file = FakeFtp::File.new('filename')
|
36
|
-
file.name.
|
37
|
-
file.bytes.
|
38
|
-
file.instance_variable_get(:@type).
|
42
|
+
expect(file.name).to eql('filename')
|
43
|
+
expect(file.bytes).to be_nil
|
44
|
+
expect(file.instance_variable_get(:@type)).to be_nil
|
39
45
|
end
|
40
46
|
|
41
47
|
it "can be initialized with name and bytes" do
|
42
48
|
file = FakeFtp::File.new('filename', 104)
|
43
|
-
file.name.
|
44
|
-
file.bytes.
|
45
|
-
file.instance_variable_get(:@type).
|
49
|
+
expect(file.name).to eql('filename')
|
50
|
+
expect(file.bytes).to eql(104)
|
51
|
+
expect(file.instance_variable_get(:@type)).to be_nil
|
46
52
|
end
|
47
|
-
|
53
|
+
|
48
54
|
it "can be initialized with name and bytes and type" do
|
49
55
|
file = FakeFtp::File.new('filename', 104, :passive)
|
50
|
-
file.name.
|
51
|
-
file.bytes.
|
52
|
-
file.instance_variable_get(:@type).
|
56
|
+
expect(file.name).to eql('filename')
|
57
|
+
expect(file.bytes).to eql(104)
|
58
|
+
expect(file.instance_variable_get(:@type)).to eql(:passive)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "can be initialized with name and bytes and type and last_modified_time" do
|
62
|
+
time = Time.now
|
63
|
+
file = FakeFtp::File.new('filename', 104, :passive, time)
|
64
|
+
expect(file.name).to eql('filename')
|
65
|
+
expect(file.bytes).to eql(104)
|
66
|
+
expect(file.instance_variable_get(:@type)).to eql(:passive)
|
67
|
+
expect(file.last_modified_time).to eql(time)
|
53
68
|
end
|
54
69
|
end
|
55
70
|
|
@@ -60,12 +75,12 @@ describe FakeFtp::File do
|
|
60
75
|
|
61
76
|
it "should be true if type is :passive" do
|
62
77
|
@file.type = :passive
|
63
|
-
@file.passive
|
78
|
+
expect(@file.passive?).to be_true
|
64
79
|
end
|
65
80
|
|
66
81
|
it "should be false if type is :active" do
|
67
82
|
@file.type = :active
|
68
|
-
@file.passive
|
83
|
+
expect(@file.passive?).to be_false
|
69
84
|
end
|
70
85
|
end
|
71
86
|
|
@@ -76,12 +91,12 @@ describe FakeFtp::File do
|
|
76
91
|
|
77
92
|
it "should be true if type is :active" do
|
78
93
|
@file.type = :active
|
79
|
-
@file.active
|
94
|
+
expect(@file.active?).to be_true
|
80
95
|
end
|
81
96
|
|
82
97
|
it "should be false if type is :passive" do
|
83
98
|
@file.type = :passive
|
84
|
-
@file.active
|
99
|
+
expect(@file.active?).to be_false
|
85
100
|
end
|
86
101
|
end
|
87
102
|
end
|
@@ -4,53 +4,53 @@ require 'net/ftp'
|
|
4
4
|
describe FakeFtp::Server, 'setup' do
|
5
5
|
it "starts a server on port n" do
|
6
6
|
server = FakeFtp::Server.new(21212)
|
7
|
-
server.port.
|
7
|
+
expect(server.port).to eql(21212)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should defaults to port 21" do
|
11
11
|
server = FakeFtp::Server.new
|
12
|
-
server.port.
|
12
|
+
expect(server.port).to eql(21)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "starts a passive server on port p" do
|
16
16
|
server = FakeFtp::Server.new(21212, 21213)
|
17
|
-
server.passive_port.
|
17
|
+
expect(server.passive_port).to eql(21213)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should start and stop" do
|
21
21
|
server = FakeFtp::Server.new(21212)
|
22
|
-
server.is_running
|
22
|
+
expect(server.is_running?).to be_false
|
23
23
|
server.start
|
24
|
-
server.is_running
|
24
|
+
expect(server.is_running?).to be_true
|
25
25
|
server.stop
|
26
|
-
server.is_running
|
26
|
+
expect(server.is_running?).to be_false
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should default :mode to :active" do
|
30
30
|
server = FakeFtp::Server.new(21212, 21213)
|
31
|
-
server.mode.
|
31
|
+
expect(server.mode).to eql(:active)
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should start and stop passive port" do
|
35
35
|
server = FakeFtp::Server.new(21212, 21213)
|
36
|
-
server.is_running?(21213).
|
36
|
+
expect(server.is_running?(21213)).to be_false
|
37
37
|
server.start
|
38
|
-
server.is_running?(21213).
|
38
|
+
expect(server.is_running?(21213)).to be_true
|
39
39
|
server.stop
|
40
|
-
server.is_running?(21213).
|
40
|
+
expect(server.is_running?(21213)).to be_false
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should raise if attempting to use a bound port" do
|
44
44
|
server = FakeFtp::Server.new(21212)
|
45
45
|
server.start
|
46
|
-
|
46
|
+
expect { FakeFtp::Server.new(21212) }.to raise_error(Errno::EADDRINUSE, "Address already in use - 21212")
|
47
47
|
server.stop
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should raise if attempting to use a bound passive_port" do
|
51
51
|
server = FakeFtp::Server.new(21212, 21213)
|
52
52
|
server.start
|
53
|
-
|
53
|
+
expect { FakeFtp::Server.new(21214, 21213) }.to raise_error(Errno::EADDRINUSE, "Address already in use - 21213")
|
54
54
|
server.stop
|
55
55
|
end
|
56
56
|
end
|
@@ -62,106 +62,96 @@ describe FakeFtp::Server, 'files' do
|
|
62
62
|
before { server.instance_variable_set(:@files, [file]) }
|
63
63
|
|
64
64
|
it "returns filenames from :files" do
|
65
|
-
server.files.
|
65
|
+
expect(server.files).to include('filename')
|
66
66
|
end
|
67
67
|
|
68
68
|
it "can be accessed with :file" do
|
69
|
-
server.file('filename').
|
69
|
+
expect(server.file('filename')).to eql(file)
|
70
70
|
end
|
71
71
|
|
72
72
|
it "can reset files" do
|
73
73
|
server.reset
|
74
|
-
server.files.
|
74
|
+
expect(server.files).to eql([])
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
describe FakeFtp::Server, 'commands' do
|
79
79
|
let(:server) { FakeFtp::Server.new(21212, 21213) }
|
80
|
+
let(:client) { TCPSocket.open('127.0.0.1', 21212) }
|
80
81
|
|
81
82
|
before { server.start }
|
82
|
-
after {
|
83
|
+
after {
|
84
|
+
client.close
|
85
|
+
server.stop
|
86
|
+
}
|
83
87
|
|
84
88
|
context 'general' do
|
85
|
-
before :each do
|
86
|
-
@client = TCPSocket.open('127.0.0.1', 21212)
|
87
|
-
end
|
88
|
-
|
89
|
-
after :each do
|
90
|
-
@client.close
|
91
|
-
end
|
92
89
|
|
93
90
|
it "should accept connections" do
|
94
|
-
|
91
|
+
expect(client.gets).to eql("220 Can has FTP?\r\n")
|
95
92
|
end
|
96
93
|
|
97
94
|
it "should get unknown command response when nothing is sent" do
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
client.gets
|
96
|
+
client.puts
|
97
|
+
expect(client.gets).to eql("500 Unknown command\r\n")
|
101
98
|
end
|
102
99
|
|
103
100
|
it "accepts QUIT" do
|
104
|
-
|
105
|
-
|
106
|
-
|
101
|
+
client.gets
|
102
|
+
client.puts "QUIT"
|
103
|
+
expect(client.gets).to eql("221 OMG bye!\r\n")
|
107
104
|
end
|
108
105
|
|
109
106
|
it "should accept multiple commands in one session" do
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
107
|
+
client.gets
|
108
|
+
client.puts "USER thing"
|
109
|
+
client.gets
|
110
|
+
client.puts "PASS thing"
|
111
|
+
client.gets
|
112
|
+
client.puts "ACCT thing"
|
113
|
+
client.gets
|
114
|
+
client.puts "USER thing"
|
118
115
|
end
|
119
116
|
end
|
120
117
|
|
121
118
|
context 'passive' do
|
122
|
-
after :each do
|
123
|
-
@client.close
|
124
|
-
end
|
125
119
|
|
126
120
|
it "accepts PASV" do
|
127
|
-
server.mode.
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
server.mode.should == :passive
|
121
|
+
expect(server.mode).to eql(:active)
|
122
|
+
client.gets
|
123
|
+
client.puts "PASV"
|
124
|
+
expect(client.gets).to eql("227 Entering Passive Mode (127,0,0,1,82,221)\r\n")
|
125
|
+
expect(server.mode).to eql(:passive)
|
133
126
|
end
|
134
127
|
|
135
128
|
it "responds with correct PASV port" do
|
136
129
|
server.stop
|
137
130
|
server.passive_port = 21111
|
138
131
|
server.start
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@client.gets.should == "227 Entering Passive Mode (127,0,0,1,82,119)\r\n"
|
132
|
+
client.gets
|
133
|
+
client.puts "PASV"
|
134
|
+
expect(client.gets).to eql("227 Entering Passive Mode (127,0,0,1,82,119)\r\n")
|
143
135
|
end
|
144
136
|
|
145
137
|
it "does not accept PASV if no port set" do
|
146
138
|
server.stop
|
147
139
|
server.passive_port = nil
|
148
140
|
server.start
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
@client.gets.should == "502 Aww hell no, use Active\r\n"
|
141
|
+
client.gets
|
142
|
+
client.puts "PASV"
|
143
|
+
expect(client.gets).to eql("502 Aww hell no, use Active\r\n")
|
153
144
|
end
|
154
145
|
end
|
155
146
|
|
156
147
|
context 'active' do
|
157
148
|
before :each do
|
158
|
-
|
159
|
-
@client.gets
|
149
|
+
client.gets
|
160
150
|
|
161
151
|
@data_server = ::TCPServer.new('127.0.0.1', 21216)
|
162
152
|
@data_connection = Thread.new do
|
163
153
|
@server_client = @data_server.accept
|
164
|
-
@server_client.
|
154
|
+
expect(@server_client).to_not be_nil
|
165
155
|
end
|
166
156
|
end
|
167
157
|
|
@@ -169,188 +159,252 @@ describe FakeFtp::Server, 'commands' do
|
|
169
159
|
@data_server.close
|
170
160
|
@data_server = nil
|
171
161
|
@data_connection = nil
|
172
|
-
@client.close
|
173
162
|
end
|
174
163
|
|
175
164
|
it "accepts PORT and connects to port" do
|
176
|
-
|
177
|
-
|
165
|
+
client.puts "PORT 127,0,0,1,82,224"
|
166
|
+
expect(client.gets).to eql("200 Okay\r\n")
|
178
167
|
|
179
168
|
@data_connection.join
|
180
169
|
end
|
181
170
|
|
182
171
|
it "should switch to :active on port command" do
|
183
|
-
server.mode.
|
184
|
-
|
185
|
-
|
186
|
-
server.mode.
|
172
|
+
expect(server.mode).to eql(:active)
|
173
|
+
client.puts 'PASV'
|
174
|
+
client.gets
|
175
|
+
expect(server.mode).to eql(:passive)
|
187
176
|
|
188
|
-
|
189
|
-
|
177
|
+
client.puts "PORT 127,0,0,1,82,224"
|
178
|
+
expect(client.gets).to eql("200 Okay\r\n")
|
190
179
|
|
191
180
|
@data_connection.join
|
192
181
|
|
193
|
-
server.mode.
|
182
|
+
expect(server.mode).to eql(:active)
|
194
183
|
end
|
195
184
|
end
|
196
185
|
|
197
186
|
context 'authentication commands' do
|
198
187
|
before :each do
|
199
|
-
|
200
|
-
@client.gets ## connection successful response
|
201
|
-
end
|
202
|
-
|
203
|
-
after :each do
|
204
|
-
@client.close
|
188
|
+
client.gets ## connection successful response
|
205
189
|
end
|
206
190
|
|
207
191
|
it "accepts USER" do
|
208
|
-
|
209
|
-
|
192
|
+
client.puts "USER some_dude"
|
193
|
+
expect(client.gets).to eql("331 send your password\r\n")
|
210
194
|
end
|
211
195
|
|
212
196
|
it "accepts anonymous USER" do
|
213
|
-
|
214
|
-
|
197
|
+
client.puts "USER anonymous"
|
198
|
+
expect(client.gets).to eql("230 logged in\r\n")
|
215
199
|
end
|
216
200
|
|
217
201
|
it "accepts PASS" do
|
218
|
-
|
219
|
-
|
202
|
+
client.puts "PASS password"
|
203
|
+
expect(client.gets).to eql("230 logged in\r\n")
|
220
204
|
end
|
221
205
|
|
222
206
|
it "accepts ACCT" do
|
223
|
-
|
224
|
-
|
207
|
+
client.puts "ACCT"
|
208
|
+
expect(client.gets).to eql("230 WHATEVER!\r\n")
|
225
209
|
end
|
226
210
|
end
|
227
211
|
|
228
212
|
context 'directory commands' do
|
229
213
|
before :each do
|
230
|
-
|
231
|
-
@client.gets ## connection successful response
|
232
|
-
end
|
233
|
-
|
234
|
-
after :each do
|
235
|
-
@client.close
|
214
|
+
client.gets ## connection successful response
|
236
215
|
end
|
237
216
|
|
238
217
|
it "returns directory on PWD" do
|
239
|
-
|
240
|
-
|
218
|
+
client.puts "PWD"
|
219
|
+
expect(client.gets).to eql("257 \"/pub\" is current directory\r\n")
|
241
220
|
end
|
242
221
|
|
243
222
|
it "says OK to any CWD, CDUP, without doing anything" do
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
end
|
249
|
-
|
250
|
-
it "does not respond to MKD" do
|
251
|
-
@client.puts "MKD some_dir"
|
252
|
-
@client.gets.should == "500 Unknown command\r\n"
|
223
|
+
client.puts "CWD somewhere/else"
|
224
|
+
expect(client.gets).to eql("250 OK!\r\n")
|
225
|
+
client.puts "CDUP"
|
226
|
+
expect(client.gets).to eql("250 OK!\r\n")
|
253
227
|
end
|
254
228
|
end
|
255
229
|
|
256
230
|
context 'file commands' do
|
257
231
|
before :each do
|
258
|
-
|
259
|
-
@client.gets ## connection successful response
|
260
|
-
end
|
261
|
-
|
262
|
-
after :each do
|
263
|
-
@client.close
|
232
|
+
client.gets ## connection successful response
|
264
233
|
end
|
265
234
|
|
266
235
|
it "accepts TYPE ascii" do
|
267
|
-
|
268
|
-
|
236
|
+
client.puts "TYPE A"
|
237
|
+
expect(client.gets).to eql("200 Type set to A.\r\n")
|
269
238
|
end
|
270
239
|
|
271
240
|
it "accepts TYPE image" do
|
272
|
-
|
273
|
-
|
241
|
+
client.puts "TYPE I"
|
242
|
+
expect(client.gets).to eql("200 Type set to I.\r\n")
|
274
243
|
end
|
275
244
|
|
276
245
|
it "does not accept TYPEs other than ascii or image" do
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
246
|
+
client.puts "TYPE E"
|
247
|
+
expect(client.gets).to eql("504 We don't allow those\r\n")
|
248
|
+
client.puts "TYPE N"
|
249
|
+
expect(client.gets).to eql("504 We don't allow those\r\n")
|
250
|
+
client.puts "TYPE T"
|
251
|
+
expect(client.gets).to eql("504 We don't allow those\r\n")
|
252
|
+
client.puts "TYPE C"
|
253
|
+
expect(client.gets).to eql("504 We don't allow those\r\n")
|
254
|
+
client.puts "TYPE L"
|
255
|
+
expect(client.gets).to eql("504 We don't allow those\r\n")
|
287
256
|
end
|
288
257
|
|
289
258
|
context 'passive' do
|
259
|
+
let(:data_client) { TCPSocket.open('127.0.0.1', 21213) }
|
260
|
+
|
290
261
|
before :each do
|
291
|
-
|
292
|
-
|
262
|
+
client.puts 'PASV'
|
263
|
+
expect(client.gets).to eql("227 Entering Passive Mode (127,0,0,1,82,221)\r\n")
|
293
264
|
end
|
294
265
|
|
295
266
|
it "accepts STOR with filename" do
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
server.
|
303
|
-
server.file('some_file').
|
267
|
+
client.puts "STOR some_file"
|
268
|
+
expect(client.gets).to eql("125 Do it!\r\n")
|
269
|
+
data_client.puts "1234567890"
|
270
|
+
data_client.close
|
271
|
+
expect(client.gets).to eql("226 Did it!\r\n")
|
272
|
+
expect(server.files).to include('some_file')
|
273
|
+
expect(server.file('some_file').bytes).to eql(10)
|
274
|
+
expect(server.file('some_file').data).to eql("1234567890")
|
275
|
+
end
|
276
|
+
|
277
|
+
it "accepts STOR with filename and trailing newline" do
|
278
|
+
client.puts "STOR some_file"
|
279
|
+
client.gets
|
280
|
+
# puts tries to be smart and only write a single \n
|
281
|
+
data_client.puts "1234567890\n\n"
|
282
|
+
data_client.close
|
283
|
+
expect(client.gets).to eql("226 Did it!\r\n")
|
284
|
+
expect(server.files).to include('some_file')
|
285
|
+
expect(server.file('some_file').bytes).to eql(11)
|
286
|
+
expect(server.file('some_file').data).to eql("1234567890\n")
|
287
|
+
end
|
288
|
+
|
289
|
+
it "accepts STOR with filename and long file" do
|
290
|
+
client.puts "STOR some_file"
|
291
|
+
expect(client.gets).to eql("125 Do it!\r\n")
|
292
|
+
data_client.puts("1234567890" * 10_000)
|
293
|
+
data_client.close
|
294
|
+
expect(client.gets).to eql("226 Did it!\r\n")
|
295
|
+
expect(server.files).to include('some_file')
|
296
|
+
end
|
297
|
+
|
298
|
+
it "accepts STOR with streams" do
|
299
|
+
client.puts "STOR some_file"
|
300
|
+
expect(client.gets).to eql("125 Do it!\r\n")
|
301
|
+
data_client.write "1234567890"
|
302
|
+
data_client.flush
|
303
|
+
data_client.write "1234567890"
|
304
|
+
data_client.flush
|
305
|
+
data_client.close
|
306
|
+
expect(client.gets).to eql("226 Did it!\r\n")
|
307
|
+
expect(server.file('some_file').data).to eql("12345678901234567890")
|
304
308
|
end
|
305
309
|
|
306
310
|
it "does not accept RETR without a filename" do
|
307
|
-
|
308
|
-
|
311
|
+
client.puts "RETR"
|
312
|
+
expect(client.gets).to eql("501 No filename given\r\n")
|
309
313
|
end
|
310
314
|
|
311
315
|
it "does not serve files that do not exist" do
|
312
|
-
|
313
|
-
|
316
|
+
client.puts "RETR some_file"
|
317
|
+
expect(client.gets).to eql("550 File not found\r\n")
|
314
318
|
end
|
315
319
|
|
316
320
|
it "accepts RETR with a filename" do
|
317
321
|
server.add_file('some_file', '1234567890')
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
322
|
+
client.puts "RETR some_file"
|
323
|
+
expect(client.gets).to eql("150 File status ok, about to open data connection\r\n")
|
324
|
+
data = data_client.read(1024)
|
325
|
+
data_client.close
|
326
|
+
expect(data).to eql('1234567890')
|
327
|
+
expect(client.gets).to eql("226 File transferred\r\n")
|
328
|
+
end
|
329
|
+
|
330
|
+
it "accepts DELE with a filename" do
|
331
|
+
server.add_file('some_file', '1234567890')
|
332
|
+
client.puts "DELE some_file"
|
333
|
+
expect(client.gets).to eql("250 Delete operation successful.\r\n")
|
334
|
+
expect(server.files).to_not include('some_file')
|
335
|
+
end
|
336
|
+
|
337
|
+
it "gives error message when trying to delete a file that does not exist" do
|
338
|
+
client.puts "DELE non_existing_file"
|
339
|
+
expect(client.gets).to eql("550 Delete operation failed.\r\n")
|
325
340
|
end
|
326
341
|
|
327
342
|
it "accepts a LIST command" do
|
328
343
|
server.add_file('some_file', '1234567890')
|
329
344
|
server.add_file('another_file', '1234567890')
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
data.should == [
|
345
|
+
client.puts "LIST"
|
346
|
+
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
347
|
+
data = data_client.read(2048)
|
348
|
+
data_client.close
|
349
|
+
expect(data).to eql([
|
336
350
|
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file('some_file').created.strftime('%b %d %H:%M')}\tsome_file",
|
337
351
|
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file('another_file').created.strftime('%b %d %H:%M')}\tanother_file",
|
338
|
-
].join("\n")
|
339
|
-
|
352
|
+
].join("\n"))
|
353
|
+
expect(client.gets).to eql("226 List information transferred\r\n")
|
354
|
+
end
|
355
|
+
|
356
|
+
it "accepts a LIST command with a wildcard argument" do
|
357
|
+
files = ['test.jpg', 'test-2.jpg', 'test.txt']
|
358
|
+
files.each do |file|
|
359
|
+
server.add_file(file, '1234567890')
|
360
|
+
end
|
361
|
+
|
362
|
+
client.puts "LIST *.jpg"
|
363
|
+
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
364
|
+
|
365
|
+
data = data_client.read(2048)
|
366
|
+
data_client.close
|
367
|
+
expect(data).to eql(files[0,2].map do |file|
|
368
|
+
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file(file).created.strftime('%b %d %H:%M')}\t#{file}"
|
369
|
+
end.join("\n"))
|
370
|
+
expect(client.gets).to eql("226 List information transferred\r\n")
|
371
|
+
end
|
372
|
+
|
373
|
+
it "accepts a LIST command with multiple wildcard arguments" do
|
374
|
+
files = ['test.jpg', 'test.gif', 'test.txt']
|
375
|
+
files.each do |file|
|
376
|
+
server.add_file(file, '1234567890')
|
377
|
+
end
|
378
|
+
|
379
|
+
client.puts "LIST *.jpg *.gif"
|
380
|
+
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
381
|
+
|
382
|
+
data = data_client.read(2048)
|
383
|
+
data_client.close
|
384
|
+
expect(data).to eql(files[0,2].map do |file|
|
385
|
+
"-rw-r--r--\t1\towner\tgroup\t10\t#{server.file(file).created.strftime('%b %d %H:%M')}\t#{file}"
|
386
|
+
end.join("\n"))
|
387
|
+
expect(client.gets).to eql("226 List information transferred\r\n")
|
340
388
|
end
|
341
389
|
|
342
390
|
it "accepts an NLST command" do
|
343
391
|
server.add_file('some_file', '1234567890')
|
344
392
|
server.add_file('another_file', '1234567890')
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
@client.gets.should == "226 List information transferred\r\n"
|
393
|
+
client.puts "NLST"
|
394
|
+
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
395
|
+
data = data_client.read(1024)
|
396
|
+
data_client.close
|
397
|
+
expect(data).to eql("some_file\nanother_file")
|
398
|
+
expect(client.gets).to eql("226 List information transferred\r\n")
|
352
399
|
end
|
353
400
|
|
401
|
+
it "should allow mdtm" do
|
402
|
+
filename = "file.txt"
|
403
|
+
now = Time.now
|
404
|
+
server.add_file(filename, "some dummy content", now)
|
405
|
+
client.puts "MDTM #{filename}"
|
406
|
+
expect(client.gets).to eql("213 #{now.strftime("%Y%m%d%H%M%S")}\r\n")
|
407
|
+
end
|
354
408
|
end
|
355
409
|
|
356
410
|
context 'active' do
|
@@ -367,58 +421,123 @@ describe FakeFtp::Server, 'commands' do
|
|
367
421
|
@data_server = nil
|
368
422
|
end
|
369
423
|
|
424
|
+
it 'creates a directory on MKD' do
|
425
|
+
client.puts "MKD some_dir"
|
426
|
+
expect(client.gets).to eql("257 OK!\r\n")
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'should save the directory after you CWD' do
|
430
|
+
client.puts "CWD /somewhere/else"
|
431
|
+
expect(client.gets).to eql("250 OK!\r\n")
|
432
|
+
client.puts "PWD"
|
433
|
+
expect(client.gets).to eql("257 \"/somewhere/else\" is current directory\r\n")
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'CWD should add a / to the beginning of the directory' do
|
437
|
+
client.puts "CWD somewhere/else"
|
438
|
+
expect(client.gets).to eql("250 OK!\r\n")
|
439
|
+
client.puts "PWD"
|
440
|
+
expect(client.gets).to eql("257 \"/somewhere/else\" is current directory\r\n")
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'should not change the directory on CDUP' do
|
444
|
+
client.puts "CDUP"
|
445
|
+
expect(client.gets).to eql("250 OK!\r\n")
|
446
|
+
client.puts "PWD"
|
447
|
+
expect(client.gets).to eql("257 \"/pub\" is current directory\r\n")
|
448
|
+
end
|
449
|
+
|
370
450
|
it "sends error message if no PORT received" do
|
371
|
-
|
372
|
-
|
451
|
+
client.puts "STOR some_file"
|
452
|
+
expect(client.gets).to eql("425 Ain't no data port!\r\n")
|
373
453
|
end
|
374
454
|
|
375
455
|
it "accepts STOR with filename" do
|
376
|
-
|
377
|
-
|
456
|
+
client.puts "PORT 127,0,0,1,82,224"
|
457
|
+
expect(client.gets).to eql("200 Okay\r\n")
|
378
458
|
|
379
|
-
|
380
|
-
|
459
|
+
client.puts "STOR some_other_file"
|
460
|
+
expect(client.gets).to eql("125 Do it!\r\n")
|
381
461
|
|
382
462
|
@data_connection.join
|
383
463
|
@server_client.print "12345"
|
384
464
|
@server_client.close
|
385
465
|
|
386
|
-
|
387
|
-
server.files.
|
388
|
-
server.file('some_other_file').bytes.
|
466
|
+
expect(client.gets).to eql("226 Did it!\r\n")
|
467
|
+
expect(server.files).to include('some_other_file')
|
468
|
+
expect(server.file('some_other_file').bytes).to eql(5)
|
389
469
|
end
|
390
470
|
|
391
471
|
it "accepts RETR with a filename" do
|
392
|
-
|
393
|
-
|
472
|
+
client.puts "PORT 127,0,0,1,82,224"
|
473
|
+
expect(client.gets).to eql("200 Okay\r\n")
|
394
474
|
|
395
475
|
server.add_file('some_file', '1234567890')
|
396
|
-
|
397
|
-
|
476
|
+
client.puts "RETR some_file"
|
477
|
+
expect(client.gets).to eql("150 File status ok, about to open data connection\r\n")
|
398
478
|
|
399
479
|
@data_connection.join
|
400
480
|
data = @server_client.read(1024)
|
401
481
|
@server_client.close
|
402
482
|
|
403
|
-
data.
|
404
|
-
|
483
|
+
expect(data).to eql('1234567890')
|
484
|
+
expect(client.gets).to eql("226 File transferred\r\n")
|
485
|
+
end
|
486
|
+
|
487
|
+
it "accepts RNFR without filename" do
|
488
|
+
client.puts "RNFR"
|
489
|
+
expect(client.gets).to eql("501 Send path name.\r\n")
|
490
|
+
end
|
491
|
+
|
492
|
+
it "accepts RNTO without RNFR" do
|
493
|
+
client.puts "RNTO some_other_file"
|
494
|
+
expect(client.gets).to eql("503 Send RNFR first.\r\n")
|
495
|
+
end
|
496
|
+
|
497
|
+
it "accepts RNTO and RNFR without filename" do
|
498
|
+
client.puts "RNFR from_file"
|
499
|
+
expect(client.gets).to eql("350 Send RNTO to complete rename.\r\n")
|
500
|
+
|
501
|
+
client.puts "RNTO"
|
502
|
+
expect(client.gets).to eql("501 Send path name.\r\n")
|
503
|
+
end
|
504
|
+
|
505
|
+
it "accepts RNTO and RNFR for not existing file" do
|
506
|
+
client.puts "RNFR from_file"
|
507
|
+
expect(client.gets).to eql("350 Send RNTO to complete rename.\r\n")
|
508
|
+
|
509
|
+
client.puts "RNTO to_file"
|
510
|
+
expect(client.gets).to eql("550 File not found.\r\n")
|
511
|
+
end
|
512
|
+
|
513
|
+
it "accepts RNTO and RNFR" do
|
514
|
+
server.add_file('from_file', '1234567890')
|
515
|
+
|
516
|
+
client.puts "RNFR from_file"
|
517
|
+
expect(client.gets).to eql("350 Send RNTO to complete rename.\r\n")
|
518
|
+
|
519
|
+
client.puts "RNTO to_file"
|
520
|
+
expect(client.gets).to eql("250 Path renamed.\r\n")
|
521
|
+
|
522
|
+
expect(server.files).to include('to_file')
|
523
|
+
expect(server.files).to_not include('from_file')
|
405
524
|
end
|
406
525
|
|
407
526
|
it "accepts an NLST command" do
|
408
|
-
|
409
|
-
|
527
|
+
client.puts "PORT 127,0,0,1,82,224"
|
528
|
+
expect(client.gets).to eql("200 Okay\r\n")
|
410
529
|
|
411
530
|
server.add_file('some_file', '1234567890')
|
412
531
|
server.add_file('another_file', '1234567890')
|
413
|
-
|
414
|
-
|
532
|
+
client.puts "NLST"
|
533
|
+
expect(client.gets).to eql("150 Listing status ok, about to open data connection\r\n")
|
415
534
|
|
416
535
|
@data_connection.join
|
417
536
|
data = @server_client.read(1024)
|
418
537
|
@server_client.close
|
419
538
|
|
420
|
-
data.
|
421
|
-
|
539
|
+
expect(data).to eql("some_file\nanother_file")
|
540
|
+
expect(client.gets).to eql("226 List information transferred\r\n")
|
422
541
|
end
|
423
542
|
end
|
424
543
|
end
|
@@ -437,54 +556,68 @@ describe FakeFtp::Server, 'with ftp client' do
|
|
437
556
|
end
|
438
557
|
|
439
558
|
it 'should accept connections' do
|
440
|
-
|
559
|
+
expect { client.connect('127.0.0.1', 21212) }.to_not raise_error
|
441
560
|
end
|
442
561
|
|
443
562
|
context "" do
|
444
563
|
before { client.connect("127.0.0.1", 21212) }
|
445
564
|
|
446
565
|
it "should allow anonymous authentication" do
|
447
|
-
|
566
|
+
expect { client.login }.to_not raise_error
|
448
567
|
end
|
449
568
|
|
450
569
|
it "should allow named authentication" do
|
451
|
-
|
570
|
+
expect { client.login('someone', 'password') }.to_not raise_error
|
452
571
|
end
|
453
572
|
|
454
573
|
it "should allow client to quit" do
|
455
|
-
|
456
|
-
|
574
|
+
expect { client.login('someone', 'password') }.to_not raise_error
|
575
|
+
expect { client.quit }.to_not raise_error
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should allow mtime" do
|
579
|
+
filename = 'someone'
|
580
|
+
time = Time.now
|
581
|
+
server.add_file(filename, "some data", time)
|
582
|
+
|
583
|
+
client.passive = false
|
584
|
+
mtime = client.mtime(filename)
|
585
|
+
expect(mtime.to_s).to eql(time.to_s)
|
586
|
+
|
587
|
+
client.passive = true
|
588
|
+
mtime = client.mtime(filename)
|
589
|
+
expect(mtime.to_s).to eql(time.to_s)
|
457
590
|
end
|
458
591
|
|
459
592
|
it "should put files using PASV" do
|
460
|
-
File.stat(text_filename).size.
|
593
|
+
expect(File.stat(text_filename).size).to eql(20)
|
461
594
|
|
462
595
|
client.passive = true
|
463
|
-
|
596
|
+
expect { client.put(text_filename) }.to_not raise_error
|
464
597
|
|
465
|
-
server.files.
|
466
|
-
server.file('text_file.txt').bytes.
|
467
|
-
server.file('text_file.txt').
|
468
|
-
server.file('text_file.txt').
|
598
|
+
expect(server.files).to include('text_file.txt')
|
599
|
+
expect(server.file('text_file.txt').bytes).to eql(20)
|
600
|
+
expect(server.file('text_file.txt')).to be_passive
|
601
|
+
expect(server.file('text_file.txt')).to_not be_active
|
469
602
|
end
|
470
603
|
|
471
604
|
it "should put files using active" do
|
472
|
-
File.stat(text_filename).size.
|
605
|
+
expect(File.stat(text_filename).size).to eql(20)
|
473
606
|
|
474
607
|
client.passive = false
|
475
|
-
|
608
|
+
expect { client.put(text_filename) }.to_not raise_error
|
476
609
|
|
477
|
-
server.files.
|
478
|
-
server.file('text_file.txt').bytes.
|
479
|
-
server.file('text_file.txt').
|
480
|
-
server.file('text_file.txt').
|
610
|
+
expect(server.files).to include('text_file.txt')
|
611
|
+
expect(server.file('text_file.txt').bytes).to eql(20)
|
612
|
+
expect(server.file('text_file.txt')).to_not be_passive
|
613
|
+
expect(server.file('text_file.txt')).to be_active
|
481
614
|
end
|
482
615
|
|
483
616
|
xit "should disconnect clients on close" do
|
484
617
|
# TODO: when this succeeds, we can care less about manually closing clients
|
485
618
|
# otherwise we get a CLOSE_WAIT process hanging around that blocks our port
|
486
619
|
server.stop
|
487
|
-
client.closed
|
620
|
+
expect(client.closed?).to be_true
|
488
621
|
end
|
489
622
|
end
|
490
623
|
end
|