right_support 2.6.17 → 2.7.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.
Files changed (83) hide show
  1. data/.rspec +4 -0
  2. data/CHANGELOG.rdoc +37 -0
  3. data/Gemfile +29 -0
  4. data/Gemfile.lock +111 -0
  5. data/README.rdoc +2 -0
  6. data/Rakefile +62 -0
  7. data/VERSION +1 -0
  8. data/features/balancer_error_handling.feature +34 -0
  9. data/features/balancer_health_check.feature +33 -0
  10. data/features/continuous_integration.feature +51 -0
  11. data/features/continuous_integration_cucumber.feature +28 -0
  12. data/features/continuous_integration_rspec1.feature +28 -0
  13. data/features/continuous_integration_rspec2.feature +28 -0
  14. data/features/http_client_timeout.feature +19 -0
  15. data/features/serialization.feature +95 -0
  16. data/features/step_definitions/http_client_steps.rb +27 -0
  17. data/features/step_definitions/request_balancer_steps.rb +93 -0
  18. data/features/step_definitions/ruby_steps.rb +176 -0
  19. data/features/step_definitions/serialization_steps.rb +96 -0
  20. data/features/step_definitions/server_steps.rb +134 -0
  21. data/features/support/env.rb +138 -0
  22. data/features/support/file_utils_bundler_mixin.rb +45 -0
  23. data/lib/right_support/ci/java_cucumber_formatter.rb +22 -8
  24. data/lib/right_support/ci/java_spec_formatter.rb +26 -8
  25. data/lib/right_support/ci/rake_task.rb +3 -0
  26. data/lib/right_support/ci.rb +24 -0
  27. data/lib/right_support/crypto/signed_hash.rb +22 -0
  28. data/lib/right_support/data/serializer.rb +24 -2
  29. data/lib/right_support/net/address_helper.rb +20 -8
  30. data/lib/right_support/net/dns.rb +20 -8
  31. data/lib/right_support/net/http_client.rb +22 -0
  32. data/lib/right_support/net/request_balancer.rb +27 -21
  33. data/lib/right_support/net/s3_helper.rb +20 -8
  34. data/lib/right_support/net/ssl/open_ssl_patch.rb +22 -0
  35. data/lib/right_support/net/ssl.rb +20 -8
  36. data/lib/right_support/ruby/easy_singleton.rb +22 -0
  37. data/lib/right_support/ruby/object_extensions.rb +22 -0
  38. data/lib/right_support/ruby/string_extensions.rb +1 -1
  39. data/lib/right_support.rb +13 -10
  40. data/right_support.gemspec +180 -18
  41. data/right_support.rconf +8 -0
  42. data/spec/config/feature_set_spec.rb +83 -0
  43. data/spec/crypto/signed_hash_spec.rb +60 -0
  44. data/spec/data/hash_tools_spec.rb +471 -0
  45. data/spec/data/uuid_spec.rb +45 -0
  46. data/spec/db/cassandra_model_part1_spec.rb +84 -0
  47. data/spec/db/cassandra_model_part2_spec.rb +73 -0
  48. data/spec/db/cassandra_model_spec.rb +359 -0
  49. data/spec/fixtures/encrypted_priv_rsa.pem +30 -0
  50. data/spec/fixtures/good_priv_dsa.pem +12 -0
  51. data/spec/fixtures/good_priv_rsa.pem +15 -0
  52. data/spec/fixtures/good_pub_dsa.ssh +1 -0
  53. data/spec/fixtures/good_pub_rsa.pem +5 -0
  54. data/spec/fixtures/good_pub_rsa.ssh +1 -0
  55. data/spec/log/exception_logger_spec.rb +76 -0
  56. data/spec/log/filter_logger_spec.rb +8 -0
  57. data/spec/log/mixin_spec.rb +62 -0
  58. data/spec/log/multiplexer_spec.rb +54 -0
  59. data/spec/log/null_logger_spec.rb +36 -0
  60. data/spec/log/system_logger_spec.rb +92 -0
  61. data/spec/net/address_helper_spec.rb +57 -0
  62. data/spec/net/balancing/health_check_spec.rb +382 -0
  63. data/spec/net/balancing/round_robin_spec.rb +15 -0
  64. data/spec/net/balancing/sticky_policy_spec.rb +92 -0
  65. data/spec/net/dns_spec.rb +152 -0
  66. data/spec/net/http_client_spec.rb +171 -0
  67. data/spec/net/request_balancer_spec.rb +579 -0
  68. data/spec/net/s3_helper_spec.rb +160 -0
  69. data/spec/net/ssl_spec.rb +42 -0
  70. data/spec/net/string_encoder_spec.rb +58 -0
  71. data/spec/rack/log_setter_spec.rb +5 -0
  72. data/spec/rack/request_logger_spec.rb +68 -0
  73. data/spec/rack/request_tracker_spec.rb +5 -0
  74. data/spec/ruby/easy_singleton_spec.rb +72 -0
  75. data/spec/ruby/object_extensions_spec.rb +27 -0
  76. data/spec/ruby/string_extensions_spec.rb +98 -0
  77. data/spec/spec_helper.rb +181 -0
  78. data/spec/stats/activity_spec.rb +193 -0
  79. data/spec/stats/exceptions_spec.rb +123 -0
  80. data/spec/stats/helpers_spec.rb +603 -0
  81. data/spec/validation/openssl_spec.rb +37 -0
  82. data/spec/validation/ssh_spec.rb +39 -0
  83. metadata +218 -19
