fake_ftp 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +16 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +27 -0
  6. data/.rubocop_todo.yml +7 -0
  7. data/.simplecov +6 -0
  8. data/.travis.yml +27 -2
  9. data/CHANGELOG.md +136 -0
  10. data/Gemfile +9 -7
  11. data/Guardfile +5 -4
  12. data/LICENSE.md +20 -0
  13. data/README.md +30 -63
  14. data/Rakefile +12 -7
  15. data/fake_ftp.gemspec +16 -17
  16. data/lib/fake_ftp.rb +3 -2
  17. data/lib/fake_ftp/file.rb +11 -5
  18. data/lib/fake_ftp/server.rb +138 -261
  19. data/lib/fake_ftp/server_commands.rb +6 -0
  20. data/lib/fake_ftp/server_commands/acct.rb +11 -0
  21. data/lib/fake_ftp/server_commands/cdup.rb +11 -0
  22. data/lib/fake_ftp/server_commands/cwd.rb +13 -0
  23. data/lib/fake_ftp/server_commands/dele.rb +25 -0
  24. data/lib/fake_ftp/server_commands/list.rb +39 -0
  25. data/lib/fake_ftp/server_commands/mdtm.rb +17 -0
  26. data/lib/fake_ftp/server_commands/mkd.rb +11 -0
  27. data/lib/fake_ftp/server_commands/nlst.rb +32 -0
  28. data/lib/fake_ftp/server_commands/pass.rb +11 -0
  29. data/lib/fake_ftp/server_commands/pasv.rb +15 -0
  30. data/lib/fake_ftp/server_commands/port.rb +23 -0
  31. data/lib/fake_ftp/server_commands/pwd.rb +11 -0
  32. data/lib/fake_ftp/server_commands/quit.rb +13 -0
  33. data/lib/fake_ftp/server_commands/retr.rb +32 -0
  34. data/lib/fake_ftp/server_commands/rnfr.rb +18 -0
  35. data/lib/fake_ftp/server_commands/rnto.rb +22 -0
  36. data/lib/fake_ftp/server_commands/site.rb +11 -0
  37. data/lib/fake_ftp/server_commands/size.rb +11 -0
  38. data/lib/fake_ftp/server_commands/stor.rb +30 -0
  39. data/lib/fake_ftp/server_commands/type.rb +18 -0
  40. data/lib/fake_ftp/server_commands/user.rb +12 -0
  41. data/lib/fake_ftp/server_commands/wat.rb +33 -0
  42. data/lib/fake_ftp/version.rb +3 -1
  43. data/spec/functional/server_spec.rb +459 -358
  44. data/spec/integration/server_spec.rb +39 -29
  45. data/spec/models/fake_ftp/file_spec.rb +22 -21
  46. data/spec/models/fake_ftp/server_spec.rb +33 -32
  47. data/spec/spec_helper.rb +98 -14
  48. metadata +32 -3
@@ -1,10 +1,13 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'net/ftp'
3
4
 
4
- describe FakeFtp::Server, 'with ftp client' do
5
- let(:server) { FakeFtp::Server.new(21212, 21213) }
6
- let(:client) { Net::FTP.new }
7
- let(:text_filename) { File.expand_path("../fixtures/text_file.txt", File.dirname(__FILE__)) }
5
+ describe FakeFtp::Server, 'with ftp client', integration: true do
6
+ let(:server) { FakeFtp::Server.new(21_212, 21_213, absolute: true) }
7
+ let(:client) { Net::FTP.new(nil, debug_mode: ENV['DEBUG'] == '1') }
8
+ let(:text_filename) do
9
+ File.expand_path('../../fixtures/text_file.txt', __FILE__)
10
+ end
8
11
 
9
12
  before { server.start }
10
13
 
@@ -14,29 +17,29 @@ describe FakeFtp::Server, 'with ftp client' do
14
17
  end
15
18
 
16
19
  it 'should accept connections' do
17
- expect { client.connect('127.0.0.1', 21212) }.to_not raise_error
20
+ expect { client.connect('127.0.0.1', 21_212) }.to_not raise_error
18
21
  end
19
22
 
