codeclimate-services 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +121 -0
- data/Rakefile +11 -0
- data/bin/bundler +16 -0
- data/bin/coderay +16 -0
- data/bin/nokogiri +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/codeclimate-services.gemspec +29 -0
- data/config/cacert.pem +4026 -0
- data/config/load.rb +4 -0
- data/lib/axiom/types/password.rb +7 -0
- data/lib/cc/formatters/linked_formatter.rb +60 -0
- data/lib/cc/formatters/plain_formatter.rb +36 -0
- data/lib/cc/formatters/snapshot_formatter.rb +101 -0
- data/lib/cc/formatters/ticket_formatter.rb +27 -0
- data/lib/cc/helpers/coverage_helper.rb +25 -0
- data/lib/cc/helpers/issue_helper.rb +9 -0
- data/lib/cc/helpers/quality_helper.rb +44 -0
- data/lib/cc/helpers/vulnerability_helper.rb +31 -0
- data/lib/cc/presenters/github_pull_requests_presenter.rb +54 -0
- data/lib/cc/service/config.rb +4 -0
- data/lib/cc/service/formatter.rb +34 -0
- data/lib/cc/service/helper.rb +54 -0
- data/lib/cc/service/http.rb +87 -0
- data/lib/cc/service/invocation/invocation_chain.rb +15 -0
- data/lib/cc/service/invocation/with_error_handling.rb +45 -0
- data/lib/cc/service/invocation/with_metrics.rb +37 -0
- data/lib/cc/service/invocation/with_retries.rb +17 -0
- data/lib/cc/service/invocation/with_return_values.rb +18 -0
- data/lib/cc/service/invocation.rb +57 -0
- data/lib/cc/service/response_check.rb +42 -0
- data/lib/cc/service.rb +127 -0
- data/lib/cc/services/asana.rb +90 -0
- data/lib/cc/services/campfire.rb +55 -0
- data/lib/cc/services/flowdock.rb +61 -0
- data/lib/cc/services/github_issues.rb +80 -0
- data/lib/cc/services/github_pull_requests.rb +210 -0
- data/lib/cc/services/hipchat.rb +57 -0
- data/lib/cc/services/jira.rb +93 -0
- data/lib/cc/services/lighthouse.rb +79 -0
- data/lib/cc/services/pivotal_tracker.rb +78 -0
- data/lib/cc/services/slack.rb +124 -0
- data/lib/cc/services/version.rb +5 -0
- data/lib/cc/services.rb +9 -0
- data/pull_request_test.rb +47 -0
- data/service_test.rb +86 -0
- data/test/asana_test.rb +85 -0
- data/test/axiom/types/password_test.rb +22 -0
- data/test/campfire_test.rb +144 -0
- data/test/fixtures.rb +68 -0
- data/test/flowdock_test.rb +148 -0
- data/test/formatters/snapshot_formatter_test.rb +47 -0
- data/test/github_issues_test.rb +96 -0
- data/test/github_pull_requests_test.rb +293 -0
- data/test/helper.rb +50 -0
- data/test/hipchat_test.rb +130 -0
- data/test/invocation_error_handling_test.rb +51 -0
- data/test/invocation_return_values_test.rb +21 -0
- data/test/invocation_test.rb +167 -0
- data/test/jira_test.rb +80 -0
- data/test/lighthouse_test.rb +74 -0
- data/test/pivotal_tracker_test.rb +73 -0
- data/test/presenters/github_pull_requests_presenter_test.rb +49 -0
- data/test/service_test.rb +63 -0
- data/test/slack_test.rb +222 -0
- data/test/support/fake_logger.rb +11 -0
- data/test/with_metrics_test.rb +19 -0
- metadata +263 -0
data/service_test.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Ad-hoc script for sending the test event to service classes
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
#
|
7
|
+
# $ <SERVICE>_<CONFIG_ATTR_1>="..." \
|
8
|
+
# <SERVICE>_<CONFIG_ATTR_2>="..." \
|
9
|
+
# ... ... bundle exec ruby service_test.rb
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# $ SLACK_WEBHOOK_URL="http://..." bundle exec ruby service_test.rb
|
14
|
+
# $ GITHUBPULLREQUESTS_UPDATE_STATUS=false GITHUBPULLREQUESTS_ADD_COMMENT=true GITHUBPULLREQUESTS_OAUTH_TOKEN=06083a4a060d358ca709939b1f00645777661c44 bundle exec ruby service_test.rb
|
15
|
+
#
|
16
|
+
# Other Environment variables used:
|
17
|
+
#
|
18
|
+
# REPO_NAME Defaults to "App"
|
19
|
+
#
|
20
|
+
###
|
21
|
+
require 'cc/services'
|
22
|
+
CC::Service.load_services
|
23
|
+
|
24
|
+
class WithResponseLogging
|
25
|
+
def initialize(invocation)
|
26
|
+
@invocation = invocation
|
27
|
+
end
|
28
|
+
|
29
|
+
def call
|
30
|
+
@invocation.call.tap { |r| p r }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ServiceTest
|
35
|
+
def initialize(klass, *params)
|
36
|
+
@klass = klass
|
37
|
+
@params = params
|
38
|
+
end
|
39
|
+
|
40
|
+
def test(payload = {})
|
41
|
+
config = {}
|
42
|
+
|
43
|
+
puts "-"*80
|
44
|
+
puts @klass
|
45
|
+
|
46
|
+
@params.each do |param|
|
47
|
+
if var = ENV[to_env_var(param)]
|
48
|
+
config[param] = var
|
49
|
+
else
|
50
|
+
puts " -> skipping"
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
puts " -> testing"
|
56
|
+
puts " -> #{config.inspect}"
|
57
|
+
print " => "
|
58
|
+
|
59
|
+
test_service(@klass, config, payload)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def to_env_var(param)
|
65
|
+
"#{@klass.to_s.split("::").last}_#{param}".upcase
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_service(klass, config, payload)
|
69
|
+
repo_name = ENV["REPO_NAME"] || "App"
|
70
|
+
|
71
|
+
service = klass.new(
|
72
|
+
config,
|
73
|
+
{ name: :test, repo_name: repo_name }.merge(payload)
|
74
|
+
)
|
75
|
+
|
76
|
+
CC::Service::Invocation.new(service) do |i|
|
77
|
+
i.wrap(WithResponseLogging)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
ServiceTest.new(CC::Service::Slack, :webhook_url).test
|
83
|
+
ServiceTest.new(CC::Service::Flowdock, :api_token).test
|
84
|
+
ServiceTest.new(CC::Service::Jira, :username, :password, :domain, :project_id).test
|
85
|
+
ServiceTest.new(CC::Service::Asana, :api_key, :workspace_id, :project_id).test
|
86
|
+
ServiceTest.new(CC::Service::GitHubPullRequests, :oauth_token, :update_status, :add_comment).test({ github_slug: "codeclimate/codeclimate" })
|
data/test/asana_test.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestAsana < CC::Service::TestCase
|
4
|
+
def test_quality
|
5
|
+
assert_asana_receives(
|
6
|
+
event(:quality, to: "D", from: "C"),
|
7
|
+
"Refactor User from a D on Code Climate - https://codeclimate.com/repos/1/feed"
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_vulnerability
|
12
|
+
assert_asana_receives(
|
13
|
+
event(:vulnerability, vulnerabilities: [{
|
14
|
+
"warning_type" => "critical",
|
15
|
+
"location" => "app/user.rb line 120"
|
16
|
+
}]),
|
17
|
+
"New critical issue found in app/user.rb line 120 - https://codeclimate.com/repos/1/feed"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_issue
|
22
|
+
payload = {
|
23
|
+
issue: {
|
24
|
+
"check_name" => "Style/LongLine",
|
25
|
+
"description" => "Line is too long [1000/80]"
|
26
|
+
},
|
27
|
+
constant_name: "foo.rb",
|
28
|
+
details_url: "http://example.com/repos/id/foo.rb#issue_123"
|
29
|
+
}
|
30
|
+
|
31
|
+
assert_asana_receives(
|
32
|
+
event(:issue, payload),
|
33
|
+
"Fix \"Style/LongLine\" issue in foo.rb",
|
34
|
+
"Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_successful_post
|
39
|
+
@stubs.post '/api/1.0/tasks' do |env|
|
40
|
+
[200, {}, '{"data":{"id":"2"}}']
|
41
|
+
end
|
42
|
+
|
43
|
+
response = receive_event
|
44
|
+
|
45
|
+
assert_equal "2", response[:id]
|
46
|
+
assert_equal "https://app.asana.com/0/1/2", response[:url]
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_receive_test
|
50
|
+
@stubs.post '/api/1.0/tasks' do |env|
|
51
|
+
[200, {}, '{"data":{"id":"4"}}']
|
52
|
+
end
|
53
|
+
|
54
|
+
response = receive_event(name: "test")
|
55
|
+
|
56
|
+
assert_equal "Ticket <a href='https://app.asana.com/0/1/4'>4</a> created.", response[:message]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def assert_asana_receives(event_data, name, notes = nil)
|
62
|
+
@stubs.post '/api/1.0/tasks' do |env|
|
63
|
+
body = JSON.parse(env[:body])
|
64
|
+
data = body["data"]
|
65
|
+
|
66
|
+
assert_equal "1", data["workspace"]
|
67
|
+
assert_equal "2", data["projects"].first
|
68
|
+
assert_equal "jim@asana.com", data["assignee"]
|
69
|
+
assert_equal name, data["name"]
|
70
|
+
assert_equal notes, data["notes"]
|
71
|
+
|
72
|
+
[200, {}, '{"data":{"id":4}}']
|
73
|
+
end
|
74
|
+
|
75
|
+
receive_event(event_data)
|
76
|
+
end
|
77
|
+
|
78
|
+
def receive_event(event_data = nil)
|
79
|
+
receive(
|
80
|
+
CC::Service::Asana,
|
81
|
+
{ api_key: "abc123", workspace_id: "1", project_id: "2", assignee: "jim@asana.com" },
|
82
|
+
event_data || event(:quality, to: "D", from: "C")
|
83
|
+
)
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../../../helper', __FILE__)
|
2
|
+
|
3
|
+
class Axiom::Types::PasswordTest < CC::Service::TestCase
|
4
|
+
class TestConfiguration < CC::Service::Config
|
5
|
+
attribute :password_attribute, Password
|
6
|
+
attribute :string_attribute, String
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_password_type_inference
|
10
|
+
assert_equal(
|
11
|
+
Axiom::Types::Password,
|
12
|
+
TestConfiguration.attribute_set[:password_attribute].type
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_string_type_inference
|
17
|
+
assert_equal(
|
18
|
+
Axiom::Types::String,
|
19
|
+
TestConfiguration.attribute_set[:string_attribute].type
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestCampfire < CC::Service::TestCase
|
4
|
+
def test_config
|
5
|
+
assert_raises CC::Service::ConfigurationError do
|
6
|
+
service(CC::Service::Campfire, {}, {name: "test"})
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_test_hook
|
11
|
+
assert_campfire_receives(
|
12
|
+
{ name: "test", repo_name: "Rails" },
|
13
|
+
"[Code Climate][Rails] This is a test of the Campfire service hook"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_coverage_improved
|
18
|
+
e = event(:coverage, to: 90.2, from: 80)
|
19
|
+
|
20
|
+
assert_campfire_receives(e, [
|
21
|
+
"[Code Climate][Example] :sunny:",
|
22
|
+
"Test coverage has improved to 90.2% (+10.2%).",
|
23
|
+
"(https://codeclimate.com/repos/1/feed)"
|
24
|
+
].join(" "))
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_coverage_declined
|
28
|
+
e = event(:coverage, to: 88.6, from: 94.6)
|
29
|
+
|
30
|
+
assert_campfire_receives(e, [
|
31
|
+
"[Code Climate][Example] :umbrella:",
|
32
|
+
"Test coverage has declined to 88.6% (-6.0%).",
|
33
|
+
"(https://codeclimate.com/repos/1/feed)"
|
34
|
+
].join(" "))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_quality_improved
|
38
|
+
e = event(:quality, to: "A", from: "B")
|
39
|
+
|
40
|
+
assert_campfire_receives(e, [
|
41
|
+
"[Code Climate][Example] :sunny:",
|
42
|
+
"User has improved from a B to an A.",
|
43
|
+
"(https://codeclimate.com/repos/1/feed)"
|
44
|
+
].join(" "))
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_quality_declined
|
48
|
+
e = event(:quality, to: "D", from: "C")
|
49
|
+
|
50
|
+
assert_campfire_receives(e, [
|
51
|
+
"[Code Climate][Example] :umbrella:",
|
52
|
+
"User has declined from a C to a D.",
|
53
|
+
"(https://codeclimate.com/repos/1/feed)"
|
54
|
+
].join(" "))
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_single_vulnerability
|
58
|
+
e = event(:vulnerability, vulnerabilities: [
|
59
|
+
{ "warning_type" => "critical" }
|
60
|
+
])
|
61
|
+
|
62
|
+
assert_campfire_receives(e, [
|
63
|
+
"[Code Climate][Example]",
|
64
|
+
"New critical issue found.",
|
65
|
+
"Details: https://codeclimate.com/repos/1/feed"
|
66
|
+
].join(" "))
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_single_vulnerability_with_location
|
70
|
+
e = event(:vulnerability, vulnerabilities: [{
|
71
|
+
"warning_type" => "critical",
|
72
|
+
"location" => "app/user.rb line 120"
|
73
|
+
}])
|
74
|
+
|
75
|
+
assert_campfire_receives(e, [
|
76
|
+
"[Code Climate][Example]",
|
77
|
+
"New critical issue found",
|
78
|
+
"in app/user.rb line 120.",
|
79
|
+
"Details: https://codeclimate.com/repos/1/feed"
|
80
|
+
].join(" "))
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_multiple_vulnerabilities
|
84
|
+
e = event(:vulnerability, warning_type: "critical", vulnerabilities: [{
|
85
|
+
"warning_type" => "unused",
|
86
|
+
"location" => "unused"
|
87
|
+
}, {
|
88
|
+
"warning_type" => "unused",
|
89
|
+
"location" => "unused"
|
90
|
+
}])
|
91
|
+
|
92
|
+
assert_campfire_receives(e, [
|
93
|
+
"[Code Climate][Example]",
|
94
|
+
"2 new critical issues found.",
|
95
|
+
"Details: https://codeclimate.com/repos/1/feed"
|
96
|
+
].join(" "))
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_receive_test
|
100
|
+
@stubs.post request_url do |env|
|
101
|
+
[200, {}, '']
|
102
|
+
end
|
103
|
+
|
104
|
+
response = receive_event(name: "test")
|
105
|
+
|
106
|
+
assert_equal "Test message sent", response[:message]
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def speak_uri
|
112
|
+
"https://#{subdomain}.campfirenow.com#{request_url}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def request_url
|
116
|
+
"/room/#{room}/speak.json"
|
117
|
+
end
|
118
|
+
|
119
|
+
def subdomain
|
120
|
+
"sub"
|
121
|
+
end
|
122
|
+
|
123
|
+
def room
|
124
|
+
"123"
|
125
|
+
end
|
126
|
+
|
127
|
+
def assert_campfire_receives(event_data, expected_body)
|
128
|
+
@stubs.post request_url do |env|
|
129
|
+
body = JSON.parse(env[:body])
|
130
|
+
assert_equal expected_body, body["message"]["body"]
|
131
|
+
[200, {}, '']
|
132
|
+
end
|
133
|
+
|
134
|
+
receive_event(event_data)
|
135
|
+
end
|
136
|
+
|
137
|
+
def receive_event(event_data = nil)
|
138
|
+
receive(
|
139
|
+
CC::Service::Campfire,
|
140
|
+
{ token: "token", subdomain: subdomain, room_id: room},
|
141
|
+
event_data || event(:quality, to: "D", from: "C")
|
142
|
+
)
|
143
|
+
end
|
144
|
+
end
|
data/test/fixtures.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
class EventFixtures
|
2
|
+
attr_reader :options
|
3
|
+
|
4
|
+
REMEDIATIONS = {
|
5
|
+
"A" => 0,
|
6
|
+
"B" => 10,
|
7
|
+
"C" => 15,
|
8
|
+
"D" => 20,
|
9
|
+
"F" => 25,
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = {
|
14
|
+
repo_name: "Example",
|
15
|
+
details_url: "https://codeclimate.com/repos/1/feed",
|
16
|
+
compare_url: "https://codeclimate.com/repos/1/compare",
|
17
|
+
}.merge(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Options: to, from
|
21
|
+
def coverage
|
22
|
+
to = options.delete(:to)
|
23
|
+
from = options.delete(:from)
|
24
|
+
delta = (to - from).round(1)
|
25
|
+
|
26
|
+
options.merge(
|
27
|
+
name: "coverage",
|
28
|
+
covered_percent: to,
|
29
|
+
previous_covered_percent: from,
|
30
|
+
covered_percent_delta: delta
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Options: to, from
|
35
|
+
def quality
|
36
|
+
to = options.delete(:to)
|
37
|
+
from = options.delete(:from)
|
38
|
+
|
39
|
+
options.merge(
|
40
|
+
name: "quality",
|
41
|
+
constant_name: "User",
|
42
|
+
rating: to,
|
43
|
+
previous_rating: from,
|
44
|
+
remediation_cost: REMEDIATIONS[to],
|
45
|
+
previous_remediation_cost: REMEDIATIONS[from]
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Options: warning_type, vulnerabilities
|
50
|
+
def vulnerability
|
51
|
+
options.merge(name: "vulnerability")
|
52
|
+
end
|
53
|
+
|
54
|
+
def issue
|
55
|
+
options.merge(name: "issue")
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def event(name, options = {})
|
61
|
+
fixtures = EventFixtures.new(options)
|
62
|
+
|
63
|
+
if fixtures.respond_to?(name)
|
64
|
+
fixtures.send(name)
|
65
|
+
else
|
66
|
+
raise ArgumentError, "No such fixture: #{name}"
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestFlowdock < CC::Service::TestCase
|
4
|
+
def test_valid_project_parameter
|
5
|
+
@stubs.post '/v1/messages/team_inbox/token' do |env|
|
6
|
+
body = Hash[URI.decode_www_form(env[:body])]
|
7
|
+
assert_equal "Exampleorg", body["project"]
|
8
|
+
[200, {}, '']
|
9
|
+
end
|
10
|
+
|
11
|
+
receive(
|
12
|
+
CC::Service::Flowdock,
|
13
|
+
{ api_token: "token" },
|
14
|
+
{ name: "test", repo_name: "Example.org" }
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_test_hook
|
19
|
+
assert_flowdock_receives(
|
20
|
+
"Test",
|
21
|
+
{ name: "test", repo_name: "Example" },
|
22
|
+
"This is a test of the Flowdock service hook"
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_coverage_improved
|
27
|
+
e = event(:coverage, to: 90.2, from: 80)
|
28
|
+
|
29
|
+
assert_flowdock_receives("Coverage", e, [
|
30
|
+
"<a href=\"https://codeclimate.com/repos/1/feed\">Test coverage</a>",
|
31
|
+
"has improved to 90.2% (+10.2%)",
|
32
|
+
"(<a href=\"https://codeclimate.com/repos/1/compare\">Compare</a>)"
|
33
|
+
].join(" "))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_coverage_declined
|
37
|
+
e = event(:coverage, to: 88.6, from: 94.6)
|
38
|
+
|
39
|
+
assert_flowdock_receives("Coverage", e, [
|
40
|
+
"<a href=\"https://codeclimate.com/repos/1/feed\">Test coverage</a>",
|
41
|
+
"has declined to 88.6% (-6.0%)",
|
42
|
+
"(<a href=\"https://codeclimate.com/repos/1/compare\">Compare</a>)"
|
43
|
+
].join(" "))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_quality_improved
|
47
|
+
e = event(:quality, to: "A", from: "B")
|
48
|
+
|
49
|
+
assert_flowdock_receives("Quality", e, [
|
50
|
+
"<a href=\"https://codeclimate.com/repos/1/feed\">User</a>",
|
51
|
+
"has improved from a B to an A",
|
52
|
+
"(<a href=\"https://codeclimate.com/repos/1/compare\">Compare</a>)"
|
53
|
+
].join(" "))
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_quality_declined
|
57
|
+
e = event(:quality, to: "D", from: "C")
|
58
|
+
|
59
|
+
assert_flowdock_receives("Quality", e, [
|
60
|
+
"<a href=\"https://codeclimate.com/repos/1/feed\">User</a>",
|
61
|
+
"has declined from a C to a D",
|
62
|
+
"(<a href=\"https://codeclimate.com/repos/1/compare\">Compare</a>)"
|
63
|
+
].join(" "))
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_single_vulnerability
|
67
|
+
e = event(:vulnerability, vulnerabilities: [
|
68
|
+
{ "warning_type" => "critical" }
|
69
|
+
])
|
70
|
+
|
71
|
+
assert_flowdock_receives("Vulnerability", e, [
|
72
|
+
"New <a href=\"https://codeclimate.com/repos/1/feed\">critical</a>",
|
73
|
+
"issue found",
|
74
|
+
].join(" "))
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_single_vulnerability_with_location
|
78
|
+
e = event(:vulnerability, vulnerabilities: [{
|
79
|
+
"warning_type" => "critical",
|
80
|
+
"location" => "app/user.rb line 120"
|
81
|
+
}])
|
82
|
+
|
83
|
+
assert_flowdock_receives("Vulnerability", e, [
|
84
|
+
"New <a href=\"https://codeclimate.com/repos/1/feed\">critical</a>",
|
85
|
+
"issue found in app/user.rb line 120",
|
86
|
+
].join(" "))
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_multiple_vulnerabilities
|
90
|
+
e = event(:vulnerability, warning_type: "critical", vulnerabilities: [{
|
91
|
+
"warning_type" => "unused",
|
92
|
+
"location" => "unused"
|
93
|
+
}, {
|
94
|
+
"warning_type" => "unused",
|
95
|
+
"location" => "unused"
|
96
|
+
}])
|
97
|
+
|
98
|
+
assert_flowdock_receives("Vulnerability", e, [
|
99
|
+
"2 new <a href=\"https://codeclimate.com/repos/1/feed\">critical</a>",
|
100
|
+
"issues found",
|
101
|
+
].join(" "))
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_receive_test
|
105
|
+
@stubs.post request_url do |env|
|
106
|
+
[200, {}, '']
|
107
|
+
end
|
108
|
+
|
109
|
+
response = receive_event(name: "test", repo_name: "foo")
|
110
|
+
|
111
|
+
assert_equal "Test message sent", response[:message]
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def endpoint_url
|
117
|
+
"https://api.flowdock.com#{request_url}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def request_url
|
121
|
+
"/v1/messages/team_inbox/#{token}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def token
|
125
|
+
"token"
|
126
|
+
end
|
127
|
+
|
128
|
+
def assert_flowdock_receives(subject, event_data, expected_body)
|
129
|
+
@stubs.post request_url do |env|
|
130
|
+
body = Hash[URI.decode_www_form(env[:body])]
|
131
|
+
assert_equal "Code Climate", body["source"]
|
132
|
+
assert_equal "hello@codeclimate.com", body["from_address"]
|
133
|
+
assert_equal "Code Climate", body["from_name"]
|
134
|
+
assert_equal "html", body["format"]
|
135
|
+
assert_equal subject, body["subject"]
|
136
|
+
assert_equal "Example", body["project"]
|
137
|
+
assert_equal expected_body, body["content"]
|
138
|
+
assert_equal "https://codeclimate.com", body["link"]
|
139
|
+
[200, {}, '']
|
140
|
+
end
|
141
|
+
|
142
|
+
receive_event(event_data)
|
143
|
+
end
|
144
|
+
|
145
|
+
def receive_event(event_data = nil)
|
146
|
+
receive(CC::Service::Flowdock, { api_token: "token" }, event_data || event(:quality, from: "D", to: "C"))
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class TestSnapshotFormatter < Test::Unit::TestCase
|
4
|
+
def described_class
|
5
|
+
CC::Formatters::SnapshotFormatter::Base
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_quality_alert_with_new_constants
|
9
|
+
f = described_class.new({"new_constants" => [{"to" => {"rating" => "D"}}], "changed_constants" => []})
|
10
|
+
refute_nil f.alert_constants_payload
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_quality_alert_with_decreased_constants
|
14
|
+
f = described_class.new({"new_constants" => [],
|
15
|
+
"changed_constants" => [{"to" => {"rating" => "D"}, "from" => {"rating" => "A"}}]
|
16
|
+
})
|
17
|
+
refute_nil f.alert_constants_payload
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_quality_improvements_with_better_ratings
|
21
|
+
f = described_class.new({"new_constants" => [],
|
22
|
+
"changed_constants" => [{"to" => {"rating" => "A"}, "from" => {"rating" => "D"}}]
|
23
|
+
})
|
24
|
+
refute_nil f.improved_constants_payload
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_nothing_set_without_changes
|
28
|
+
f = described_class.new({"new_constants" => [], "changed_constants" => []})
|
29
|
+
assert_nil f.alert_constants_payload
|
30
|
+
assert_nil f.improved_constants_payload
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_snapshot_formatter_test_with_relaxed_constraints
|
34
|
+
f = CC::Formatters::SnapshotFormatter::Sample.new({
|
35
|
+
"new_constants" => [{"name" => "foo", "to" => {"rating" => "A"}}, {"name" => "bar", "to" => {"rating" => "A"}}],
|
36
|
+
"changed_constants" => [
|
37
|
+
{"from" => {"rating" => "B"}, "to" => {"rating" => "C"}},
|
38
|
+
{"from" => {"rating" => "D"}, "to" => {"rating" => "D"}},
|
39
|
+
{"from" => {"rating" => "D"}, "to" => {"rating" => "D"}},
|
40
|
+
{"from" => {"rating" => "A"}, "to" => {"rating" => "B"}},
|
41
|
+
{"from" => {"rating" => "C"}, "to" => {"rating" => "B"}}
|
42
|
+
]})
|
43
|
+
|
44
|
+
refute_nil f.alert_constants_payload
|
45
|
+
refute_nil f.improved_constants_payload
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.expand_path('../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestGitHubIssues < CC::Service::TestCase
|
4
|
+
def test_creation_success
|
5
|
+
id = 1234
|
6
|
+
number = 123
|
7
|
+
url = "https://github.com/#{project}/pulls/#{number}"
|
8
|
+
stub_http(request_url, [201, {}, %<{"id": #{id}, "number": #{number}, "html_url":"#{url}"}>])
|
9
|
+
|
10
|
+
response = receive_event
|
11
|
+
|
12
|
+
assert_equal id, response[:id]
|
13
|
+
assert_equal number, response[:number]
|
14
|
+
assert_equal url, response[:url]
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_quality
|
18
|
+
assert_github_receives(
|
19
|
+
event(:quality, to: "D", from: "C"),
|
20
|
+
"Refactor User from a D on Code Climate",
|
21
|
+
"https://codeclimate.com/repos/1/feed"
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_issue
|
26
|
+
payload = {
|
27
|
+
issue: {
|
28
|
+
"check_name" => "Style/LongLine",
|
29
|
+
"description" => "Line is too long [1000/80]"
|
30
|
+
},
|
31
|
+
constant_name: "foo.rb",
|
32
|
+
details_url: "http://example.com/repos/id/foo.rb#issue_123"
|
33
|
+
}
|
34
|
+
|
35
|
+
assert_github_receives(
|
36
|
+
event(:issue, payload),
|
37
|
+
"Fix \"Style/LongLine\" issue in foo.rb",
|
38
|
+
"Line is too long [1000/80]\n\nhttp://example.com/repos/id/foo.rb#issue_123"
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_vulnerability
|
43
|
+
assert_github_receives(
|
44
|
+
event(:vulnerability, vulnerabilities: [{
|
45
|
+
"warning_type" => "critical",
|
46
|
+
"location" => "app/user.rb line 120"
|
47
|
+
}]),
|
48
|
+
"New critical issue found in app/user.rb line 120",
|
49
|
+
"A critical vulnerability was found by Code Climate in app/user.rb line 120.\n\nhttps://codeclimate.com/repos/1/feed"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_receive_test
|
54
|
+
@stubs.post request_url do |env|
|
55
|
+
[200, {}, '{"number": 2, "html_url": "http://foo.bar"}']
|
56
|
+
end
|
57
|
+
|
58
|
+
response = receive_event(name: "test")
|
59
|
+
|
60
|
+
assert_equal "Issue <a href='http://foo.bar'>#2</a> created.", response[:message]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def project
|
66
|
+
"brynary/test_repo"
|
67
|
+
end
|
68
|
+
|
69
|
+
def oauth_token
|
70
|
+
"123"
|
71
|
+
end
|
72
|
+
|
73
|
+
def request_url
|
74
|
+
"repos/#{project}/issues"
|
75
|
+
end
|
76
|
+
|
77
|
+
def assert_github_receives(event_data, title, ticket_body)
|
78
|
+
@stubs.post request_url do |env|
|
79
|
+
body = JSON.parse(env[:body])
|
80
|
+
assert_equal "token #{oauth_token}", env[:request_headers]["Authorization"]
|
81
|
+
assert_equal title, body["title"]
|
82
|
+
assert_equal ticket_body, body["body"]
|
83
|
+
[200, {}, '{}']
|
84
|
+
end
|
85
|
+
|
86
|
+
receive_event(event_data)
|
87
|
+
end
|
88
|
+
|
89
|
+
def receive_event(event_data = nil)
|
90
|
+
receive(
|
91
|
+
CC::Service::GitHubIssues,
|
92
|
+
{ oauth_token: "123", project: project },
|
93
|
+
event_data || event(:quality, from: "D", to: "C")
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|