harness 0.0.1 → 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.
Files changed (34) hide show
  1. data/README.md +152 -2
  2. data/Rakefile +8 -0
  3. data/harness.gemspec +7 -0
  4. data/lib/harness/adapters/librato_adapter.rb +68 -0
  5. data/lib/harness/adapters/memory_adapter.rb +19 -0
  6. data/lib/harness/adapters/null_adapter.rb +11 -0
  7. data/lib/harness/consumer.rb +47 -0
  8. data/lib/harness/counter.rb +27 -0
  9. data/lib/harness/gauge.rb +21 -0
  10. data/lib/harness/instrumentation.rb +6 -0
  11. data/lib/harness/integration/action_controller.rb +9 -0
  12. data/lib/harness/integration/action_mailer.rb +10 -0
  13. data/lib/harness/integration/action_view.rb +10 -0
  14. data/lib/harness/integration/active_support.rb +9 -0
  15. data/lib/harness/measurement.rb +17 -0
  16. data/lib/harness/railtie.rb +36 -0
  17. data/lib/harness/tasks.rake +6 -0
  18. data/lib/harness/version.rb +1 -1
  19. data/lib/harness.rb +104 -1
  20. data/test/integration/counters_with_redis_test.rb +69 -0
  21. data/test/integration/instrumentation_test.rb +28 -0
  22. data/test/integration/integrations/action_controller_test.rb +51 -0
  23. data/test/integration/integrations/action_mailer_test.rb +22 -0
  24. data/test/integration/integrations/action_view_test.rb +22 -0
  25. data/test/integration/integrations/active_support_test.rb +40 -0
  26. data/test/integration/logging_test.rb +17 -0
  27. data/test/test_helper.rb +54 -0
  28. data/test/unit/adapters/librato_adapter_test.rb +176 -0
  29. data/test/unit/adapters/memory_adapter_test.rb +22 -0
  30. data/test/unit/counter_test.rb +55 -0
  31. data/test/unit/gauge_test.rb +53 -0
  32. data/test/unit/harness_test.rb +27 -0
  33. data/test/unit/measurement_test.rb +36 -0
  34. metadata +101 -10
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveSupportTestCase < IntegrationTest
4
+ def test_a_gauge_is_logged
5
+ ActiveSupport::Notifications.instrument "gauge_test.harness", :gauge => true do |args|
6
+ # do nothing
7
+ end
8
+
9
+ assert_gauge_logged "gauge_test.harness"
10
+ end
11
+
12
+ def test_a_counter_is_logged
13
+ ActiveSupport::Notifications.instrument "counter_test.harness", :counter => { :value => 5 } do |args|
14
+ # do nothing
15
+ end
16
+
17
+ assert_counter_logged "counter_test.harness"
18
+ end
19
+
20
+ def test_can_log_both
21
+ ActiveSupport::Notifications.instrument "test.harness", :gauge => { :id => "test-gauge" }, :counter => {:id => 'test-counter', :value => 5 } do |args|
22
+ # do nothing
23
+ end
24
+
25
+ assert_counter_logged "test-counter"
26
+ assert_gauge_logged "test-gauge"
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ require 'test_helper'
2
+
3
+ class ActionControllerIntegration < IntegrationTest
4
+ def test_logs_write_fragment
5
+ instrument "write_fragment"
6
+
7
+ assert_gauge_logged "write_fragment.action_controller"
8
+ end
9
+
10
+ def test_logs_read_fragment
11
+ instrument "read_fragment"
12
+
13
+ assert_gauge_logged "read_fragment.action_controller"
14
+ end
15
+
16
+ def test_logs_expire_fragment
17
+ instrument "expire_fragment"
18
+
19
+ assert_gauge_logged "expire_fragment.action_controller"
20
+ end
21
+
22
+ def test_logs_write_page
23
+ instrument "write_page"
24
+
25
+ assert_gauge_logged "write_page.action_controller"
26
+ end
27
+
28
+ def test_logs_expire_page
29
+ instrument "expire_page"
30
+
31
+ assert_gauge_logged "expire_page.action_controller"
32
+ end
33
+
34
+ def test_logs_process_action
35
+ instrument "process_action"
36
+
37
+ assert_gauge_logged "process_action.action_controller"
38
+ end
39
+
40
+ def test_logs_send_file
41
+ instrument "send_file"
42
+
43
+ assert_gauge_logged "send_file.action_controller"
44
+ end
45
+
46
+ def instrument(event)
47
+ ActiveSupport::Notifications.instrument "#{event}.action_controller" do |*args|
48
+ # nada
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class ActionMailerIntegration < IntegrationTest
4
+ def test_logs_mail_received
5
+ instrument "receive"
6
+
7
+ assert_gauge_logged "receive.action_mailer"
8
+ end
9
+
10
+ def test_logs_mail_delivered
11
+ instrument "deliver"
12
+
13
+ assert_gauge_logged "deliver.action_mailer"
14
+ end
15
+
16
+ private
17
+ def instrument(event)
18
+ ActiveSupport::Notifications.instrument "#{event}.action_mailer" do |*args|
19
+ # nada
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class ActionViewIntegration < IntegrationTest
4
+ def test_logs_render_template
5
+ instrument "render_template"
6
+
7
+ assert_gauge_logged "render_template.action_view"
8
+ end
9
+
10
+ def test_logs_render_partial
11
+ instrument "render_partial"
12
+
13
+ assert_gauge_logged "render_partial.action_view"
14
+ end
15
+
16
+ private
17
+ def instrument(event)
18
+ ActiveSupport::Notifications.instrument "#{event}.action_view" do |*args|
19
+ # nada
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class ActiveSupportIntegration < IntegrationTest
4
+ def test_logs_cache_read
5
+ instrument "cache_read"
6
+
7
+ assert_gauge_logged "cache_read.active_support"
8
+ end
9
+
10
+ def test_logs_cache_generate
11
+ instrument "cache_generate"
12
+
13
+ assert_gauge_logged "cache_generate.active_support"
14
+ end
15
+
16
+ def test_logs_cache_fetch_hit
17
+ instrument "cache_fetch_hit"
18
+
19
+ assert_gauge_logged "cache_fetch_hit.active_support"
20
+ end
21
+
22
+ def test_logs_cache_write
23
+ instrument "cache_write"
24
+
25
+ assert_gauge_logged "cache_write.active_support"
26
+ end
27
+
28
+ def test_logs_cache_delete
29
+ instrument "cache_delete"
30
+
31
+ assert_gauge_logged "cache_delete.active_support"
32
+ end
33
+
34
+ private
35
+ def instrument(event)
36
+ ActiveSupport::Notifications.instrument "#{event}.active_support" do |*args|
37
+ # nada
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class HarnessTest < IntegrationTest
4
+ def test_gauges_are_logged
5
+ gauge = Harness::Gauge.new :name => 'minitest'
6
+ gauge.log
7
+
8
+ assert_includes gauges, gauge
9
+ end
10
+
11
+ def test_counters_are_logged
12
+ counter = Harness::Counter.new :name => 'minitest'
13
+ counter.log
14
+
15
+ assert_includes counters, counter
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'harness'
5
+
6
+ require 'minitest/unit'
7
+ require 'minitest/pride'
8
+ require 'minitest/autorun'
9
+
10
+ require 'webmock/minitest'
11
+
12
+ WebMock.disable_net_connect!
13
+
14
+ Thread.abort_on_exception = true
15
+
16
+ Harness.config.test_mode = true
17
+
18
+ Harness.logger = Logger.new '/dev/null'
19
+
20
+ Harness.redis = Redis::Namespace.new 'harness-test', :redis => Redis.connect(:host => 'localhost', :port => '6379')
21
+
22
+ class IntegrationTest < MiniTest::Unit::TestCase
23
+ def setup
24
+ Harness.config.adapter = :memory
25
+ gauges.clear ; counters.clear
26
+ redis.flushall
27
+ end
28
+
29
+ def assert_gauge_logged(name)
30
+ refute_empty gauges.select {|g| g.name = name }, "Expected #{gauges.inspect} to contain a #{name} result"
31
+ end
32
+
33
+ def assert_counter_logged(name)
34
+ refute_empty counters.select {|c| c.name = name }, "Expected #{counters.inspect} to contain a #{name} result"
35
+ end
36
+
37
+ def gauges
38
+ Harness::MemoryAdapter.gauges
39
+ end
40
+
41
+ def counters
42
+ Harness::MemoryAdapter.counters
43
+ end
44
+
45
+ def redis
46
+ Harness.redis
47
+ end
48
+
49
+ def instrument(name, data = {})
50
+ ActiveSupport::Notifications.instrument name, data do
51
+ # nothing
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,176 @@
1
+ require 'test_helper'
2
+
3
+ class LibratoAdapterTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @adapter = Harness::LibratoAdapter
6
+
7
+ @gauge = Harness::Gauge.new
8
+ @gauge.id = "fake-gauge"
9
+ @gauge.name = "Fake Gauge"
10
+ @gauge.source = "minitest"
11
+ @gauge.time = Time.now
12
+ @gauge.value = 55
13
+
14
+ @counter = Harness::Counter.new
15
+ @counter.id = "fake-counter"
16
+ @counter.name = "Fake Counter"
17
+ @counter.source = "minitest"
18
+ @counter.time = Time.now
19
+ @counter.value = 55
20
+ @counter.units = :bytes
21
+
22
+ Harness::LibratoAdapter.config.email = email
23
+ Harness::LibratoAdapter.config.token = token
24
+ end
25
+
26
+ def teardown
27
+ WebMock.reset!
28
+
29
+ Harness::LibratoAdapter.config.email = nil
30
+ Harness::LibratoAdapter.config.token = nil
31
+ end
32
+
33
+ def test_gauge_is_logged
34
+ json = {
35
+ :gauges => [{
36
+ :name => @gauge.id,
37
+ :display_name => @gauge.name,
38
+ :value => @gauge.value,
39
+ :measure_time => @gauge.time.to_i,
40
+ :source => @gauge.source,
41
+ :attributes => { :display_units_short => @gauge.units }
42
+ }]
43
+ }.to_json
44
+
45
+ expected_request = stub_request(:post, "https://#{email}:#{token}@metrics-api.librato.com/v1/metrics").
46
+ with(:body => json, :headers => {"Content-Type" => "application/json"}).
47
+ to_return(:status => 200)
48
+
49
+ assert @adapter.log_gauge(@gauge)
50
+ assert_requested expected_request
51
+ end
52
+
53
+ def test_gauge_id_is_sanitized
54
+ @gauge.id = "process_action.action_controller"
55
+
56
+ json = {
57
+ :gauges => [{
58
+ :name => "process_action-action_controller",
59
+ :display_name => @gauge.name,
60
+ :value => @gauge.value,
61
+ :measure_time => @gauge.time.to_i,
62
+ :source => @gauge.source,
63
+ :attributes => { :display_units_short => @gauge.units }
64
+ }]
65
+ }.to_json
66
+
67
+ expected_request = stub_request(:post, "https://#{email}:#{token}@metrics-api.librato.com/v1/metrics").
68
+ with(:body => json, :headers => {"Content-Type" => "application/json"}).
69
+ to_return(:status => 200)
70
+
71
+ assert @adapter.log_gauge(@gauge)
72
+ assert_requested expected_request
73
+ end
74
+
75
+
76
+ def test_logging_gague_raises_an_exception
77
+ stub_request(:post, %r{metrics}).to_return(:status => 500, :body => "message")
78
+
79
+ assert_raises Harness::LoggingError do
80
+ @adapter.log_gauge @gauge
81
+ end
82
+ end
83
+
84
+ def test_logging_gauge_raises_an_exception_when_id_is_too_long
85
+ @gauge.id = "f" * 64
86
+
87
+ assert_raises Harness::LoggingError do
88
+ @adapter.log_gauge @gauge
89
+ end
90
+ end
91
+
92
+ def test_logging_gauge_raises_an_exception_when_not_configured
93
+ Harness::LibratoAdapter.config.email = nil
94
+ Harness::LibratoAdapter.config.token = nil
95
+
96
+ assert_raises RuntimeError do
97
+ @adapter.log_gauge @gauge
98
+ end
99
+ end
100
+
101
+ def test_counter_is_logged
102
+ json = {
103
+ :counters => [{
104
+ :name => @counter.id,
105
+ :display_name => @counter.name,
106
+ :value => @counter.value,
107
+ :measure_time => @counter.time.to_i,
108
+ :source => @counter.source,
109
+ :attributes => { :display_units_short => @counter.units }
110
+ }]
111
+ }.to_json
112
+
113
+ expected_request = stub_request(:post, "https://#{email}:#{token}@metrics-api.librato.com/v1/metrics").
114
+ with(:body => json, :headers => {"Content-Type" => "application/json"}).
115
+ to_return(:status => 200)
116
+
117
+ assert @adapter.log_counter(@counter)
118
+ assert_requested expected_request
119
+ end
120
+
121
+ def test_counter_id_is_sanitized
122
+ @counter.id = "total_requests.action_controller"
123
+
124
+ json = {
125
+ :counters => [{
126
+ :name => "total_requests-action_controller",
127
+ :display_name => @counter.name,
128
+ :value => @counter.value,
129
+ :measure_time => @counter.time.to_i,
130
+ :source => @counter.source,
131
+ :attributes => { :display_units_short => @counter.units }
132
+ }]
133
+ }.to_json
134
+
135
+ expected_request = stub_request(:post, "https://#{email}:#{token}@metrics-api.librato.com/v1/metrics").
136
+ with(:body => json, :headers => {"Content-Type" => "application/json"}).
137
+ to_return(:status => 200)
138
+
139
+ assert @adapter.log_counter(@counter)
140
+ assert_requested expected_request
141
+ end
142
+
143
+ def test_logging_counter_raises_an_exception
144
+ stub_request(:post, %r{metrics}).to_return(:status => 500, :body => "message")
145
+
146
+ assert_raises Harness::LoggingError do
147
+ @adapter.log_counter @counter
148
+ end
149
+ end
150
+
151
+ def test_logging_counter_raises_an_exception_when_not_configured
152
+ Harness::LibratoAdapter.config.email = nil
153
+ Harness::LibratoAdapter.config.token = nil
154
+
155
+ assert_raises RuntimeError do
156
+ @adapter.log_counter @counter
157
+ end
158
+ end
159
+
160
+ def test_logging_counter_raises_an_exception_when_id_is_too_long
161
+ @counter.id = "f" * 64
162
+
163
+ assert_raises Harness::LoggingError do
164
+ @adapter.log_counter @counter
165
+ end
166
+ end
167
+
168
+ private
169
+ def email
170
+ 'example@example.com'
171
+ end
172
+
173
+ def token
174
+ 'a-complete-api-token'
175
+ end
176
+ end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class MemoryAdapterTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ Harness::MemoryAdapter.counters.clear
6
+ Harness::MemoryAdapter.gauges.clear
7
+
8
+ @adapter = Harness::MemoryAdapter
9
+ end
10
+
11
+ def test_log_gauge_adds_to_gauges
12
+ @adapter.log_gauge :foo
13
+
14
+ assert_includes @adapter.gauges, :foo
15
+ end
16
+
17
+ def test_log_counter_adds_to_counters
18
+ @adapter.log_counter :bar
19
+
20
+ assert_includes @adapter.counters, :bar
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ require 'test_helper'
2
+
3
+ class CounterTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @counter = Harness::Counter.new
6
+ Harness.redis.flushall
7
+ end
8
+
9
+ def test_sets_id_from_event
10
+ event = ActiveSupport::Notifications::Event.new "name", Time.now, Time.now, nil, {}
11
+
12
+ counter = Harness::Counter.from_event event
13
+
14
+ assert_equal "name", counter.id
15
+ end
16
+
17
+ def tests_sets_source_from_event_payload
18
+ event = ActiveSupport::Notifications::Event.new "name", Time.now, Time.now, nil, :counter => { :source => 'box1' }
19
+
20
+ counter = Harness::Counter.from_event event
21
+
22
+ assert_equal "box1", counter.source
23
+ end
24
+
25
+ def test_sets_value_from_event_payload
26
+ base = Time.now
27
+
28
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, :counter => {:value => 5 }
29
+
30
+ counter = Harness::Counter.from_event event
31
+
32
+ assert_equal 5, counter.value
33
+ end
34
+
35
+ def test_sets_value_from_event_payload_with_number
36
+ base = Time.now
37
+
38
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, :counter => 5
39
+
40
+ counter = Harness::Counter.from_event event
41
+
42
+ assert_equal 5, counter.value
43
+ end
44
+
45
+ def test_sets_name_from_event
46
+ base = Time.now
47
+
48
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, :counter => { :name => 'foo' }
49
+
50
+ counter = Harness::Counter.from_event event
51
+
52
+ assert_equal 'foo', counter.name
53
+ end
54
+
55
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ class GaugeTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @gauge = Harness::Gauge.new
6
+ end
7
+
8
+ def test_initializes_units
9
+ assert_equal :ms, @gauge.units
10
+ end
11
+
12
+ def test_sets_name_id_event
13
+ event = ActiveSupport::Notifications::Event.new "name", Time.now, Time.now, nil, {}
14
+
15
+ gauge = Harness::Gauge.from_event event
16
+
17
+ assert_equal "name", gauge.id
18
+ end
19
+
20
+ def tests_sets_source_from_event_payload
21
+ event = ActiveSupport::Notifications::Event.new "name", Time.now, Time.now, nil, :gauge => { :source => 'box1' }
22
+
23
+ gauge = Harness::Gauge.from_event event
24
+
25
+ assert_equal "box1", gauge.source
26
+ end
27
+
28
+ def test_sets_duration_from_event
29
+ base = Time.now
30
+
31
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, {}
32
+
33
+ gauge = Harness::Gauge.from_event event
34
+
35
+ assert_in_delta 1000, 0.01, gauge.value
36
+ end
37
+
38
+ def test_sets_name_from_event
39
+ base = Time.now
40
+
41
+ event = ActiveSupport::Notifications::Event.new "name", base - 1, Time.now, nil, :gauge => { :name => 'foo' }
42
+
43
+ gauge = Harness::Gauge.from_event event
44
+
45
+ assert_equal 'foo', gauge.name
46
+ end
47
+
48
+ def test_initializes_time_if_not_set
49
+ gauge = Harness::Gauge.new
50
+
51
+ assert gauge.time
52
+ end
53
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ class HarnessModuleTest < MiniTest::Unit::TestCase
4
+ def test_can_set_the_adapter_with_a_symbol
5
+ Harness.config.adapter = :memory
6
+
7
+ assert_equal Harness::MemoryAdapter, Harness.config.adapter
8
+ end
9
+
10
+ def test_can_set_the_adapter_with_a_class
11
+ Harness.config.adapter = Harness::MemoryAdapter
12
+
13
+ assert_equal Harness::MemoryAdapter, Harness.config.adapter
14
+ end
15
+
16
+ def test_uses_method_missing_to_configure_adapters
17
+ Harness.config.librato.email = 'foo'
18
+
19
+ assert_equal 'foo', Harness.config.librato.email
20
+ end
21
+
22
+ def test_blows_up_when_calling_an_unknown_adapter
23
+ assert_raises NoMethodError do
24
+ Harness.config.foo_bar
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class MeasurementTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @measurement = Harness::Measurement.new
6
+ end
7
+
8
+ def test_has_an_id_attribute
9
+ assert @measurement.respond_to?(:id)
10
+ assert @measurement.respond_to?(:id=)
11
+ end
12
+
13
+ def test_has_a_source_attribute
14
+ assert @measurement.respond_to?(:source)
15
+ assert @measurement.respond_to?(:source=)
16
+ end
17
+
18
+ def test_has_a_time_attribute
19
+ assert @measurement.respond_to?(:time)
20
+ assert @measurement.respond_to?(:time=)
21
+ end
22
+
23
+ def test_has_a_value_attribute
24
+ assert @measurement.respond_to?(:value)
25
+ assert @measurement.respond_to?(:value=)
26
+ end
27
+
28
+ def test_has_a_name_attribute
29
+ assert @measurement.respond_to?(:name)
30
+ assert @measurement.respond_to?(:name=)
31
+ end
32
+
33
+ def test_initializes_time
34
+ assert @measurement.time
35
+ end
36
+ end