20
- context "with client" do
21
- before { client.connect("127.0.0.1", 21212) }
23
+ context 'with client' do
24
+ before { client.connect('127.0.0.1', 21_212) }
22
25
 
23
- it "should allow anonymous authentication" do
26
+ it 'should allow anonymous authentication' do
24
27
  expect { client.login }.to_not raise_error
25
28
  end
26
29
 
27
- it "should allow named authentication" do
30
+ it 'should allow named authentication' do
28
31
  expect { client.login('someone', 'password') }.to_not raise_error
29
32
  end
30
33
 
31
- it "should allow client to quit" do
34
+ it 'should allow client to quit' do
32
35
  expect { client.login('someone', 'password') }.to_not raise_error
33
36
  expect { client.quit }.to_not raise_error
34
37
  end
35
38
 
36
- it "should allow mtime" do
37
- filename = 'someone'
39
+ it 'should allow mtime' do
40
+ filename = '/pub/someone'
38
41
  time = Time.now
39
- server.add_file(filename, "some data", time)
42
+ server.add_file(filename, 'some data', time)
40
43
 
41
44
  client.passive = false
42
45
  mtime = client.mtime(filename)
@@ -47,35 +50,42 @@ describe FakeFtp::Server, 'with ftp client' do
47
50
  expect(mtime.to_s).to eql(time.to_s)
48
51
  end
49
52
 
50
- it "should put files using PASV" do
53
+ it 'should put files using PASV' do
51
54
  expect(File.stat(text_filename).size).to eql(20)
52
55
 
53
56
  client.passive = true
54
57
  expect { client.put(text_filename) }.to_not raise_error
55
58
 
56
- expect(server.files).to include('text_file.txt')
57
- expect(server.file('text_file.txt').bytes).to eql(20)
58
- expect(server.file('text_file.txt')).to be_passive
59
- expect(server.file('text_file.txt')).to_not be_active
59
+ expect(server.files).to include('/pub/text_file.txt')
60
+ expect(server.file('/pub/text_file.txt').bytes).to eql(20)
61
+ expect(server.file('/pub/text_file.txt')).to be_passive
62
+ expect(server.file('/pub/text_file.txt')).to_not be_active
60
63
  end
61
64
 
62
- it "should put files using active" do
65
+ it 'should put files using active' do
63
66
  expect(File.stat(text_filename).size).to eql(20)
64
67
 
65
68
  client.passive = false
66
69
  expect { client.put(text_filename) }.to_not raise_error
67
70
 
68
- expect(server.files).to include('text_file.txt')
69
- expect(server.file('text_file.txt').bytes).to eql(20)
70
- expect(server.file('text_file.txt')).to_not be_passive
71
- expect(server.file('text_file.txt')).to be_active
71
+ expect(server.files).to include('/pub/text_file.txt')
72
+ expect(server.file('/pub/text_file.txt').bytes).to eql(20)
73
+ expect(server.file('/pub/text_file.txt')).to_not be_passive
74
+ expect(server.file('/pub/text_file.txt')).to be_active
75
+ end
76
+
77
+ it 'should allow client to execute SITE command' do
78
+ expect { client.site('umask') }.to_not raise_error
72
79
  end
73
80
 
74
- xit "should disconnect clients on close" do
75
- # TODO: when this succeeds, we can care less about manually closing clients
76
- # otherwise we get a CLOSE_WAIT process hanging around that blocks our port
77
- server.stop
78
- expect(client.closed?).to be_true
81
+ it 'should be able to delete files added using put' do
82
+ expect(File.stat(text_filename).size).to eql(20)
83
+
84
+ client.passive = false
85
+ expect { client.put(text_filename) }.to_not raise_error
86
+ expect(server.files).to include('/pub/text_file.txt')
87
+ expect { client.delete(text_filename) }.to_not raise_error
88
+ expect(server.files).to_not include('/pub/text_file.txt')
79
89
  end
80
90
  end
81
91
  end
@@ -1,4 +1,4 @@
1
- require "spec_helper.rb"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe FakeFtp::File do
4
4
  context 'attributes' do
@@ -6,59 +6,60 @@ describe FakeFtp::File do
6
6
  @file = FakeFtp::File.new
