1 # -*- encoding: binary -*-
7 class MogileFS::Backend
10 attr_reader :timeout, :dead
11 attr_writer :lasterr, :lasterrstr, :socket
15 class TestBackend < Test::Unit::TestCase
18 @backend = MogileFS::Backend.new :hosts => ['localhost:1']
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
37 srv = TCPServer.new("127.0.0.1", 0)
39 accepted = Thread.new do
41 client.write("OK 1 you=win\r\n")
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)
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_equal MogileFS::Error, MogileFS::Backend::OMFGWTFBBQError.superclass
63 assert_raises(NameError) do
64 MogileFS::Backend::FailFailFail
69 assert_equal "go! fight=team+fight%21\r\n",
70 @backend.make_request('go!', { 'fight' => 'team fight!' })
73 def test_parse_response
74 assert_equal({'foo' => 'bar', 'baz' => 'hoge'},
75 @backend.parse_response("OK 1 foo=bar&baz=hoge\r\n"))
79 @backend.parse_response("ERR you totally suck\r\n")
80 rescue MogileFS::Error => err
81 assert_equal 'MogileFS::Backend::YouError', err.class.to_s
82 assert_equal 'totally suck', err.message
84 assert_equal 'MogileFS::Backend::YouError', err.class.to_s
86 assert_equal 'you', @backend.lasterr
87 assert_equal 'totally suck', @backend.lasterrstr
89 assert_raises MogileFS::InvalidResponseError do
90 @backend.parse_response 'garbage'
92 assert_raises MogileFS::InvalidResponseError do
93 @backend.parse_response("OK 1 foo=bar&baz=hoge")
97 def test_parse_response_newline
99 @backend.parse_response("ERR you totally suck\r\n")
100 rescue MogileFS::Error => err
101 assert_equal 'MogileFS::Backend::YouError', err.class.to_s
102 assert_equal 'totally suck', err.message
105 assert_equal 'you', @backend.lasterr
106 assert_equal 'totally suck', @backend.lasterrstr
109 def test_readable_eh_not_readable
110 srv = TCPServer.new("127.0.0.1", 0)
112 @backend = MogileFS::Backend.new(:hosts => [ "127.0.0.1:#{port}" ],
115 @backend.do_request 'foo', {}
116 rescue MogileFS::UnreadableSocketError => e
117 assert_match(/127\.0\.0\.1:#{port} never became readable/, e.message)
118 rescue Exception => err
119 flunk "MogileFS::UnreadableSocketError not raised #{err} #{err.backtrace}"
121 flunk "MogileFS::UnreadableSocketError not raised"
126 assert_equal({}, @backend.dead)
127 assert_raises(MogileFS::UnreachableBackendError) do
128 @backend.do_request('test', {})
130 assert_equal(['localhost:1'], @backend.dead.keys)
133 def test_socket_robust_on_dead_server
135 t1 = TCPServer.new("127.0.0.1", 0)
136 t2 = TCPServer.new("127.0.0.1", 0)
137 hosts = ["127.0.0.1:#{t1.addr[1]}", "127.0.0.1:#{t2.addr[1]}"]
138 @backend = MogileFS::Backend.new(:hosts => hosts.dup)
139 assert_equal({}, @backend.dead)
143 client.write("OK 1 foo=bar\r\n")
146 rv = @backend.do_request('test', { "all" => "ALL" })
148 assert_equal "test all=ALL\r\n", accepted.readpartial(666)
149 assert_equal({"foo"=>"bar"}, rv)
154 srv = TCPServer.new('127.0.0.1', 0)
156 @backend = MogileFS::Backend.new :hosts => [ "127.0.0.1:#{port}" ]
157 assert @backend.socket
158 assert ! @backend.socket.closed?
161 resp = @backend.socket.read(1)
163 assert_equal nil, @backend.instance_variable_get(:@socket)
164 assert_equal 1, resp.to_i
168 assert_equal({"\272z" => "\360opy", "f\000" => "\272r"},
169 @backend.url_decode("%baz=%f0opy&f%00=%bar"))
170 assert_equal({}, @backend.url_decode(''))
174 params = [["f\000", "\272r"], ["\272z", "\360opy"]]
175 assert_equal "f%00=%bar&%baz=%f0opy", @backend.url_encode(params)
178 def test_url_escape # \n for unit_diff
179 actual = (0..255).map { |c| @backend.url_escape c.chr }.join "\n"
182 expected.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
184 expected.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
185 expected.push(*%w[, - . /])
186 expected.push(*('0'..'9'))
187 expected.push(*%w[: %3b %3c %3d %3e %3f %40])
188 expected.push(*('A'..'Z'))
189 expected.push(*%w[%5b \\ %5d %5e _ %60])
190 expected.push(*('a'..'z'))
191 expected.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
193 expected = expected.join "\n"
195 assert_equal expected, actual
198 def test_url_unescape
200 input.push(*(0..0x1f).map { |c| "%%%0.2x" % c })
202 input.push(*(0x21..0x2b).map { |c| "%%%0.2x" % c })
203 input.push(*%w[, - . /])
204 input.push(*('0'..'9'))
205 input.push(*%w[: %3b %3c %3d %3e %3f %40])
206 input.push(*('A'..'Z'))
207 input.push(*%w[%5b \\ %5d %5e _ %60])
208 input.push(*('a'..'z'))
209 input.push(*(0x7b..0xff).map { |c| "%%%0.2x" % c })
211 actual = input.map { |c| @backend.url_unescape c }.join "\n"
213 expected = (0..255).map { |c| c.chr }.join "\n"
215 assert_equal expected, actual
218 def test_fail_timeout
219 o = { :domain => "none", :hosts => %w(0:666 0:6 0:66) }
220 c = MogileFS::MogileFS.new(o)
221 assert_equal 5, c.backend.instance_variable_get(:@fail_timeout)
222 o[:fail_timeout] = 0.666
223 c = MogileFS::MogileFS.new(o)
224 assert_equal 0.666, c.backend.instance_variable_get(:@fail_timeout)