codeclimate-services 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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