7
7
  end
8
8
 
9
- it "has a name attribute" do
10
- @file.name = "some name"
11
- expect(@file.name).to eql("some name")
9
+ it 'has a name attribute' do
10
+ @file.name = 'some name'
11
+ expect(@file.name).to eql('some name')
12
12
  end
13
13
 
14
- it "has a last_modified_time attribute" do
14
+ it 'has a last_modified_time attribute' do
15
15
  now = Time.now
16
16
  @file.last_modified_time = now
17
17
  expect(@file.last_modified_time).to eql(now)
18
18
  end
19
19
 
20
- it "has a bytes attribute" do
20
+ it 'has a bytes attribute' do
21
21
  @file.bytes = 87
22
22
  expect(@file.bytes).to eql(87)
23
23
  end
24
24
 
25
- it "has a data attribute" do
25
+ it 'has a data attribute' do
26
26
  @file.data = 'some data'
27
27
  expect(@file.data).to eql('some data')
28
- expect(@file.bytes).to eql('some data'.length)
28
+ expect(@file.bytes).to eql(9)
29
29
  end
30
30
  end
31
31
 
32
32
  context 'setup' do
33
- it "can be initialized without attributes" do
33
+ it 'can be initialized without attributes' do
34
34
  file = FakeFtp::File.new
35
35
  expect(file.name).to be_nil
36
36
  expect(file.bytes).to be_nil
37
37
  expect(file.instance_variable_get(:@type)).to be_nil
38
38
  end
39
39
 
40
- it "can be initialized with name" do
40
+ it 'can be initialized with name' do
41
41
  file = FakeFtp::File.new('filename')
42
42
  expect(file.name).to eql('filename')
43
43
  expect(file.bytes).to be_nil
44
44
  expect(file.instance_variable_get(:@type)).to be_nil
45
45
  end
46
46
 
47
- it "can be initialized with name and bytes" do
47
+ it 'can be initialized with name and bytes' do
48
48
  file = FakeFtp::File.new('filename', 104)
49
49
  expect(file.name).to eql('filename')
50
50
  expect(file.bytes).to eql(104)
51
51
  expect(file.instance_variable_get(:@type)).to be_nil
52
52
  end
53
53
 
54
- it "can be initialized with name and bytes and type" do
54
+ it 'can be initialized with name and bytes and type' do
55
55
  file = FakeFtp::File.new('filename', 104, :passive)
56
56
  expect(file.name).to eql('filename')
57
57
  expect(file.bytes).to eql(104)
58
58
  expect(file.instance_variable_get(:@type)).to eql(:passive)
59
59
  end
60
60
 
61
- it "can be initialized with name and bytes and type and last_modified_time" do
61
+ it 'can be initialized with name and bytes and type ' \
62
+ 'and last_modified_time' do
62
63
  time = Time.now
63
64
  file = FakeFtp::File.new('filename', 104, :passive, time)
64
65
  expect(file.name).to eql('filename')
@@ -73,14 +74,14 @@ describe FakeFtp::File do
73
74
  @file = FakeFtp::File.new
74
75
  end
75
76
 
76
- it "should be true if type is :passive" do
77
+ it 'should be true if type is :passive' do
77
78
  @file.type = :passive
78
- expect(@file.passive?).to be_true
79
+ expect(@file.passive?).to be true
79
80
  end
80
81
 
81
- it "should be false if type is :active" do
82
+ it 'should be false if type is :active' do
82
83
  @file.type = :active
83
- expect(@file.passive?).to be_false
84
+ expect(@file.passive?).to be false
84
85
  end
85
86
  end
86
87
 
@@ -89,14 +90,14 @@ describe FakeFtp::File do
89
90
  @file = FakeFtp::File.new
90
91
  end
91
92
 
92
- it "should be true if type is :active" do
93
+ it 'should be true if type is :active' do
93
94
  @file.type = :active
94
- expect(@file.active?).to be_true
95
+ expect(@file.active?).to be true
95
96
  end
96
97
 
97
- it "should be false if type is :passive" do
98
+ it 'should be false if type is :passive' do
98
99
  @file.type = :passive
