fluent-plugin-hoop 0.1.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.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'fluent/test'
16
+
17
+ if ENV['FLUENT_TEST_DEBUG'] == 'TRUE'
18
+ nulllogger = Object.new
19
+ nulllogger.instance_eval {|obj|
20
+ def method_missing(method, *args)
21
+ # pass
22
+ end
23
+ }
24
+ $log = nulllogger
25
+ end
26
+
27
+ require 'fluent/plugin/out_hoop'
28
+
29
+ class Test::Unit::TestCase
30
+ end
31
+
32
+ require 'webrick'
33
+
34
+ # to handle PUT/DELETE ...
35
+ module WEBrick::HTTPServlet
36
+ class ProcHandler < AbstractServlet
37
+ alias do_PUT do_GET
38
+ alias do_DELETE do_GET
39
+ end
40
+ end
41
+
42
+ def get_code(server, port, path, headers)
43
+ require 'net/http'
44
+ Net::HTTP.start(server, port){|http|
45
+ http.get(path, headers).code
46
+ }
47
+ end
48
+ def get_content(server, port, path, headers)
49
+ require 'net/http'
50
+ Net::HTTP.start(server, port){|http|
51
+ http.get(path, headers).body
52
+ }
53
+ end
@@ -0,0 +1,441 @@
1
+ require 'helper'
2
+
3
+ class HoopOutputTest < Test::Unit::TestCase
4
+ # setup/teardown and tests of dummy hoop server defined at the end of this class...
5
+
6
+ CONFIG = %[
7
+ hoop_server localhost:14000
8
+ path /logs/from/fluentd/foo-%Y%m%d
9
+ username hoopuser
10
+ ]
11
+
12
+ def create_driver(conf=CONFIG, tag='test')
13
+ Fluent::Test::TimeSlicedOutputTestDriver.new(Fluent::HoopOutput, tag).configure(conf)
14
+ end
15
+
16
+ def test_configure
17
+ assert_raise(Fluent::ConfigError) {
18
+ d = create_driver %[
19
+ ]
20
+ }
21
+ assert_raise(Fluent::ConfigError) {
22
+ d = create_driver %[
23
+ path /logs/from/fluentd/foo-%Y%m%d%H
24
+ username hoopuser
25
+ ]
26
+ }
27
+ assert_raise(Fluent::ConfigError) {
28
+ d = create_driver %[
29
+ hoop_server hoop.master.local:14000
30
+ username hoopuser
31
+ ]
32
+ }
33
+ assert_raise(Fluent::ConfigError) {
34
+ d = create_driver %[
35
+ hoop_server hoop.master.local:14000
36
+ path /logs/from/fluentd/foo-%Y%m%d%H
37
+ ]
38
+ }
39
+ assert_raise(Fluent::ConfigError) {
40
+ d = create_driver %[
41
+ hoop_server hoop.master.local
42
+ path /logs/from/fluentd/foo-%Y%m%d%H
43
+ username hoopuser
44
+ ]
45
+ }
46
+ assert_raise(Fluent::ConfigError) {
47
+ d = create_driver %[
48
+ hoop_server hoop.master.local:xxx
49
+ path /logs/from/fluentd/foo-%Y%m%d%H
50
+ username hoopuser
51
+ ]
52
+ }
53
+ assert_raise(Fluent::ConfigError) {
54
+ d = create_driver %[
55
+ hoop_server hoop.master.local:xxx
56
+ path logs/from/fluentd/foo-%Y%m%d%H
57
+ username hoopuser
58
+ ]
59
+ }
60
+ assert_raise(Fluent::ConfigError) {
61
+ d = create_driver %[
62
+ hoop_server hoop.master.local:14000
63
+ path /logs/from/fluentd/access.log.%Y%m%d
64
+ output_include_tag true
65
+ remove_prefix testing
66
+ ]
67
+ }
68
+
69
+ # config_param :path, :string # /path/pattern/to/hdfs/file can use %Y %m %d %H %M %S and %T(tag, not-supported-yet)
70
+
71
+ d = create_driver(CONFIG)
72
+
73
+ assert_equal '%Y%m%d', d.instance.time_slice_format
74
+
75
+ assert_equal 'localhost:14000', d.instance.hoop_server
76
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d', d.instance.path
77
+ assert_equal 'hoopuser', d.instance.username
78
+
79
+ assert_equal true, d.instance.output_include_time
80
+ assert_equal true, d.instance.output_include_tag
81
+ assert_equal 'json', d.instance.output_data_type
82
+ assert_equal true, d.instance.add_newline
83
+ assert_equal "\t", d.instance.field_separator
84
+
85
+ assert_nil d.instance.remove_prefix
86
+
87
+ d = create_driver(CONFIG + %[
88
+ add_newline false
89
+ remove_prefix testing
90
+ default_tag unknown
91
+ ], 'testing.error')
92
+ assert_equal 'testing.error', d.tag
93
+
94
+ assert_equal '%Y%m%d', d.instance.time_slice_format
95
+
96
+ assert_equal 'localhost:14000', d.instance.hoop_server
97
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d', d.instance.path
98
+ assert_equal 'hoopuser', d.instance.username
99
+
100
+ assert_equal true, d.instance.output_include_time
101
+ assert_equal true, d.instance.output_include_tag
102
+ assert_equal 'json', d.instance.output_data_type
103
+ assert_equal false, d.instance.add_newline
104
+ assert_equal "\t", d.instance.field_separator
105
+
106
+ assert_equal 'testing', d.instance.remove_prefix
107
+ assert_equal 'unknown', d.instance.default_tag
108
+ end
109
+
110
+ def test_configure_path_and_slice_format
111
+ d = create_driver(CONFIG)
112
+ assert_equal '%Y%m%d', d.instance.time_slice_format
113
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d', d.instance.path
114
+ assert_equal '/logs/from/fluentd/foo-20111125', d.instance.path_format('20111125')
115
+
116
+ d = create_driver CONFIG + %[
117
+ path /logs/from/fluentd/foo-%Y%m
118
+ ]
119
+ assert_equal '%Y%m%d', d.instance.time_slice_format
120
+ assert_equal '/logs/from/fluentd/foo-%Y%m', d.instance.path
121
+ assert_equal '/logs/from/fluentd/foo-201111', d.instance.path_format('20111125')
122
+
123
+ d = create_driver CONFIG + %[
124
+ path /logs/from/fluentd/foo-%Y%m%d%H
125
+ ]
126
+ assert_equal '%Y%m%d%H', d.instance.time_slice_format
127
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d%H', d.instance.path
128
+ assert_equal '/logs/from/fluentd/foo-2011112508', d.instance.path_format('2011112508')
129
+
130
+ d = create_driver CONFIG + %[
131
+ path /logs/from/fluentd/foo-%Y%m%d%H%M
132
+ ]
133
+ assert_equal '%Y%m%d%H%M', d.instance.time_slice_format
134
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d%H%M', d.instance.path
135
+ assert_equal '/logs/from/fluentd/foo-201111250811', d.instance.path_format('201111250811')
136
+
137
+ d = create_driver CONFIG + %[
138
+ path /logs/from/fluentd/foo-%Y%m%d%H%M%S
139
+ ]
140
+ assert_equal '%Y%m%d%H%M%S', d.instance.time_slice_format
141
+ assert_equal '/logs/from/fluentd/foo-%Y%m%d%H%M%S', d.instance.path
142
+ assert_equal '/logs/from/fluentd/foo-20111125081159', d.instance.path_format('20111125081159')
143
+
144
+ d = create_driver CONFIG + %[
145
+ path /logs/from/fluentd/foo-%m%d%H
146
+ ]
147
+ assert_equal '%Y%m%d%H', d.instance.time_slice_format
148
+ assert_equal '/logs/from/fluentd/foo-%m%d%H', d.instance.path
149
+ assert_equal '/logs/from/fluentd/foo-112508', d.instance.path_format('2011112508')
150
+
151
+ d = create_driver CONFIG + %[
152
+ path /logs/from/fluentd/foo-%M%S.log
153
+ ]
154
+ assert_equal '%Y%m%d%H%M%S', d.instance.time_slice_format
155
+ assert_equal '/logs/from/fluentd/foo-%M%S.log', d.instance.path
156
+ assert_equal '/logs/from/fluentd/foo-1159.log', d.instance.path_format('20111125081159')
157
+
158
+ d = create_driver CONFIG + %[
159
+ path /logs/from/fluentd/%Y%m%d/%H/foo-%M-%S.log
160
+ ]
161
+ assert_equal '%Y%m%d%H%M%S', d.instance.time_slice_format
162
+ assert_equal '/logs/from/fluentd/%Y%m%d/%H/foo-%M-%S.log', d.instance.path
163
+ assert_equal '/logs/from/fluentd/20111125/08/foo-11-59.log', d.instance.path_format('20111125081159')
164
+ end
165
+
166
+ def test_format
167
+ d = create_driver
168
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
169
+ d.emit({"a"=>1}, time)
170
+ d.emit({"a"=>2}, time)
171
+ d.expect_format %[2011-11-25T13:14:15Z\ttest\t{"a":1}\n]
172
+ d.expect_format %[2011-11-25T13:14:15Z\ttest\t{"a":2}\n]
173
+ d.run
174
+
175
+ d = create_driver(CONFIG + %[
176
+ remove_prefix testing
177
+ default_tag unknown
178
+ ], 'testing.log')
179
+ assert_equal 'testing.log', d.tag
180
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
181
+ d.emit({"a"=>1}, time)
182
+ d.emit({"a"=>2}, time)
183
+ d.expect_format %[2011-11-25T13:14:15Z\tlog\t{"a":1}\n]
184
+ d.expect_format %[2011-11-25T13:14:15Z\tlog\t{"a":2}\n]
185
+ d.run
186
+
187
+ d = create_driver(CONFIG + %[
188
+ remove_prefix testing
189
+ default_tag unknown
190
+ ], 'extra.testing.log')
191
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
192
+ d.emit({"a"=>1}, time)
193
+ d.emit({"a"=>2}, time)
194
+ d.expect_format %[2011-11-25T13:14:15Z\textra.testing.log\t{"a":1}\n]
195
+ d.expect_format %[2011-11-25T13:14:15Z\textra.testing.log\t{"a":2}\n]
196
+ d.run
197
+
198
+ d = create_driver(CONFIG + %[
199
+ remove_prefix testing
200
+ default_tag unknown
201
+ ], 'testing')
202
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
203
+ d.emit({"a"=>1}, time)
204
+ d.emit({"a"=>2}, time)
205
+ d.expect_format %[2011-11-25T13:14:15Z\tunknown\t{"a":1}\n]
206
+ d.expect_format %[2011-11-25T13:14:15Z\tunknown\t{"a":2}\n]
207
+ d.run
208
+
209
+ d = create_driver CONFIG + %[
210
+ output_include_tag false
211
+ ]
212
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
213
+ d.emit({"a"=>1}, time)
214
+ d.emit({"a"=>2}, time)
215
+ d.expect_format %[2011-11-25T13:14:15Z\t{"a":1}\n]
216
+ d.expect_format %[2011-11-25T13:14:15Z\t{"a":2}\n]
217
+ d.run
218
+
219
+ d = create_driver CONFIG + %[
220
+ output_include_time false
221
+ ]
222
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
223
+ d.emit({"a"=>1}, time)
224
+ d.emit({"a"=>2}, time)
225
+ d.expect_format %[test\t{"a":1}\n]
226
+ d.expect_format %[test\t{"a":2}\n]
227
+ d.run
228
+
229
+ d = create_driver CONFIG + %[
230
+ output_include_time false
231
+ output_include_tag false
232
+ ]
233
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
234
+ d.emit({"a"=>1}, time)
235
+ d.emit({"a"=>2}, time)
236
+ d.expect_format %[{"a":1}\n]
237
+ d.expect_format %[{"a":2}\n]
238
+ d.run
239
+
240
+ d = create_driver CONFIG + %[
241
+ output_include_time false
242
+ output_include_tag false
243
+ output_data_type attr:a
244
+ add_newline true # default
245
+ ]
246
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
247
+ d.emit({"a"=>1}, time)
248
+ d.emit({"a"=>2}, time)
249
+ d.expect_format %[1\n]
250
+ d.expect_format %[2\n]
251
+ d.run
252
+
253
+ d = create_driver CONFIG + %[
254
+ output_include_time false
255
+ output_include_tag false
256
+ output_data_type attr:a
257
+ add_newline false
258
+ ]
259
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
260
+ d.emit({"a"=>1}, time)
261
+ d.emit({"a"=>2}, time)
262
+ d.expect_format %[1]
263
+ d.expect_format %[2]
264
+ d.run
265
+
266
+ d = create_driver CONFIG + %[
267
+ output_include_time false
268
+ output_include_tag false
269
+ output_data_type attr:a,b,c
270
+ add_newline true
271
+ ]
272
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
273
+ d.emit({"a"=>1}, time)
274
+ d.emit({"a"=>2,"c"=>6,"b"=>4}, time)
275
+ d.expect_format %[1\tNULL\tNULL\n]
276
+ d.expect_format %[2\t4\t6\n]
277
+ d.run
278
+
279
+ d = create_driver CONFIG + %[
280
+ output_include_time false
281
+ output_include_tag false
282
+ output_data_type attr:message
283
+ add_newline false
284
+ ]
285
+ time = Time.parse("2011-11-25 13:14:15 UTC").to_i
286
+ d.emit({"tag"=>"from.scribe", "message"=>'127.0.0.1 - tagomoris [25/Nov/2011:20:19:04 +0900] "GET http://example.com/api/ HTTP/1.1" 200 39 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2" "-" 71383"' + "\n"}, time)
287
+ d.expect_format '127.0.0.1 - tagomoris [25/Nov/2011:20:19:04 +0900] "GET http://example.com/api/ HTTP/1.1" 200 39 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2" "-" 71383"' + "\n"
288
+ d.run
289
+ end
290
+
291
+ def test_write
292
+ d = create_driver CONFIG + %[
293
+ utc
294
+ ]
295
+
296
+ assert_equal '404', get_code('localhost', 14000, '/logs/from/fluentd/foo-20111124', {'Cookie' => VALID_COOKIE_STRING})
297
+
298
+ time = Time.parse("2011-11-24 00:14:15 UTC").to_i
299
+ d.emit({"a"=>1}, time)
300
+ d.emit({"a"=>2}, time)
301
+ paths = d.run
302
+ assert_equal ['/logs/from/fluentd/foo-20111124'], paths
303
+ assert_equal %[2011-11-24T00:14:15Z\ttest\t{"a":1}\n2011-11-24T00:14:15Z\ttest\t{"a":2}\n], get_content('localhost', 14000, paths.first, {'Cookie' => VALID_COOKIE_STRING})
304
+ end
305
+
306
+ VALID_COOKIE_VALUE = 'u=hoopuser&p=hoopuser&t=simple&e=1322203001386&s=SErpv88rOAVEItSOIoCtIV/DSpE='
307
+ VALID_COOKIE_STRING = 'alfredo.auth="u=hoopuser&p=hoopuser&t=simple&e=1322203001386&s=SErpv88rOAVEItSOIoCtIV/DSpE="'
308
+ RES_COOKIE_AUTH_FAILURE = WEBrick::Cookie.parse_set_cookie('alfredo.auth=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/')
309
+ RES_COOKIE_AUTH_SUCCESS = WEBrick::Cookie.parse_set_cookie(VALID_COOKIE_STRING + '; Version=1; Path=/')
310
+ RES_BODY_STATUS_ROOT = '{"path":"http:\/\/localhost:14000\/","isDir":true,"len":0,"owner":"hoopuser","group":"supergroup","permission":"-rwxr-xr-x","accessTime":0,"modificationTime":1320055230010,"blockSize":0,"replication":0}'
311
+ RES_FORMAT_ALREADY_EXISTS = "{\"statusCode\":500,\"reason\":\"Internal Server Error\",\"message\":\"java.io.IOException: failed to create file %s on client 127.0.0.1 either because the filename is invalid or the file exists\",\"exception\":\"org.apache.hadoop.ipc.RemoteException\"}"
312
+ RES_FORMAT_NOT_FOUND = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"java.io.FileNotFoundException: failed to append to non-existent file %s on client 127.0.0.1\",\"exception\":\"java.io.FileNotFoundException\"}"
313
+ RES_FORMAT_NOT_FOUND_GET = "{\"statusCode\":404,\"reason\":\"Not Found\",\"message\":\"File does not exist: %s\",\"exception\":\"java.io.FileNotFoundException\"}"
314
+
315
+ CONTENT_TYPE_JSON = 'application/json'
316
+
317
+ def setup
318
+ Fluent::Test.setup
319
+ @dummy_server_thread = Thread.new do
320
+ srv = if ENV['FLUENT_TEST_DEBUG']
321
+ logger = WEBrick::Log.new('/dev/null', WEBrick::BasicLog::DEBUG)
322
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000, :Logger => logger, :AccessLog => []})
323
+ else
324
+ WEBrick::HTTPServer.new({:BindAddress => '127.0.0.1', :Port => 14000})
325
+ end
326
+ @fsdata = {}
327
+ begin
328
+ srv.mount_proc('/'){|req,res|
329
+ # status only...
330
+ if req.query['user.name'] or req.cookies.index{|item| item.name == 'alfredo.auth' and item.value}
331
+ res.status = 200
332
+ res.content_type = CONTENT_TYPE_JSON
333
+ res.cookies << RES_COOKIE_AUTH_SUCCESS
334
+ res.body = RES_BODY_STATUS_ROOT
335
+ else
336
+ res.cookies << RES_COOKIE_AUTH_FAILURE
337
+ res.status = 401
338
+ end
339
+ }
340
+ srv.mount_proc('/logs/from/fluentd') {|req, res|
341
+ if req.request_method == 'POST' or req.request_method == 'PUT' or req.request_method == 'DELETE'
342
+ # WEBrick's default handler ignores query parameter of URI without method GET
343
+ req.query.update(Hash[*(req.request_line.split(' ')[1].split('?')[1].split('&').map{|kv|kv.split('=')}.flatten)])
344
+ end
345
+ case
346
+ when (not req.query['user.name'] and req.cookies.index{|i| i.name == 'alfredo.auth' and i.value == VALID_COOKIE_VALUE} < 0)
347
+ res.cookies << RES_COOKIE_AUTH_FAILURE
348
+ res.status = 401
349
+ when (req.query['op'] == 'create' and @fsdata[req.path] and req.query['overwrite'] and req.query['overwrite'] == 'false')
350
+ res.status = 500
351
+ res.content_type = CONTENT_TYPE_JSON
352
+ res.body = sprintf RES_FORMAT_ALREADY_EXISTS, req.path
353
+ when req.query['op'] == 'create'
354
+ @fsdata[req.path] = req.body
355
+ res.status = 201
356
+ res['Location'] = 'http://localhost:14000' + req.path
357
+ res.content_type = CONTENT_TYPE_JSON
358
+ when (req.query['op'] == 'append' and @fsdata[req.path])
359
+ @fsdata[req.path] += req.body
360
+ res.status = 200
361
+ res['Location'] = 'http://localhost:14000' + req.path
362
+ res.content_type = CONTENT_TYPE_JSON
363
+ when req.query['op'] == 'append'
364
+ res.status = 404
365
+ res.content_type = CONTENT_TYPE_JSON
366
+ res.body = sprintf RES_FORMAT_NOT_FOUND, req.path
367
+ when (req.request_method == 'GET' and @fsdata[req.path]) # maybe GET
368
+ res.status = 200
369
+ res.content_type = 'application/octet-stream'
370
+ res.body = @fsdata[req.path]
371
+ else
372
+ res.status = 404
373
+ res.content_type = CONTENT_TYPE_JSON
374
+ res.body = sprintf RES_FORMAT_NOT_FOUND_GET, req.path
375
+ end
376
+ }
377
+ srv.start
378
+ ensure
379
+ srv.shutdown
380
+ end
381
+ end
382
+
383
+ # to wait completion of dummy server.start()
384
+ require 'thread'
385
+ cv = ConditionVariable.new
386
+ watcher = Thread.new {
387
+ connected = false
388
+ while not connected
389
+ begin
390
+ get_content('localhost', 14000, '/', {'Cookie' => VALID_COOKIE_STRING})
391
+ connected = true
392
+ rescue Errno::ECONNREFUSED
393
+ sleep 0.1
394
+ rescue StandardError => e
395
+ p e
396
+ sleep 0.1
397
+ end
398
+ end
399
+ cv.signal
400
+ }
401
+ mutex = Mutex.new
402
+ mutex.synchronize {
403
+ cv.wait(mutex)
404
+ }
405
+ end
406
+
407
+ def test_dummy_server
408
+ d = create_driver
409
+ authheader = {'Cookie' => VALID_COOKIE_STRING}
410
+ client = Net::HTTP.start(d.instance.hoop_server.split(':')[0], d.instance.hoop_server.split(':')[1])
411
+ assert_equal '401', client.request_get('/').code
412
+ assert_equal '200', client.request_get('/?user.name=hoopuser').code
413
+ assert_equal '200', client.request_get('/', authheader).code
414
+
415
+ # /logs/from/fluentd
416
+ path1 = '/logs/from/fluentd/hoge001/moge-access-log'
417
+ path1_line1 = "1111111111111111111111111111111\n"
418
+ path1_line2 = "2222222222222222222222222222222222222222222222222\n"
419
+ assert_equal '404', client.request_put(path1 + '?op=append', path1_line1, authheader).code
420
+ assert_equal '201', client.request_post(path1 + '?op=create&overwrite=false', path1_line1, authheader).code
421
+ assert_equal path1_line1, client.request_get(path1, authheader).body
422
+ assert_equal '200', client.request_put(path1 + '?op=append', path1_line2, authheader).code
423
+ assert_equal path1_line1 + path1_line2, client.request_get(path1, authheader).body
424
+
425
+ path2 = '/logs/from/fluentd/hoge002/moge-access-log'
426
+ path2_line1 = "XXXXX___1111111111111111111111111111111\n"
427
+ path2_line2 = "YYYYY___2222222222222222222222222222222222222222222222222\n"
428
+ assert_equal '404', client.request_put(path2 + '?op=append', path2_line1, authheader).code
429
+ assert_equal '201', client.request_post(path2 + '?op=create&overwrite=false', path2_line1, authheader).code
430
+ assert_equal '500', client.request_post(path2 + '?op=create&overwrite=false', path2_line1, authheader).code
431
+ assert_equal path2_line1, client.request_get(path2, authheader).body
432
+ assert_equal '200', client.request_put(path2 + '?op=append', path2_line2, authheader).code
433
+ assert_equal path2_line1 + path2_line2, client.request_get(path2, authheader).body
434
+ assert_equal path2_line1 + path2_line2, get_content('localhost', 14000, path2, authheader)
435
+ end
436
+
437
+ def teardown
438
+ @dummy_server_thread.kill
439
+ @dummy_server_thread.join
440
+ end
441
+ end