1 # -*- encoding: binary -*-
7 require 'mogilefs/backend'
9 class MogileFS::Backend
12 attr_reader :timeout, :dead
13 attr_writer :lasterr, :lasterrstr, :socket
17 class TestBackend < Test::Unit::TestCase
20 @backend = MogileFS::Backend.new :hosts => ['localhost:1']
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
39 srv = TCPServer.new("127.0.0.1", 0)
41 accepted = Thread.new do
43 client.write("OK 1 you=win\r\n")
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)
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')
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')
74 assert_equal "go! fight=team+fight%21\r\n",
75 @backend.make_request('go!', { 'fight' => 'team fight!' })
78 def test_parse_response
79 assert_equal({'foo' => 'bar', 'baz' => 'hoge'},
80 @backend.parse_response("OK 1 foo=bar&baz=hoge\r\n"))
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
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'
97 assert_raises MogileFS::InvalidResponseError do
98 @backend.parse_response("OK 1 foo=bar&baz=hoge")
102 def test_parse_response_newline
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
110 assert_equal 'you', @backend.lasterr
111 assert_equal 'totally suck', @backend.lasterrstr
114 def test_readable_eh_not_readable
115 srv = TCPServer.new("127.0.0.1", 0)
117 @backend = MogileFS::Backend.new(:hosts => [ "127.0.0.1:#{port}" ],
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}"
126 flunk "MogileFS::UnreadableSocketError not raised"
131 assert_equal({}, @backend.dead)
132 assert_raises(MogileFS::UnreachableBackendError) do
133 @backend.do_request('test', {})
135 assert_equal(['localhost:1'], @backend.dead.keys)
138 def test_socket_robust_on_dead_server
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)
148 client.write("OK 1 foo=bar\r\n")
152 assert_nothing_raised do
153 rv = @backend.do_request('test', { "all" => "ALL" })
156 assert_equal "test all=ALL\r\n", accepted.readpartial(666)
157 assert_equal({"foo"=>"bar"}, rv)
162 srv = TCPServer.new('127.0.0.1', 0)
164 @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{port}" ]
165 assert @backend.socket
166 assert ! @backend.socket.closed?
169 resp = @backend.socket.read(1)
171 assert_equal nil, @backend.instance_variable_get(:@socket)
172 assert_equal 1, resp.to_i
176 assert_equal({"\272z" => "\360opy", "f\000" => "\272r"},
177 @backend.url_decode("%baz=%f0opy&f%00=%bar"))
178 assert_equal({}, @backend.url_decode(''))
182 params = [["f\000", "\272r"], ["\272z", "\360opy"]]
183 assert_equal "f%00=%bar&%baz=%f0opy", @backend.url_encode(params)
186 def test_url_escape # \n for unit_diff
187 actual = (0..255).map { |c| @backend.url_escape c.chr }.join "\n"
190 expected.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
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
206 def test_url_unescape
208 input.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
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