@@ -0,0 +1,359 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::DB::CassandraModel do
4
+
5
+ class Cassandra
6
+ class OrderedHash < Hash
7
+ def keys
8
+ super.sort
9
+ end
10
+ end
11
+ end
12
+
13
+ def init_app_state(column_family,keyspace,server,env)
14
+ ENV["RACK_ENV"] = env
15
+ RightSupport::DB::CassandraModel.column_family = column_family
16
+ RightSupport::DB::CassandraModel.keyspace = keyspace
17
+ RightSupport::DB::CassandraModel.config = {"#{env}" => {"server" => server}}
18
+ end
19
+
20
+ context "initialization" do
21
+ # This method determines the current keyspace based on the return value of the CassandraModel.keyspace method
22
+ # which looks at the value of @@current_keyspace or @@default_keyspace to determine the keyspace it is operating
23
+ # under. If a connection already exists for the keyspace it will re-use it. If a connection does not exist,
24
+ # it will create a new persistent connection for that keyspace that can be re-used with future requests
25
+ context :conn do
26
+ let(:column_family) { 'column_family' }
27
+ let(:env) { 'test' }
28
+ let(:server) { 'localhost:9160' }
29
+ let(:keyspace) { 'SatelliteService_1' }
30
+ let(:default_keyspace) { 'SatelliteService' }
31
+ let(:current_keyspace_connection) { flexmock('cassandra') }
32
+ let(:default_keyspace_connection) { flexmock('cassandra') }
33
+
34
+ before(:each) do
35
+ ENV["RACK_ENV"] = env
36
+ RightSupport::DB::CassandraModel.column_family = column_family
37
+ RightSupport::DB::CassandraModel.keyspace = default_keyspace
38
+ RightSupport::DB::CassandraModel.config = {env => {"server" => server}}
39
+
40
+ current_keyspace_connection.should_receive(:disable_node_auto_discovery!).and_return(true)
41
+ current_keyspace_connection.should_receive(:name).and_return('connection1')
42
+
43
+ default_keyspace_connection.should_receive(:disable_node_auto_discovery!).and_return(true)
44
+ default_keyspace_connection.should_receive(:name).and_return('connection2')
45
+
46
+ flexmock(Cassandra).should_receive(:new).with(keyspace + '_' + (ENV['RACK_ENV'] || 'development'), "localhost:9160", {:timeout=>10}).and_return(current_keyspace_connection)
47
+ flexmock(Cassandra).should_receive(:new).with(default_keyspace + '_' + (ENV['RACK_ENV'] || 'development'), "localhost:9160", {:timeout=>10}).and_return(default_keyspace_connection)
48
+ end
49
+
50
+ it 'raises a meaningful exception when a config stanza is missing' do
51
+ old_rack_env = ENV['RACK_ENV']
52
+
53
+ begin
54
+ ENV['RACK_ENV'] = 'foobar_12345'
55
+ bad_proc = lambda { RightSupport::DB::CassandraModel.reconnect }
56
+ bad_proc.should raise_error(RightSupport::DB::MissingConfiguration)
57
+ # This must be the very first attempt to call #conn during the execution of this spec
58
+ bad_proc = lambda { RightSupport::DB::CassandraModel.conn }
59
+ bad_proc.should raise_error(RightSupport::DB::MissingConfiguration)
60
+ ensure
61
+ ENV['RACK_ENV'] = old_rack_env
62
+ end
63
+ end
64
+
65
+ # This method assumes that keyspaces being requested to connect to already exist.
66
+ # If they do not exist, it should NOT create them. If the connection is able
67
+ # to be successfully established then it should be stored in a pool of connections
68
+ it 'creates a new connection if no connection exists for provided keyspace' do
69
+ RightSupport::DB::CassandraModel.conn.name.should == default_keyspace_connection.name
70
+ end
71
+
72
+ # If a connection has already been opened for a keyspace it should be re-used
73
+ it 're-uses an existing connection if it exists for provided keyspace' do
74
+ RightSupport::DB::CassandraModel.conn.name.should == RightSupport::DB::CassandraModel.conn.name
75
+ end
76
+
77
+ # The keyspace being used for the connection should be either the current keyspace or the default keyspace
78
+ it 'uses the connection that corresponds to the provided keyspace' do
79
+ RightSupport::DB::CassandraModel.with_keyspace(keyspace) do
80
+ RightSupport::DB::CassandraModel.conn.name.should == current_keyspace_connection.name
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ context "use" do
87
+
88
+ before(:each) do
89
+ @column_family = "TestApp"
90
+ @keyspace = "TestAppService"
91
+ @server = "localhost:9160"
92
+ @env = "test"
93
+ @timeout = {:timeout => RightSupport::DB::CassandraModel::DEFAULT_TIMEOUT}
94
+
95
+ init_app_state(@column_family, @keyspace, @server, @env)
96
+
97
+ @key = 'key'
98
+ @value = 'foo'
99
+ @offset = 'bar'
100
+ @attrs = {@offset => @value}
101
+ @opt = {}
102
+ @get_opt = {:count => RightSupport::DB::CassandraModel::DEFAULT_COUNT}
103
+
104
+ @instance = RightSupport::DB::CassandraModel.new(@key, @attrs)
105
+
106
+ @conn = flexmock(:connection)
107
+ flexmock(RightSupport::DB::CassandraModel).should_receive(:conn).and_return(@conn)
108
+ @conn.should_receive(:insert).with(@column_family, @key, @attrs,@opt).and_return(true)
109
+ @conn.should_receive(:remove).with(@column_family, @key).and_return(true)
110
+ @conn.should_receive(:get).with(@column_family, @key, @get_opt).and_return(@attrs).by_default
111
+ @conn.should_receive(:multi_get).with(@column_family, [1,2], @opt).and_return(Hash.new)
112
+ end
113
+
114
+ describe "instance methods" do
115
+ context :save do
116
+ it 'saves the row' do
117
+ @instance.save.should be_true
118
+ end
119
+ end
120
+
121
+ context :destroy do
122
+ it 'destroys the row' do
123
+ @instance.destroy.should be_true
124
+ end
125
+ end
126
+
127
+ context :reload do
128
+ it 'returns a new object for the row' do
129
+ @instance.reload.should be_a_kind_of(RightSupport::DB::CassandraModel)
130
+ @instance.reload!.should be_a_kind_of(RightSupport::DB::CassandraModel)
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "class methods" do
136
+ # We want to remain backward-compatible for existing services so we expect this call to be made
137
+ # as such: RightSupport::DB::CassandraModel.keyspace = "SatelliteService" and CassandraModel
138
+ # will append the RACK_ENV to the end of it. Ex: "SatelliteService_development"
139
+ context :keyspace= do
140
+ let(:keyspace) { 'SatelliteService' }
141
+
142
+ it 'appends the environment to the keyspace provided' do
143
+ RightSupport::DB::CassandraModel.keyspace = keyspace
144
+ RightSupport::DB::CassandraModel.send(:class_variable_get, :@@default_keyspace).should == (keyspace + "_" + (ENV['RACK_ENV'] || 'development'))
145
+ end
146
+ end
147
+
148
+ # If a current keyspace is provided it takes precedence over the default keyspace. If none is
149
+ # provided, the default keyspace should be returned.
150
+ context :keyspace do
151
+ let(:keyspace) { 'SatelliteService_' + ENV['RACK_ENV'] }
152
+
153
+ it 'returns the default keyspace if no current keyspace is set' do
154
+ RightSupport::DB::CassandraModel.send(:class_variable_set, :@@current_keyspace, nil)
155
+ RightSupport::DB::CassandraModel.send(:class_variable_set, :@@default_keyspace, keyspace)
156
+ RightSupport::DB::CassandraModel.keyspace.should == keyspace
157
+ end
158
+
159
+ it 'returns the current keyspace if a current keyspace is set' do
160
+ RightSupport::DB::CassandraModel.send(:class_variable_set, :@@current_keyspace, keyspace)
161
+ RightSupport::DB::CassandraModel.send(:class_variable_set, :@@default_keyspace, nil)
162
+ RightSupport::DB::CassandraModel.keyspace.should == keyspace
163
+ end
164
+ end
165
+
166
+ # This method assumes that a valid keyspace is passed in. If the keyspace does not exist we do NOT
167
+ # want to create it. CassandraModel should use the keyspace provided for the duration of the code
168
+ # executed within the block. Any requests processed outside of the block should execute using the
169
+ # default keyspace.
170
+ context :with_keyspace do
171
+ let(:keyspace) { 'SatelliteService_1' }
172
+ let(:default_keyspace) { 'SatelliteService' }
173
+
174
+ before(:each) do
175
+ RightSupport::DB::CassandraModel.keyspace = default_keyspace
176
+ end
177
+
178
+ it 'sets the current keyspace to the keyspace provided for execution within the block' do
179
+ RightSupport::DB::CassandraModel.with_keyspace(keyspace) do
180
+ RightSupport::DB::CassandraModel.keyspace.should == keyspace + "_" + 'test'
181
+ end
182
+ end
183
+
184
+ it 'resets back to the default keyspace for execution outside of the block' do
185
+ RightSupport::DB::CassandraModel.with_keyspace(keyspace) {}
186
+ RightSupport::DB::CassandraModel.keyspace.should == default_keyspace + "_" + 'test'
187
+ end
188
+ context 'append_env parameter' do
189
+ it 'appends the environment by default' do
190
+ RightSupport::DB::CassandraModel.with_keyspace('Monkey') do
191
+ RightSupport::DB::CassandraModel.keyspace.should == 'Monkey_test'
192
+ end
193
+ end
194
+
195
+ it 'appends the environment when append_env == true' do
196
+ RightSupport::DB::CassandraModel.with_keyspace('Monkey', true) do
197
+ RightSupport::DB::CassandraModel.keyspace.should == 'Monkey_test'
198
+ end
199
+ end
200
+
201
+ it 'does NOT append the environment when append_env == false' do
202
+ RightSupport::DB::CassandraModel.with_keyspace('Monkey_notatest', false) do
203
+ RightSupport::DB::CassandraModel.keyspace.should == 'Monkey_notatest'
204
+ end
205
+ end
206
+
207
+ it 'avoids double-appending the environment' do
208
+ RightSupport::DB::CassandraModel.with_keyspace('Monkey_test') do
209
+ RightSupport::DB::CassandraModel.keyspace.should == 'Monkey_test'
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ context :insert do
216
+ it 'inserts a row by using the class method' do
217
+ RightSupport::DB::CassandraModel.insert(@key, @attrs, @opt).should be_true
218
+ end
219
+ end
220
+
221
+ context :remove do
222
+ it 'removes a row by using the class method' do
223
+ RightSupport::DB::CassandraModel.remove(@key).should be_true
224
+ end
225
+ end
226
+
227
+ context :all do
228
+ it 'returns all existing rows for the specified array of keys' do
229
+ RightSupport::DB::CassandraModel.all([1, 2]).should be_a_kind_of(Hash)
230
+ end
231
+ end
232
+
233
+ context :get do
234
+ it 'returns row for the specified key' do
235
+ RightSupport::DB::CassandraModel.get(@key).should be_a_kind_of(RightSupport::DB::CassandraModel)
236
+ end
237
+
238
+ it 'returns only number of columns requested' do
239
+ attrs = {@offset + '1' => @value, @offset + '2' => @value}
240
+ get_opt = {:count => 2}
241
+ @conn.should_receive(:get).with(@column_family, @key, get_opt).and_return(attrs).once
242
+ RightSupport::DB::CassandraModel.get(@key, get_opt).attributes.should == attrs
243
+ end
244
+
245
+ it 'returns all columns for the specified key if no count specified' do
246
+ pending "Unpredictable behavior on ruby < 1.9" unless RUBY_VERSION >= "1.9"
247
+ default_count = RightSupport::DB::CassandraModel::DEFAULT_COUNT
248
+
249
+ RightSupport::DB::CassandraModel.instance_eval { remove_const :DEFAULT_COUNT }
250
+ RightSupport::DB::CassandraModel.const_set(:DEFAULT_COUNT, 2)
251
+ begin
252
+
253
+ attrs1 = {@offset + '1' => @value, @offset + '2' => @value}
254
+ attrs2 = {@offset + '3' => @value}
255
+ attrs = attrs1.merge(attrs2)
256
+ get_opt1 = {:count => 2}
257
+ get_opt2 = {:count => 2, :start => @offset + '2'}
258
+ @conn.should_receive(:get).with(@column_family, @key, get_opt1).and_return(attrs1).once
259
+ @conn.should_receive(:get).with(@column_family, @key, get_opt2).and_return(attrs2).once
260
+ RightSupport::DB::CassandraModel.get(@key).attributes.should == attrs
261
+ ensure
262
+ RightSupport::DB::CassandraModel.instance_eval { remove_const :DEFAULT_COUNT }
263
+ RightSupport::DB::CassandraModel.const_set(:DEFAULT_COUNT, default_count)
264
+ end
265
+ end
266
+
267
+ it 'returns nil if key not found' do
268
+ @conn.should_receive(:get).with(@column_family, @key, @get_opt).and_return({})
269
+ RightSupport::DB::CassandraModel.get(@key).should be_nil
270
+ end
271
+ end
272
+
273
+ def real_get_indexed(index, key, columns = nil, opt = {})
274
+ rows = {}
275
+ start = ""
276
+ count = DEFAULT_COUNT
277
+ expr = do_op(:create_idx_expr, index, key, "EQ")
278
+ opt = opt[:consistency] ? {:consistency => opt[:consistency]} : {}
279
+ while true
280
+ clause = do_op(:create_idx_clause, [expr], start, count)
281
+ chunk = do_op(:get_indexed_slices, column_family, clause, columns, opt)
282
+ rows.merge!(chunk)
283
+ if chunk.size == count
284
+ # Assume there are more chunks, use last key as start of next get
285
+ start = chunk.keys.last
286
+ else
287
+ # This must be the last chunk
288
+ break
289
+ end
290
+ end
291
+ rows
292
+ end
293
+
294
+ context :get_indexed do
295
+
296
+ before(:each) do
297
+ @column = flexmock(:column, :name => 'foo', :value => 'bar')
298
+ @column_or_super = flexmock(:column_or_super, :column => @column)
299
+ @rows = {@key => [@column_or_super]}
300
+ @index = 'index'
301
+ @index_key = 'index_key'
302
+ @start = ""
303
+ @count = RightSupport::DB::CassandraModel::DEFAULT_COUNT
304
+ @expr = flexmock(:expr)
305
+ @conn.should_receive(:create_idx_expr).and_return(@expr)
306
+ @clause = flexmock(:clause)
307
+ @conn.should_receive(:create_idx_clause).with([@expr], @start, @count).and_return(@clause).by_default
308
+ @conn.should_receive(:get_indexed_slices).with(@column_family, @clause, nil, {}).and_return(@rows).by_default
309
+ end
310
+
311
+ it 'returns row for the specified key' do
312
+ rows = RightSupport::DB::CassandraModel.get_indexed(@index, @index_key)
313
+ rows.should be_a_kind_of(Array)
314
+ rows.size.should == 1
315
+ rows.first.should be_a_kind_of(RightSupport::DB::CassandraModel)
316
+ end
317
+
318
+ it 'returns all rows for the specified key if no count specified' do
319
+ pending "Unpredictable behavior on ruby < 1.9" unless RUBY_VERSION >= "1.9"
320
+
321
+ default_count = RightSupport::DB::CassandraModel::DEFAULT_COUNT
322
+
323
+ RightSupport::DB::CassandraModel.instance_eval { remove_const :DEFAULT_COUNT }
324
+ RightSupport::DB::CassandraModel.const_set(:DEFAULT_COUNT, 2)
325
+ begin
326
+ key1 = @key + '8'
327
+ key2 = @key + '12'
328
+ key3 = @key + '13'
329
+ cols = {'foo' => 'bar'}
330
+ rows1 = {key1 => [@column_or_super], key2 => [@column_or_super]}
331
+ rows2 = {key3 => [@column_or_super]}
332
+ @conn.should_receive(:create_idx_clause).with([@expr], @start, 2).and_return(@clause).once
333
+ @conn.should_receive(:get_indexed_slices).with(@column_family, @clause, nil, {}).and_return(rows1).once
334
+ @conn.should_receive(:create_idx_clause).with([@expr], key2, 2).and_return(@clause).once
335
+ @conn.should_receive(:get_indexed_slices).with(@column_family, @clause, nil, {}).and_return(rows2).once
336
+ rows = RightSupport::DB::CassandraModel.get_indexed(@index, @index_key)
337
+ rows.size.should == 3
338
+ rows.inject({}) { |s, r| s[r.key] = r.attributes; s }.should == {key1 => cols, key2 => cols, key3 => cols}
339
+ ensure
340
+ RightSupport::DB::CassandraModel.instance_eval { remove_const :DEFAULT_COUNT }
341
+ RightSupport::DB::CassandraModel.const_set(:DEFAULT_COUNT, default_count)
342
+ end
343
+ end
344
+
345
+ it 'returns empty array if no rows found for key' do
346
+ @conn.should_receive(:get_indexed_slices).with(@column_family, @clause, nil, {}).and_return({}).once
347
+ RightSupport::DB::CassandraModel.get_indexed(@index, @index_key).should == []
348
+ end
349
+ end
350
+ end
351
+ end
352
+
353
+ context '.calculate_random_partitioner_token' do
354
+ it 'works' do
355
+ expected = 73001115008341200964825964898370608784
356
+ described_class.calculate_random_partitioner_token('71:foomonkeys').should == expected
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,30 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ Proc-Type: 4,ENCRYPTED
3
+ DEK-Info: DES-EDE3-CBC,61C0BBB816A708D4
4
+
5
+ SQ/Y+U7z51OL/hx4YnfVPl6uqsOjpSjU2w4NFqB6EyBtMFgOoqW6ryuIi6ycHgWJ
6
+ YabUlMPdnexU+1BGqu9P2M9kQejfVddBFmbE09s54dwamk7q6RjaARN0UvHq6Ms9
7
+ r5/nqRBrinWtOeU/+7cV5qGp2/HbWC2bHAUCCwBcq+ymEpLOw/dpHpxjuiB5ZOLt
8
+ QzcEJ8ePlNDYvPxw4Wjvb5IDjS4MSoJfA5Y2ZXwVSI1d7mhx4Cekx3CkTMWKYIU8
9
+ FS6hO39tfPTcxb4os3FYl8D8TlRHv5atrsptKqYE4DKhZhoDA5yFbFJHZlEp+G6H
10
+ t+snMyuzEWNWq3wGI/gaglatUMq6airA50qticbw8kvz+67pPh2gkAgMXBHEGgPW
11
+ m0xnqaWf+MyQCUfaxjvxAq68Ced+H9ULLYFrzWAIuSh1M+QartuvP1ZOIcDpY83i
12
+ RybTJC5MLgGE0COM7oOtPfXO/rvdf6jqTr54HhYwq18aWrqjnacZi0JzNEFRtpNa
13
+ 5FNbwbQGT4Y8J1rOgbyjrd712oRhfjMAU39w4b6gEIATB4zDnMVkdb/Rde/nB7i4
14
+ CqDB9iSEbtQN4Ev5fXpIDrmhbdT/CURpzRvIt68x08qvX8KQwqDbLz39luocApxN
15
+ lnTihGiYAiNflMpDvhNL8MSLxYgL5Or2KVF/Ppc+1Qw23BEYDlaRRzc71PWdrVr8
16
+ UxEdFtqQnnthO7PabrwmLiPeArIGL4U8wrbXA3GbkbDtClftWMaC9JrzVJvXp1z7
17
+ sXZPhdZzSCg3n3ZbAGKYUx/G8AHojLKfAF3BHufnU9qM+6TS/KtTnhrvHSgYPcBn
18
+ etI7DWZ/mUnLwnsWfiQ3n5PeeUq/nU+IFJT+Y3r+TfFVCiGPW60XpfLzH1U4otKq
19
+ KaEnCQX6S7p1Jinx+FHL9fg1I50E6QUNO+N8XO4ay3Jzumz2yjZeudyt+uy954Hq
20
+ RfmDGYImqrkYrzd8d6hNUTBGnUfUTvq2k/orKhvcxtX+tr9WhNshnJcK18SgBRKT
21
+ 6XLybyioikVSpUGsL95kIhrspryO8v7/M1igWWrLu9fe/jUJLCFzlbGSRYDn+QTp
22
+ z7qpbBvxHPgu1fKurZ96nCcigVxQgY5rPwpQvG+mkD6YOhgDvSAjTos7Fagz6nb6
23
+ 01MufCVvFx+BBZEsol0Ag23RhXeJXvxkcF//hcPseCA/oB9Aqzyu+ml9mgp4v6P+
24
+ AgVopgGCfZR31CQ2b9AxdSARFCj2AFE9AdeNy268KSx8wLWAlzUcnGi57eK8WJM3
25
+ KTDOwJZDgZkjYyMZATpxOj8w3euAflQjQmyXpN1/Q0BW66Mj9+XzVk2yEpF2S0iE
26
+ xKzPcNEzlGMo1xo6mqVE/kIuRSodXuGp5Eoo8ITvpYWcSdtCTSKJNxAUqry/k9bg
27
+ 9sf3jSmSHzUd92pdM02p8A42HcaPGLmbuFjIfZPK7KoYmIdhJqy7s08CFUlk9XI/
28
+ PxdYph5J4WkAbeomPGUTFxEWV1cM2lvW4hotrx8MS50WLVZxxY1p7D+JxN3KeJyc
29
+ mytYpg2dxr8UF64NFKuyEmunkDTdYH2Oi7B7mAcw/QlBN+qCARP+tA==
30
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,12 @@
1
+ -----BEGIN DSA PRIVATE KEY-----
2
+ MIIBvAIBAAKBgQC1RlSYmT8VVtunF3xcpSdJfihaHAGzLH0nf2ooHcKIH6PyxjCe
3
+ Q93rFB69uyGPUJVawz3w0Y1B7B6h5eXI4SEKA4iqmSHXdFonO7wvOT5DTkzQl3yX
4
+ B1s4xU8+GW1MtUNKxHPzFpX40797Z1enO/EZRNLYt0P4IG86s/O+BqD6IwIVAJ24
5
+ m8A6ZFqsunyy2L5o9UInE+gfAoGAPuEeCrIginLFwSdwyrxEwC/gblKvOf/9+iMB
6
+ Jo+5fN2E9AHKrCsCa53E2lnUbXOdSKDR6yVmUQSB3EsBDoLqDOVJh/BWVTABebIO
7
+ WwnjQZX9UBGnsJwD4zpvtXLU4qnyFywSr1kdLDHMsGS3WVocJ9jOEH7Rh0xMM423
8
+ yYS4bUQCgYEApxLO5sTQS/Ej8+BFJQrMS3xHpONQxiMnTLE3q6qZXJRgHDgsqqwZ
9
+ pAfZCjRxvvfUfBGzjKwJlGQOLZVaGZOArGQF7NRtOfCHZtMBg1vubewXs64gmH5G
10
+ aXvJ17D6W2vCO04R/vXh43qKLDcPu7wGng+eWNaw/LkbdfC4k8uVfp4CFQCHe0yn
11
+ rgxFphSFqf8Spchmsg/coA==
12
+ -----END DSA PRIVATE KEY-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICWwIBAAKBgQDm4VY+mDoYUsH+7idaNXP6Bq+YIXwqGNar6zgbPEe6qgpF9nSQ
3
+ B98gMObcZ9H7qGm3e/bQTCrlBAB/jMUQQbTyPsEy3pIKeWoPl7GTQpcSS9WZoHYX
4
+ yf4TN8MMOeJ3rGbyGb8Xm13R+SBpOMOZfnT3KUJLznIYVtBvv+uFz/C9ewIDAQAB
5
+ AoGAMJzLkvxsZwitziaI5KaSl9dmi4qpYRe/w40QUDO+CqCY7yg4XMc7hMSnJ0s4
6
+ 3FsWf0q7qhoPgg74p+KU75pWJbEHoYor6LqDuxov4DSx/2SfqmDBIAb9IQ4KWDr2
7
+ WU/o2/ivbHJ89FxCCroMzNg+qm8pAHVYQ72E/w/1sSRuQPECQQD4fes0OrE8qlJ0
8
+ 0UCxiTbotreNY7N/pKPIFqL/+ODcXJIHCyvVAMXw469r9KacrC/t8qKNatWk3mP1
9
+ uZzfoAJJAkEA7dsxOJcTqMAncOzbP2cXPkGvzyS06mUoziuZWDc+NAOy4R/V/xHW
10
+ +CUleXs06G9c/LoZ+E80MP600YvPtKUBowJAZXXMfnPkgDevGUGDX7n98IECazml
11
+ Rd3sfbs4bLmq3m7xtOyLxhnduGDO9I5dJSVtIIPseT+A4iNnvyI9elVz+QJALTBe
12
+ mpTBTGctVxv4z4Pje5V5NE6R/JI5fWn1GThtne4x9ulYe7xE7iIIi5rnw10c+nrU
13
+ 4kMR9Fj/u2vnizdqJwJACb7DY5rKZa/xgzgLtE5jW60zZWvwS9LYUs+1ND6CeiFy
14
+ s3EiH2z+zbSO+cnMtCutdRKIHk3wy6tyRKOq0EMafA==
15
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1 @@
1
+ ssh-dss AAAAB3NzaC1kc3MAAACBALVGVJiZPxVW26cXfFylJ0l+KFocAbMsfSd/aigdwogfo/LGMJ5D3esUHr27IY9QlVrDPfDRjUHsHqHl5cjhIQoDiKqZIdd0Wic7vC85PkNOTNCXfJcHWzjFTz4ZbUy1Q0rEc/MWlfjTv3tnV6c78RlE0ti3Q/ggbzqz874GoPojAAAAFQCduJvAOmRarLp8sti+aPVCJxPoHwAAAIA+4R4KsiCKcsXBJ3DKvETAL+BuUq85//36IwEmj7l83YT0AcqsKwJrncTaWdRtc51IoNHrJWZRBIHcSwEOguoM5UmH8FZVMAF5sg5bCeNBlf1QEaewnAPjOm+1ctTiqfIXLBKvWR0sMcywZLdZWhwn2M4QftGHTEwzjbfJhLhtRAAAAIEApxLO5sTQS/Ej8+BFJQrMS3xHpONQxiMnTLE3q6qZXJRgHDgsqqwZpAfZCjRxvvfUfBGzjKwJlGQOLZVaGZOArGQF7NRtOfCHZtMBg1vubewXs64gmH5GaXvJ17D6W2vCO04R/vXh43qKLDcPu7wGng+eWNaw/LkbdfC4k8uVfp4= user@host
@@ -0,0 +1,5 @@
1
+ -----BEGIN RSA PUBLIC KEY-----
2
+ MIGJAoGBAObhVj6YOhhSwf7uJ1o1c/oGr5ghfCoY1qvrOBs8R7qqCkX2dJAH3yAw
3
+ 5txn0fuoabd79tBMKuUEAH+MxRBBtPI+wTLekgp5ag+XsZNClxJL1ZmgdhfJ/hM3
4
+ www54nesZvIZvxebXdH5IGk4w5l+dPcpQkvOchhW0G+/64XP8L17AgMBAAE=
5
+ -----END RSA PUBLIC KEY-----
@@ -0,0 +1 @@
1
+ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEApfGJ2lzcKgWq50u4yhZyP7yDjA4CF2pV19TUOlXg4a40FCuKi7JIqPCyixmrWYrvRFz+5zY+gHWOHkCfjNaDryUgPsBMzk2DZDD8fsrJ5KU7QDAa1RMbixnWsWTDcXGhHxC7jtRV7qKFvribLYEoUfVbWZYQR3Wl9kfc78sF6Z2TZuL2Fb9YJc5f2uDz6+I6oy8LtGci4Y2hKVYxrN5tgvFcwkE8JsRaAnjWicfoHRUIBZ/tdT76EMhBEKdO63hO9AZ8phTyZoJyLzykDRaelBUYXjG0LQXTq4jiJhKa07kv0XIgNwnz4zB9qtiOHXm6KZZDxy7xnVXOFum+JqcUxQ== user@host
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Log::ExceptionLogger do
4
+ before(:all) do
5
+ @actual_logger = Logger.new(StringIO.new)
6
+ @logger = RightSupport::Log::ExceptionLogger.new(@actual_logger)
7
+ @exception = Exception.new('message')
8
+ end
9
+
10
+ context :exception do
11
+ it 'logs an error with exception information' do
12
+ flexmock(@actual_logger).should_receive(:fatal)
13
+ @logger.exception('desc', @exception)
14
+ end
15
+ end
16
+
17
+ context 'class methods' do
18
+ context :format_exception do
19
+ it 'includes the description' do
20
+ RightSupport::Log::ExceptionLogger.format_exception('desc').should == 'desc'
21
+ end
22
+
23
+ it 'includes the exception message if present' do
24
+ e = ArgumentError.new("err")
25
+ RightSupport::Log::ExceptionLogger.format_exception('desc', e).should =~ /desc \(ArgumentError: err.*/
26
+ end
27
+
28
+ it 'includes the exception string if present' do
29
+ RightSupport::Log::ExceptionLogger.format_exception('desc', 'err', :no_trace).should == 'desc (err)'
30
+ end
31
+
32
+ context 'with backtrace=:no_trace' do
33
+ it 'does not include a backtrace' do
34
+ e = ArgumentError.new("err")
35
+ RightSupport::Log::ExceptionLogger.format_exception('desc', e, :no_trace).should == 'desc (ArgumentError: err)'
36
+ end
37
+ end
38
+
39
+ context 'with backtrace=:caller' do
40
+ it 'includes a single line of backtrace' do
41
+ begin
42
+ raise ArgumentError.new("err")
43
+ rescue Exception => e
44
+ RightSupport::Log::ExceptionLogger.format_exception('desc', e, :caller).should == "desc (ArgumentError: err IN #{e.backtrace[0]})"
45
+ end
46
+ end
47
+
48
+ it 'defaults to :caller trace' do
49
+ begin
50
+ raise ArgumentError.new("err")
51
+ rescue Exception => e
52
+ RightSupport::Log::ExceptionLogger.format_exception('desc', e).should == "desc (ArgumentError: err IN #{e.backtrace[0]})"
53
+ end
54
+ end
55
+
56
+ it 'excludes backtrace if the exception does not respond to backtrace' do
57
+ RightSupport::Log::ExceptionLogger.format_exception('desc', 'err').should == "desc (err)"
58
+ end
59
+ end
60
+
61
+ context 'with backtrace=:trace' do
62
+ it 'includes a full backtrace' do
63
+ begin
64
+ raise ArgumentError.new("err")
65
+ rescue Exception => e
66
+ RightSupport::Log::ExceptionLogger.format_exception('desc', e, :trace).should == "desc (ArgumentError: err IN\n " + e.backtrace.join("\n ") + ")"
67
+ end
68
+ end
69
+
70
+ it 'excludes backtrace if the exception does not respond to backtrace' do
71
+ RightSupport::Log::ExceptionLogger.format_exception('desc', 'err').should == "desc (err)"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Log::FilterLogger do
4
+ it 'does method_missing correctly'
5
+ it 'does respond_to? correctly'
6
+ it 'transparently proxies Logger methods to underlying object'
7
+ it 'filters log messages'
8
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ class InnocentVictim
4
+ include RightSupport::Log::Mixin
5
+ end
6
+
7
+ describe RightSupport::Log::Mixin do
8
+ context 'when mixed into a base class' do
9
+ before(:each) do
10
+ @victim = InnocentVictim.new
11
+ end
12
+
13
+ it 'provides a class-level accessor' do
14
+ @victim.class.should respond_to(:logger)
15
+ @victim.class.should respond_to(:logger=)
16
+ end
17
+
18
+ it 'provides an instance-level accessor' do
19
+ @victim.should respond_to(:logger)
20
+ end
21
+
22
+ context :logger do
23
+ context 'without a logger' do
24
+ before(:each) do
25
+ @victim.class.logger = nil
26
+ end
27
+
28
+ it 'uses the default logger' do
29
+ flexmock(RightSupport::Log::Mixin.default_logger).should_receive(:info).twice.and_return(true)
30
+ @victim.class.logger.info('lalalala').should be_true
31
+ @victim.logger.info('lalalala').should be_true
32
+ end
33
+ end
34
+
35
+ context 'with class logger' do
36
+ before(:each) do
37
+ @logger = mock_logger
38
+ @victim.class.logger = @logger
39
+ end
40
+
41
+ it 'uses class logger' do
42
+ @logger.should_receive(:info).and_return(true).twice
43
+ @victim.class.logger.info('lalalala').should be_true
44
+ @victim.logger.info('lalalala').should be_true
45
+ end
46
+
47
+ context 'with instance logger' do
48
+ before(:each) do
49
+ @instance_logger = mock_logger
50
+ @victim.logger = @instance_logger
51
+ end
52
+
53
+ it 'uses instance logger' do
54
+ @instance_logger.should_receive(:info).and_return(true).once
55
+ @logger.should_receive(:info).never
56
+ @victim.logger.info('lalalala').should be_true
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ #
2
+ # Copyright (c) 2009-2012 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'spec_helper'
24
+
25
+ describe RightSupport::Log::Multiplexer do
26
+
27
+ before(:all) do
28
+ @target1 = RightSupport::Log::NullLogger.new
29
+ @target2 = RightSupport::Log::NullLogger.new
30
+ @target3 = RightSupport::Log::NullLogger.new
31
+ @multiplexer = RightSupport::Log::Multiplexer.new(@target1, @target2, @target3)
32
+ end
33
+
34
+ it 'multiplexes method calls to all callers' do
35
+ flexmock(@target1).should_receive(:some_method).with('arg', 'arg2').once
36
+ flexmock(@target2).should_receive(:some_method).with('arg', 'arg2').once
37
+ flexmock(@target3).should_receive(:some_method).with('arg', 'arg2').once
38
+ @multiplexer.some_method('arg', 'arg2')
39
+ end
40
+
41
+ it 'returns the result returned by the first target' do
42
+ flexmock(@target1).should_receive(:some_method).with('arg', 'arg2').and_return('res1').once
43
+ flexmock(@target2).should_receive(:some_method).with('arg', 'arg2').and_return('res2').once
44
+ flexmock(@target3).should_receive(:some_method).with('arg', 'arg2').and_return('res3').once
45
+ @multiplexer.some_method('arg', 'arg2').should == 'res1'
46
+ end
47
+
48
+ it 'multiplexes #warn by undefining Object#warn' do
49
+ flexmock(@target1).should_receive(:warn).with('arg').and_return('res1')
50
+ flexmock(@target2).should_receive(:warn).with('arg').and_return('res2')
51
+ flexmock(@target3).should_receive(:warn).with('arg').and_return('res3')
52
+ @multiplexer.warn('arg').should == 'res1'
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe RightSupport::Log::NullLogger do
4
+ before(:each) do
5
+ @logger = RightSupport::Log::NullLogger.new
6
+ end
7
+
8
+ context 'log levels' do
9
+ [:debug, :info, :warn, :error, :fatal].each do |method|
10
+ it "responds to ##{method}" do
11
+ block_called = false
12
+ @logger.__send__(method, 'lalalala').should be_true
13
+ @logger.__send__(method) { block_called = true ; 'lalalala' }.should be_true
14
+ block_called.should be_true
15
+ end
16
+ end
17
+ end
18
+
19
+ context '<< method' do
20
+ it 'responds like Logger' do
21
+ (@logger << 'lalalala').should == 8
22
+ end
23
+ end
24
+
25
+ context :close do
26
+ it 'responds' do
27
+ @logger.close.should be_nil
28
+ end
29
+
30
+ it 'is idempotent' do
31
+ @logger.close.should be_nil
32
+ @logger.close.should be_nil
33
+ @logger.close.should be_nil
34
+ end
35
+ end
36
+ end