codeclimate-services 0.3.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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +121 -0
  8. data/Rakefile +11 -0
  9. data/bin/bundler +16 -0
  10. data/bin/coderay +16 -0
  11. data/bin/nokogiri +16 -0
  12. data/bin/pry +16 -0
  13. data/bin/rake +16 -0
  14. data/codeclimate-services.gemspec +29 -0
  15. data/config/cacert.pem +4026 -0
  16. data/config/load.rb +4 -0
  17. data/lib/axiom/types/password.rb +7 -0
  18. data/lib/cc/formatters/linked_formatter.rb +60 -0
  19. data/lib/cc/formatters/plain_formatter.rb +36 -0
  20. data/lib/cc/formatters/snapshot_formatter.rb +101 -0
  21. data/lib/cc/formatters/ticket_formatter.rb +27 -0
  22. data/lib/cc/helpers/coverage_helper.rb +25 -0
  23. data/lib/cc/helpers/issue_helper.rb +9 -0
  24. data/lib/cc/helpers/quality_helper.rb +44 -0
  25. data/lib/cc/helpers/vulnerability_helper.rb +31 -0
  26. data/lib/cc/presenters/github_pull_requests_presenter.rb +54 -0
  27. data/lib/cc/service/config.rb +4 -0
  28. data/lib/cc/service/formatter.rb +34 -0
  29. data/lib/cc/service/helper.rb +54 -0
  30. data/lib/cc/service/http.rb +87 -0
  31. data/lib/cc/service/invocation/invocation_chain.rb +15 -0
  32. data/lib/cc/service/invocation/with_error_handling.rb +45 -0
  33. data/lib/cc/service/invocation/with_metrics.rb +37 -0
  34. data/lib/cc/service/invocation/with_retries.rb +17 -0
  35. data/lib/cc/service/invocation/with_return_values.rb +18 -0
  36. data/lib/cc/service/invocation.rb +57 -0
  37. data/lib/cc/service/response_check.rb +42 -0
  38. data/lib/cc/service.rb +127 -0
  39. data/lib/cc/services/asana.rb +90 -0
  40. data/lib/cc/services/campfire.rb +55 -0
  41. data/lib/cc/services/flowdock.rb +61 -0
  42. data/lib/cc/services/github_issues.rb +80 -0
  43. data/lib/cc/services/github_pull_requests.rb +210 -0
  44. data/lib/cc/services/hipchat.rb +57 -0
  45. data/lib/cc/services/jira.rb +93 -0
  46. data/lib/cc/services/lighthouse.rb +79 -0
  47. data/lib/cc/services/pivotal_tracker.rb +78 -0
  48. data/lib/cc/services/slack.rb +124 -0
  49. data/lib/cc/services/version.rb +5 -0
  50. data/lib/cc/services.rb +9 -0
  51. data/pull_request_test.rb +47 -0
  52. data/service_test.rb +86 -0
  53. data/test/asana_test.rb +85 -0
  54. data/test/axiom/types/password_test.rb +22 -0
  55. data/test/campfire_test.rb +144 -0
  56. data/test/fixtures.rb +68 -0
  57. data/test/flowdock_test.rb +148 -0
  58. data/test/formatters/snapshot_formatter_test.rb +47 -0
  59. data/test/github_issues_test.rb +96 -0
  60. data/test/github_pull_requests_test.rb +293 -0
  61. data/test/helper.rb +50 -0
  62. data/test/hipchat_test.rb +130 -0
  63. data/test/invocation_error_handling_test.rb +51 -0
  64. data/test/invocation_return_values_test.rb +21 -0
  65. data/test/invocation_test.rb +167 -0
  66. data/test/jira_test.rb +80 -0
  67. data/test/lighthouse_test.rb +74 -0
  68. data/test/pivotal_tracker_test.rb +73 -0
  69. data/test/presenters/github_pull_requests_presenter_test.rb +49 -0
  70. data/test/service_test.rb +63 -0
  71. data/test/slack_test.rb +222 -0
  72. data/test/support/fake_logger.rb +11 -0
  73. data/test/with_metrics_test.rb +19 -0
  74. metadata +263 -0