99
- expect(@file.active?).to be_false
100
+ expect(@file.active?).to be false
100
101
  end
101
102
  end
102
103
  end
@@ -1,76 +1,77 @@
1
- require "spec_helper.rb"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe FakeFtp::Server, 'setup' do
4
- it "starts a server on port n" do
5
- server = FakeFtp::Server.new(21212)
6
- expect(server.port).to eql(21212)
4
+ it 'starts a server on port n' do
5
+ server = FakeFtp::Server.new(21_212)
6
+ expect(server.port).to eql(21_212)
7
7
  end
8
8
 
9
- it "should defaults to port 21" do
9
+ it 'should defaults to port 21' do
10
10
  server = FakeFtp::Server.new
11
11
  expect(server.port).to eql(21)
12
12
  end
13
13
 
14
- it "starts a passive server on port p" do
15
- server = FakeFtp::Server.new(21212, 21213)
16
- expect(server.passive_port).to eql(21213)
14
+ it 'starts a passive server on port p' do
15
+ server = FakeFtp::Server.new(21_212, 21_213)
16
+ expect(server.passive_port).to eql(21_213)
17
17
  end
18
18
 
19
- it "should start and stop" do
20
- server = FakeFtp::Server.new(21212)
21
- expect(server.is_running?).to be_false
19
+ it 'should start and stop' do
20
+ server = FakeFtp::Server.new(21_212)
21
+ expect(server.running?).to be false
22
22
  server.start
23
- expect(server.is_running?).to be_true
23
+ expect(server.running?).to be true
24
24
  server.stop
25
- expect(server.is_running?).to be_false
25
+ expect(server.running?).to be false
26
26
  end
27
27
 
28
- it "should default :mode to :active" do
29
- server = FakeFtp::Server.new(21212, 21213)
28
+ it 'should default :mode to :active' do
29
+ server = FakeFtp::Server.new(21_212, 21_213)
30
30
  expect(server.mode).to eql(:active)
31
31
  end
32
32
 
33
- it "should start and stop passive port" do
34
- server = FakeFtp::Server.new(21212, 21213)
35
- expect(server.is_running?(21213)).to be_false
33
+ it 'should start and stop passive port' do
34
+ server = FakeFtp::Server.new(21_212, 21_213)
35
+ expect(server.running?(21_213)).to be false
36
36
  server.start
37
- expect(server.is_running?(21213)).to be_true
37
+ expect(server.running?(21_213)).to be true
38
38
  server.stop
39
- expect(server.is_running?(21213)).to be_false
39
+ expect(server.running?(21_213)).to be false
40
40
  end
41
41
 
42
- it "should raise if attempting to use a bound port" do
43
- server = FakeFtp::Server.new(21212)
42
+ it 'should raise if attempting to use a bound port' do
43
+ server = FakeFtp::Server.new(21_212)
44
44
  server.start
45
- expect { FakeFtp::Server.new(21212) }.to raise_error(Errno::EADDRINUSE, "Address already in use - 21212")
45
+ expect { FakeFtp::Server.new(21_212) }
46
+ .to raise_error(Errno::EADDRINUSE, 'Address already in use - 21212')
46
47
  server.stop
47
48
  end
48
49
 
49
- it "should raise if attempting to use a bound passive_port" do
50
- server = FakeFtp::Server.new(21212, 21213)
50
+ it 'should raise if attempting to use a bound passive_port' do
51
+ server = FakeFtp::Server.new(21_212, 21_213)
51
52
  server.start
52
- expect { FakeFtp::Server.new(21214, 21213) }.to raise_error(Errno::EADDRINUSE, "Address already in use - 21213")
53
+ expect { FakeFtp::Server.new(21_214, 21_213) }
54
+ .to raise_error(Errno::EADDRINUSE, 'Address already in use - 21213')
53
55
  server.stop
54
56
  end
55
57
  end
56
58
 
57
59
  describe FakeFtp::Server, 'files' do
58
60
  let(:file) { FakeFtp::File.new('filename', 34) }
59
- let(:server) { FakeFtp::Server.new(21212) }
61
+ let(:server) { FakeFtp::Server.new(21_212) }
60
62
 
