http_file: remove unnecessary check
[ruby-mogilefs-client.git] / test / test_backend.rb
blob6211b34dd6f6f6a08749ead95318a05618d30e2f
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require './test/setup'
5 $TESTING = true
7 require 'mogilefs/backend'
9 class MogileFS::Backend
11   attr_accessor :hosts
12   attr_reader :timeout, :dead
13   attr_writer :lasterr, :lasterrstr, :socket
15 end
17 class TestBackend < Test::Unit::TestCase
19   def setup
20     @backend = MogileFS::Backend.new :hosts => ['localhost:1']
21   end
23   def test_initialize
24     assert_raises ArgumentError do MogileFS::Backend.new end
25     assert_raises ArgumentError do MogileFS::Backend.new :hosts => [] end
26     assert_raises ArgumentError do MogileFS::Backend.new :hosts => [''] end
28     assert_equal ['localhost:1'], @backend.hosts
29     assert_equal 3, @backend.timeout
30     assert_equal nil, @backend.lasterr
31     assert_equal nil, @backend.lasterrstr
32     assert_equal({}, @backend.dead)
34     @backend = MogileFS::Backend.new :hosts => ['localhost:6001'], :timeout => 1
35     assert_equal 1, @backend.timeout
36   end
38   def test_do_request
39     srv = TCPServer.new("127.0.0.1", 0)
40     port = srv.addr[1]
41     accepted = Thread.new do
42       client = srv.accept
43       client.write("OK 1 you=win\r\n")
44       client
45     end
46     @backend.hosts = [ "127.0.0.1:#{port}" ]
47     assert_equal({'you' => 'win'},
48                  @backend.do_request('go!', { 'fight' => 'team fight!' }))
49     accepted = accepted.value
50     assert_equal "go! fight=team+fight%21\r\n", accepted.readpartial(4096)
51   end
53   def test_automatic_exception
54     assert ! MogileFS::Backend.const_defined?('PebkacError')
55     assert @backend.error('pebkac')
56     assert_equal MogileFS::Error, @backend.error('PebkacError').superclass
57     assert MogileFS::Backend.const_defined?('PebkacError')
59     assert ! MogileFS::Backend.const_defined?('PebKacError')
60     assert @backend.error('peb_kac')
61     assert_equal MogileFS::Error, @backend.error('PebKacError').superclass
62     assert MogileFS::Backend.const_defined?('PebKacError')
63   end
65   def test_size_verify_error_defined
66     # "ErrorError" just looks dumb, but we used to get it
67     # since mogilefs would send us "size_verify_error" and we'd
68     # blindly append "Error" to the exception
69     assert ! MogileFS::Backend.const_defined?('SizeVerifyErrorError')
70     assert MogileFS::Backend.const_defined?('SizeVerifyError')
71   end
73   def test_make_request
74     assert_equal "go! fight=team+fight%21\r\n",
75                  @backend.make_request('go!', { 'fight' => 'team fight!' })
76   end
78   def test_parse_response
79     assert_equal({'foo' => 'bar', 'baz' => 'hoge'},
80                  @backend.parse_response("OK 1 foo=bar&baz=hoge\r\n"))
82     err = nil
83     begin
84       @backend.parse_response("ERR you totally suck\r\n")
85     rescue MogileFS::Error => err
86       assert_equal 'MogileFS::Backend::YouError', err.class.to_s
87       assert_equal 'totally suck', err.message
88     end
89     assert_equal 'MogileFS::Backend::YouError', err.class.to_s
91     assert_equal 'you', @backend.lasterr
92     assert_equal 'totally suck', @backend.lasterrstr
94     assert_raises MogileFS::InvalidResponseError do
95       @backend.parse_response 'garbage'
96     end
97     assert_raises MogileFS::InvalidResponseError do
98       @backend.parse_response("OK 1 foo=bar&baz=hoge")
99     end
100   end
102   def test_parse_response_newline
103     begin
104       @backend.parse_response("ERR you totally suck\r\n")
105     rescue MogileFS::Error => err
106       assert_equal 'MogileFS::Backend::YouError', err.class.to_s
107       assert_equal 'totally suck', err.message
108     end
110     assert_equal 'you', @backend.lasterr
111     assert_equal 'totally suck', @backend.lasterrstr
112   end
114   def test_readable_eh_not_readable
115     srv = TCPServer.new("127.0.0.1", 0)
116     port = srv.addr[1]
117     @backend = MogileFS::Backend.new(:hosts => [ "127.0.0.1:#{port}" ],
118                                      :timeout => 0.5)
119     begin
120       @backend.do_request 'foo', {}
121     rescue MogileFS::UnreadableSocketError => e
122       assert_equal "127.0.0.1:#{port} never became readable", e.message
123     rescue Exception => err
124       flunk "MogileFS::UnreadableSocketError not raised #{err} #{err.backtrace}"
125     else
126       flunk "MogileFS::UnreadableSocketError not raised"
127     end
128   end
130   def test_socket_dead
131     assert_equal({}, @backend.dead)
132     assert_raises(MogileFS::UnreachableBackendError) do
133       @backend.do_request('test', {})
134     end
135     assert_equal(['localhost:1'], @backend.dead.keys)
136   end
138   def test_socket_robust_on_dead_server
139     10.times do
140       t1 = TCPServer.new("127.0.0.1", 0)
141       t2 = TCPServer.new("127.0.0.1", 0)
142       hosts = ["127.0.0.1:#{t1.addr[1]}", "127.0.0.1:#{t2.addr[1]}"]
143       @backend = MogileFS::Backend.new(:hosts => hosts.dup)
144       assert_equal({}, @backend.dead)
145       t1.close
146       thr = Thread.new do
147         client = t2.accept
148         client.write("OK 1 foo=bar\r\n")
149         client
150       end
151       rv = nil
152       assert_nothing_raised do
153         rv = @backend.do_request('test', { "all" => "ALL" })
154       end
155       accepted = thr.value
156       assert_equal "test all=ALL\r\n", accepted.readpartial(666)
157       assert_equal({"foo"=>"bar"}, rv)
158     end
159   end
161   def test_shutdown
162     srv = TCPServer.new('127.0.0.1', 0)
163     port = srv.addr[1]
164     @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{port}" ]
165     assert @backend.socket
166     assert ! @backend.socket.closed?
167     client = srv.accept
168     client.write '1'
169     resp = @backend.socket.read(1)
170     @backend.shutdown
171     assert_equal nil, @backend.instance_variable_get(:@socket)
172     assert_equal 1, resp.to_i
173   end
175   def test_url_decode
176     assert_equal({"\272z" => "\360opy", "f\000" => "\272r"},
177                  @backend.url_decode("%baz=%f0opy&f%00=%bar"))
178     assert_equal({}, @backend.url_decode(''))
179   end
181   def test_url_encode
182     params = [["f\000", "\272r"], ["\272z", "\360opy"]]
183     assert_equal "f%00=%bar&%baz=%f0opy", @backend.url_encode(params)
184   end
186   def test_url_escape # \n for unit_diff
187     actual = (0..255).map { |c| @backend.url_escape c.chr }.join "\n"
189     expected = []
190     expected.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
191     expected << '+'
192     expected.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
193     expected.push(*%w[, - . /])
194     expected.push(*('0'..'9'))
195     expected.push(*%w[: %3b %3c %3d %3e %3f %40])
196     expected.push(*('A'..'Z'))
197     expected.push(*%w[%5b \\ %5d %5e _ %60])
198     expected.push(*('a'..'z'))
199     expected.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
201     expected = expected.join "\n"
203     assert_equal expected, actual
204   end
206   def test_url_unescape
207     input = []
208     input.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
209     input << '+'
210     input.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
211     input.push(*%w[, - . /])
212     input.push(*('0'..'9'))
213     input.push(*%w[: %3b %3c %3d %3e %3f %40])
214     input.push(*('A'..'Z'))
215     input.push(*%w[%5b \\ %5d %5e _ %60])
216     input.push(*('a'..'z'))
217     input.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
219     actual = input.map { |c| @backend.url_unescape c }.join "\n"
221     expected = (0..255).map { |c| c.chr }.join "\n"
222     expected.sub! '+', ' '
224     assert_equal expected, actual
225   end