@@ -0,0 +1,167 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class TestInvocation < Test::Unit::TestCase
4
+ def test_success
5
+ service = FakeService.new(:some_result)
6
+
7
+ result = CC::Service::Invocation.invoke(service)
8
+
9
+ assert_equal 1, service.receive_count
10
+ assert_equal :some_result, result
11
+ end
12
+
13
+ def test_success_with_return_values
14
+ service = FakeService.new(:some_result)
15
+
16
+ result = CC::Service::Invocation.invoke(service) do |i|
17
+ i.with :return_values, "error"
18
+ end
19
+
20
+ assert_equal 1, service.receive_count
21
+ assert_equal :some_result, result
22
+ end
23
+
24
+ def test_failure_with_return_values
25
+ service = FakeService.new(nil)
26
+
27
+ result = CC::Service::Invocation.invoke(service) do |i|
28
+ i.with :return_values, "error"
29
+ end
30
+
31
+ assert_equal 1, service.receive_count
32
+ assert_equal( {ok: false, message: "error"}, result )
33
+ end
34
+
35
+ def test_retries
36
+ service = FakeService.new
37
+ service.fake_error = RuntimeError.new
38
+ error_occurred = false
39
+
40
+ begin
41
+ CC::Service::Invocation.invoke(service) do |i|
42
+ i.with :retries, 3
43
+ end
44
+ rescue
45
+ error_occurred = true
46
+ end
47
+
48
+ assert error_occurred
49
+ assert_equal 1 + 3, service.receive_count
50
+ end
51
+
52
+ def test_metrics
53
+ statsd = FakeStatsd.new
54
+
55
+ CC::Service::Invocation.invoke(FakeService.new) do |i|
56
+ i.with :metrics, statsd, "a_prefix"
57
+ end
58
+
59
+ assert_equal 1, statsd.incremented_keys.length
60
+ assert_equal "services.invocations.a_prefix", statsd.incremented_keys.first
61
+ end
62
+
63
+ def test_metrics_on_errors
64
+ statsd = FakeStatsd.new
65
+ service = FakeService.new
66
+ service.fake_error = RuntimeError.new
67
+ error_occurred = false
68
+
69
+ begin
70
+ CC::Service::Invocation.invoke(service) do |i|
71
+ i.with :metrics, statsd, "a_prefix"
72
+ end
73
+ rescue
74
+ error_occurred = true
75
+ end
76
+
77
+ assert error_occurred
78
+ assert_equal 1, statsd.incremented_keys.length
79
+ assert_match /^services\.errors\.a_prefix/, statsd.incremented_keys.first
80
+ end
81
+
82
+ def test_user_message
83
+ service = FakeService.new
84
+ service.fake_error = CC::Service::HTTPError.new("Boom", {})
85
+ service.override_user_message = "Hey do this"
86
+ logger = FakeLogger.new
87
+
88
+ result = CC::Service::Invocation.invoke(service) do |i|
89
+ i.with :error_handling, logger, "a_prefix"
90
+ end
91
+
92
+ assert_equal "Hey do this", result[:message]
93
+ assert_match /Boom/, result[:log_message]
94
+ end
95
+
96
+ def test_error_handling
97
+ service = FakeService.new
98
+ service.fake_error = RuntimeError.new("Boom")
99
+ logger = FakeLogger.new
100
+
101
+ result = CC::Service::Invocation.invoke(service) do |i|
102
+ i.with :error_handling, logger, "a_prefix"
103
+ end
104
+
105
+ assert_equal({ok: false, message: "Boom", log_message: "Exception invoking service: [a_prefix] (RuntimeError) Boom"}, result)
106
+ assert_equal 1, logger.logged_errors.length
107
+ assert_match /^Exception invoking service: \[a_prefix\]/, logger.logged_errors.first
108
+ end
109
+
110
+ def test_multiple_middleware
111
+ service = FakeService.new
112
+ service.fake_error = RuntimeError.new("Boom")
113
+ logger = FakeLogger.new
114
+
115
+ result = CC::Service::Invocation.invoke(service) do |i|
116
+ i.with :retries, 3
117
+ i.with :error_handling, logger
118
+ end
119
+
120
+ assert_equal({ok: false, message: "Boom", log_message: "Exception invoking service: (RuntimeError) Boom"}, result)
121
+ assert_equal 1 + 3, service.receive_count
122
+ assert_equal 1, logger.logged_errors.length
123
+ end
124
+
125
+ private
126
+
127
+ class FakeService
128
+ attr_reader :receive_count
129
+ attr_accessor :fake_error, :override_user_message
130
+
131
+ def initialize(result = nil)
132
+ @result = result
133
+ @receive_count = 0
134
+ end
135
+
136
+ def receive
137
+ @receive_count += 1
138
+
139
+ begin
140
+ raise @fake_error if @fake_error
141
+ rescue => e
142
+ if override_user_message
143
+ e.user_message = override_user_message
144
+ end
145
+ raise e
146
+ end
147
+
148
+ @result
149
+ end
150
+ end
151
+
152
+ class FakeStatsd
153
+ attr_reader :incremented_keys
154
+
155
+ def initialize
156
+ @incremented_keys = Set.new
157
+ end
158
+
159
+ def increment(key)
160
+ @incremented_keys << key
161
+ end
162
+
163
+ def timing(key, value)
164
+ end
165
+ end
166
+
167
+ end
data/test/jira_test.rb ADDED
@@ -0,0 +1,80 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class TestJira < CC::Service::TestCase
4
+ def test_successful_receive
5
+ response = assert_jira_receives(
6
+ event(:quality, to: "D", from: "C"),
7
+ "Refactor User from a D on Code Climate",
8
+ "https://codeclimate.com/repos/1/feed"
9
+ )
10
+ assert_equal "10000", response[:id]
11
+ end
12
+
13
+ def test_quality
14
+ assert_jira_receives(
15
+ event(:quality, to: "D", from: "C"),
16
+ "Refactor User from a D on Code Climate",
17
+ "https://codeclimate.com/repos/1/feed"
18
+ )
19
+ end
20
+
21
+ def test_vulnerability
22
+ assert_jira_receives(
23
+ event(:vulnerability, vulnerabilities: [{
24
+ "warning_type" => "critical",
25
+ "location" => "app/user.rb line 120"
26
+ }]),
27
+ "New critical issue found in app/user.rb line 120",
28
+ "A critical vulnerability was found by Code Climate in app/user.rb line 120.\n\nhttps://codeclimate.com/repos/1/feed"
29
+ )
30
+ end
31
+
32
+ def test_issue
33
+ payload = {
34
+ issue: {
35
+ "check_name" => "Style/LongLine",
36
+ "description" => "Line is too long [1000/80]"
37
+ },
38
+ constant_name: "foo.rb",
39
+ details_url: "http://example.com/repos/id/foo.rb#issue_123"
40
+ }
41
+
42
+ assert_jira_receives(
43
+ event(:issue, payload),
44
+ "Fix \"Style/LongLine\" issue in foo.rb",
45
+ "Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123"
46
+ )
47
+ end
48
+
49
+ def test_receive_test
50
+ @stubs.post '/rest/api/2/issue' do |env|
51
+ [200, {}, '{"id": 12345, "key": "CC-123", "self": "http://foo.bar"}']
52
+ end
53
+
54
+ response = receive_event(name: "test")
55
+
56
+ assert_equal "Ticket <a href='http://foo.bar'>12345</a> created.", response[:message]
57
+ end
58
+
59
+ private
60
+
61
+ def assert_jira_receives(event_data, title, ticket_body)
62
+ @stubs.post '/rest/api/2/issue' do |env|
63
+ body = JSON.parse(env[:body])
64
+ assert_equal "Basic Zm9vOmJhcg==", env[:request_headers]["Authorization"]
65
+ assert_equal title, body["fields"]["summary"]
66
+ assert_equal ticket_body, body["fields"]["description"]
67
+ [200, {}, '{"id":"10000"}']
68
+ end
69
+
70
+ receive_event(event_data)
71
+ end
72
+
73
+ def receive_event(event_data = nil)
74
+ receive(
75
+ CC::Service::Jira,
76
+ { domain: "foo.com", username: "foo", password: "bar", project_id: "100" },
77
+ event_data || event(:quality, from: "C", to: "D")
78
+ )
79
+ end
80
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class TestLighthouse < CC::Service::TestCase
4
+ def test_quality
5
+ response = assert_lighthouse_receives(
6
+ event(:quality, to: "D", from: "C"),
7
+ "Refactor User from a D on Code Climate",
8
+ "https://codeclimate.com/repos/1/feed"
9
+ )
10
+ assert_equal "123", response[:id]
11
+ assert_equal "http://lighthouse.com/projects/123/tickets/123.json",
12
+ response[:url]
13
+ end
14
+
15
+ def test_vulnerability
16
+ assert_lighthouse_receives(
17
+ event(:vulnerability, vulnerabilities: [{
18
+ "warning_type" => "critical",
19
+ "location" => "app/user.rb line 120"
20
+ }]),
21
+ "New critical issue found in app/user.rb line 120",
22
+ "A critical vulnerability was found by Code Climate in app/user.rb line 120.\n\nhttps://codeclimate.com/repos/1/feed"
23
+ )
24
+ end
25
+
26
+ def test_issue
27
+ payload = {
28
+ issue: {
29
+ "check_name" => "Style/LongLine",
30
+ "description" => "Line is too long [1000/80]"
31
+ },
32
+ constant_name: "foo.rb",
33
+ details_url: "http://example.com/repos/id/foo.rb#issue_123"
34
+ }
35
+
36
+ assert_lighthouse_receives(
37
+ event(:issue, payload),
38
+ "Fix \"Style/LongLine\" issue in foo.rb",
39
+ "Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123"
40
+ )
41
+ end
42
+
43
+ def test_receive_test
44
+ @stubs.post 'projects/123/tickets.json' do |env|
45
+ [200, {}, '{"ticket":{"number": "123", "url":"http://foo.bar"}}']
46
+ end
47
+
48
+ response = receive_event(name: "test")
49
+
50
+ assert_equal "Ticket <a href='http://foo.bar'>123</a> created.", response[:message]
51
+ end
52
+
53
+ private
54
+
55
+ def assert_lighthouse_receives(event_data, title, ticket_body)
56
+ @stubs.post 'projects/123/tickets.json' do |env|
57
+ body = JSON.parse(env[:body])
58
+ assert_equal "token", env[:request_headers]["X-LighthouseToken"]
59
+ assert_equal title, body["ticket"]["title"]
60
+ assert_equal ticket_body, body["ticket"]["body"]
61
+ [200, {}, '{"ticket":{"number": "123", "url":"http://lighthouse.com/projects/123/tickets/123.json"}}']
62
+ end
63
+
64
+ receive_event(event_data)
65
+ end
66
+
67
+ def receive_event(event_data = nil)
68
+ receive(
69
+ CC::Service::Lighthouse,
70
+ { subdomain: "foo", api_token: "token", project_id: "123" },
71
+ event_data || event(:quality, from: "C", to: "D")
72
+ )
73
+ end
74
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class TestPivotalTracker < CC::Service::TestCase
4
+ def test_quality
5
+ response = assert_pivotal_receives(
6
+ event(:quality, to: "D", from: "C"),
7
+ "Refactor User from a D on Code Climate",
8
+ "https://codeclimate.com/repos/1/feed"
9
+ )
10
+ assert_equal "123", response[:id]
11
+ assert_equal "http://pivotaltracker.com/n/projects/123/stories/123",
12
+ response[:url]
13
+ end
14
+
15
+ def test_vulnerability
16
+ assert_pivotal_receives(
17
+ event(:vulnerability, vulnerabilities: [{
18
+ "warning_type" => "critical",
19
+ "location" => "app/user.rb line 120"
20
+ }]),
21
+ "New critical issue found in app/user.rb line 120",
22
+ "A critical vulnerability was found by Code Climate in app/user.rb line 120.\n\nhttps://codeclimate.com/repos/1/feed"
23
+ )
24
+ end
25
+
26
+ def test_issue
27
+ payload = {
28
+ issue: {
29
+ "check_name" => "Style/LongLine",
30
+ "description" => "Line is too long [1000/80]"
31
+ },
32
+ constant_name: "foo.rb",
33
+ details_url: "http://example.com/repos/id/foo.rb#issue_123"
34
+ }
35
+
36
+ assert_pivotal_receives(
37
+ event(:issue, payload),
38
+ "Fix \"Style/LongLine\" issue in foo.rb",
39
+ "Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123"
40
+ )
41
+ end
42
+
43
+ def test_receive_test
44
+ @stubs.post 'services/v3/projects/123/stories' do |env|
45
+ [200, {}, '<story><id>123</id><url>http://foo.bar</url></story>']
46
+ end
47
+
48
+ response = receive_event(name: "test")
49
+
50
+ assert_equal "Ticket <a href='http://foo.bar'>123</a> created.", response[:message]
51
+ end
52
+
53
+ private
54
+
55
+ def assert_pivotal_receives(event_data, name, description)
56
+ @stubs.post 'services/v3/projects/123/stories' do |env|
57
+ body = Hash[URI.decode_www_form(env[:body])]
58
+ assert_equal "token", env[:request_headers]["X-TrackerToken"]
59
+ assert_equal name, body["story[name]"]
60
+ assert_equal description, body["story[description]"]
61
+ [200, {}, '<doc><story><id>123</id><url>http://pivotaltracker.com/n/projects/123/stories/123</url></story></doc>']
62
+ end
63
+ receive_event(event_data)
64
+ end
65
+
66
+ def receive_event(event_data = nil)
67
+ receive(
68
+ CC::Service::PivotalTracker,
69
+ { api_token: "token", project_id: "123" },
70
+ event_data || event(:quality, from: "C", to: "D")
71
+ )
72
+ end
73
+ end
@@ -0,0 +1,49 @@
1
+ require "helper"
2
+ require "cc/presenters/github_pull_requests_presenter"
3
+
4
+ class TestGitHubPullRequestsPresenter < CC::Service::TestCase
5
+ def test_message_singular
6
+ assert_equal(
7
+ "Code Climate found 1 new issue and 1 fixed issue.",
8
+ build_presenter("fixed" => 1, "new" => 1).success_message
9
+ )
10
+ end
11
+
12
+ def test_message_plural
13
+ assert_equal(
14
+ "Code Climate found 2 new issues and 1 fixed issue.",
15
+ build_presenter("fixed" => 1, "new" => 2).success_message
16
+ )
17
+ end
18
+
19
+ def test_message_only_fixed
20
+ assert_equal(
21
+ "Code Climate found 1 fixed issue.",
22
+ build_presenter("fixed" => 1, "new" => 0).success_message
23
+ )
24
+ end
25
+
26
+ def test_message_only_new
27
+ assert_equal(
28
+ "Code Climate found 3 new issues.",
29
+ build_presenter("fixed" => 0, "new" => 3).success_message
30
+ )
31
+ end
32
+
33
+ def test_message_no_new_or_fixed
34
+ assert_equal(
35
+ "Code Climate didn't find any new or fixed issues.",
36
+ build_presenter("fixed" => 0, "new" => 0).success_message
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ def build_payload(issue_counts)
43
+ { "issue_comparison_counts" => issue_counts }
44
+ end
45
+
46
+ def build_presenter(issue_counts)
47
+ CC::Service::GitHubPullRequestsPresenter.new(build_payload(issue_counts))
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path("../helper", __FILE__)
2
+
3
+ class TestService < CC::Service::TestCase
4
+ def test_validates_events
5
+ assert_raises(ArgumentError) do
6
+ CC::Service.new(:foo, {}, {}, {})
7
+ end
8
+ end
9
+
10
+ def test_default_path_to_ca_file
11
+ s = CC::Service.new({}, {name: "test"})
12
+ assert_equal(File.expand_path("../../config/cacert.pem", __FILE__), s.ca_file)
13
+ assert File.exist?(s.ca_file)
14
+ end
15
+
16
+ def test_custom_path_to_ca_file
17
+ ENV["CODECLIMATE_CA_FILE"] = "/tmp/cacert.pem"
18
+ s = CC::Service.new({}, {name: "test"})
19
+ assert_equal("/tmp/cacert.pem", s.ca_file)
20
+ ensure
21
+ ENV.delete("CODECLIMATE_CA_FILE")
22
+ end
23
+
24
+ def test_nothing_has_a_handler
25
+ service = CC::Service.new({}, {name: "test"})
26
+
27
+ result = service.receive
28
+
29
+ assert_equal false, result[:ok]
30
+ assert_true true, result[:ignored]
31
+ assert_equal "No service handler found", result[:message]
32
+ end
33
+
34
+ def test_post_success
35
+ stub_http("/my/test/url", [200, {}, '{"ok": true, "thing": "123"}'])
36
+
37
+ response = service_post("/my/test/url", {token: "1234"}.to_json, {}) do |response|
38
+ body = JSON.parse(response.body)
39
+ { thing: body["thing"] }
40
+ end
41
+
42
+ assert_true response[:ok]
43
+ assert_equal '{"token":"1234"}', response[:params]
44
+ assert_equal "/my/test/url", response[:endpoint_url]
45
+ assert_equal 200, response[:status]
46
+ end
47
+
48
+ def test_post_http_failure
49
+ stub_http("/my/wrong/url", [404, {}, ""])
50
+
51
+ assert_raises(CC::Service::HTTPError) do
52
+ service_post("/my/wrong/url", {token: "1234"}.to_json, {})
53
+ end
54
+ end
55
+
56
+ def test_post_some_other_failure
57
+ stub_http("/my/wrong/url"){ raise ArgumentError.new("lol") }
58
+
59
+ assert_raises(ArgumentError) do
60
+ service_post("/my/wrong/url", {token: "1234"}.to_json, {})
61
+ end
62
+ end
63
+ end