fake_ftp 0.2.0 → 0.3.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.
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