61
- before { server.instance_variable_set(:@files, [file]) }
63
+ before { server.instance_variable_set(:@store, '/pub/filename' => file) }
62
64
 
63
- it "returns filenames from :files" do
65
+ it 'returns filenames from :files' do
64
66
  expect(server.files).to include('filename')
65
67
  end
66
68
 
67
- it "can be accessed with :file" do
69
+ it 'can be accessed with :file' do
68
70
  expect(server.file('filename')).to eql(file)
69
71
  end
70
72
 
71
- it "can reset files" do
73
+ it 'can reset files' do
72
74
  server.reset
73
75
  expect(server.files).to eql([])
74
76
  end
75
77
  end
76
-
@@ -1,18 +1,102 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
3
- $LOAD_PATH.unshift(File.dirname(__FILE__), '**','*')
1
+ # frozen_string_literal: true
4
2
 
5
- require 'rubygems'
6
- require 'bundler/setup'
3
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
7
4
 
5
+ require 'simplecov'
8
6
  require 'rspec'
9
- require 'fake_ftp' # and any other gems you need
10
-
11
- RSpec.configure do |config|
12
-
13
- # Run specs in random order to surface order dependencies. If you find an
14
- # order dependency and want to debug it, you can fix the order by providing
15
- # the seed, which is printed after each run.
16
- # --seed 1234
17
- config.order = 'random'
7
+ require 'fake_ftp'
8
+
9
+ RSpec.configure do |c|
10
+ c.filter_run_excluding(
11
+ functional: ENV['FUNCTIONAL_SPECS'] != '1',
12
+ integration: ENV['INTEGRATION_SPECS'] != '1'
13
+ )
14
+ end
15
+
16
+ module SpecHelper
17
+ def gets_with_timeout(client, timeout: 5, endwith: "\r\n", chunk: 1024)
18
+ outer_caller = caller(0..1).last.to_s
19
+ start = Time.now
20
+ buf = ''
21
+ loop do
22
+ if Time.now - start >= timeout
23
+ raise Timeout::Error, "client=#{client} timeout=#{timeout}s " \
24
+ "buf=#{buf.inspect} caller=#{outer_caller.inspect}"
25
+ end
26
+ bytes = client.read_nonblock(chunk, exception: false)
27
+ return buf if bytes.nil?
28
+ buf += bytes unless bytes == :wait_readable
29
+ return buf if buf.end_with?(endwith)
30
+ end
31
+ buf
32
+ end
33
+
34
+ module_function :gets_with_timeout
35
+
36
+ def local_addr_bits(port)
37
+ [
38
+ 127, 0, 0, 1,
39
+ port / 256,
40
+ port % 256
41
+ ].map(&:to_s).join(',')
42
+ end
43
+
44
+ module_function :local_addr_bits
45
+
46
+ def statline(ftp_file)
47
+ %W[
48
+ -rw-r--r--
49
+ 1
50
+ owner
51
+ group
52
+ 10
53
+ #{ftp_file.created.strftime('%b %d %H:%M')}
54
+ #{ftp_file.name}
55
+ ].join("\t")
56
+ end
57
+
58
+ module_function :statline
59
+
60
+ class FakeDataServer
61
+ def initialize(port)
62
+ @port = port
63
+ end
64
+
65
+ attr_reader :port
66
+
67
+ def addr_bits
68
+ ::SpecHelper.local_addr_bits(port)
69
+ end
70
+
71
+ def start
72
+ server
73
+ end
74
+
75
+ def stop
76
+ server.close
77
+ end
78
+
79
+ def handler_sock
80
+ @handler_sock ||= wait_for_handler_sock
81
+ end
82
+
83
+ private
84
+
85
+ def wait_for_handler_sock
86
+ sock = nil
87
+
88
+ while sock.nil? || sock == :wait_readable
89
+ sleep 0.01
90
+ sock = server.accept_nonblock(exception: false)
91
+ end
92
+
93
+ sock
94
+ end
95
+
96
+ def server
97
+ @server ||= TCPServer.new('127.0.0.1', port).tap do |srv|
98
+ srv.sync = true
99
+ end
100
+ end
101
+ end
18
102
  end