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