reviewlette 0.0.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.travis.yml +2 -2
- data/Gemfile +13 -16
- data/Gemfile.lock +100 -101
- data/README.md +26 -26
- data/config/github_example.yml +4 -5
- data/lib/reviewlette.rb +40 -32
- data/lib/reviewlette/github_connection.rb +42 -38
- data/lib/reviewlette/trello_connection.rb +34 -33
- data/lib/reviewlette/version.rb +3 -0
- data/reviewlette.gemspec +4 -7
- data/spec/github_connection_spec.rb +23 -20
- data/spec/reviewlette_spec.rb +136 -38
- data/spec/trello_connection_spec.rb +18 -17
- metadata +12 -45
- data/bin/reviewlette +0 -8
- data/config/members_example.yml +0 -25
- data/lib/reviewlette/vacations.rb +0 -30
- data/spec/vacation_spec.rb +0 -36
@@ -1,42 +1,46 @@
|
|
1
|
-
require 'yaml'
|
2
1
|
require 'octokit'
|
3
2
|
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
class Reviewlette
|
4
|
+
class GithubConnection
|
5
|
+
attr_accessor :client, :repo
|
6
|
+
|
7
|
+
def initialize(repo, token)
|
8
|
+
@client = Octokit::Client.new(access_token: token)
|
9
|
+
@repo = repo
|
10
|
+
end
|
11
|
+
|
12
|
+
def pull_requests
|
13
|
+
@client.pull_requests(@repo)
|
14
|
+
end
|
15
|
+
|
16
|
+
def labels(issue)
|
17
|
+
@client.labels_for_issue(@repo, issue).map(&:name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_assignees(number, assignees)
|
21
|
+
@client.update_issue(@repo, number, assignees: assignees)
|
22
|
+
end
|
23
|
+
|
24
|
+
def comment_reviewers(number, reviewers, trello_card)
|
25
|
+
assignees = reviewers.map { |r| "@#{r.github_handle}" }.join(' and ')
|
26
|
+
|
27
|
+
comment = <<-eos
|
28
|
+
#{assignees} will review your pull request :dancers: check #{trello_card.url}
|
29
|
+
#{assignees}: Please review this pull request using our guidelines:
|
30
|
+
* test for acceptance criteria / functionality
|
31
|
+
* check if the new code is covered with tests
|
32
|
+
* check for unintended consequences
|
33
|
+
* encourage usage of the boyscout rule
|
34
|
+
* make sure the code is architected in the best way
|
35
|
+
* check that no unnecessary technical debt got introduced
|
36
|
+
* make sure that no unnecessary FIXMEs or TODOs got added
|
37
|
+
eos
|
38
|
+
|
39
|
+
@client.add_comment(@repo, number, comment)
|
40
|
+
end
|
41
|
+
|
42
|
+
def repo_exists?
|
43
|
+
@client.repository?(@repo)
|
44
|
+
end
|
15
45
|
end
|
16
|
-
|
17
|
-
def add_assignee(number, assignee)
|
18
|
-
@client.update_issue(@repo, number, assignee: assignee)
|
19
|
-
end
|
20
|
-
|
21
|
-
def reviewer_comment(number, assignee, trello_card)
|
22
|
-
comment = "@#{assignee} is your reviewer :dancers: check #{trello_card.url} \n" \
|
23
|
-
"@#{assignee}: Please review this pull request using our guidelines: \n" \
|
24
|
-
"* test for acceptance criteria / functionality \n" \
|
25
|
-
"* check if the new code is covered with tests \n" \
|
26
|
-
"* check for unintended consequences \n" \
|
27
|
-
"* encourage usage of the boyscout rule \n" \
|
28
|
-
"* make sure the code is architected in the best way \n" \
|
29
|
-
"* check that no unnecessary technical debt got introduced \n" \
|
30
|
-
"* make sure that no unnecessary FIXMEs or TODOs got added \n"
|
31
|
-
@client.add_comment(@repo, number, comment)
|
32
|
-
end
|
33
|
-
|
34
|
-
def unassigned_pull_requests
|
35
|
-
list_pulls.select { |issue| !issue[:assignee] }
|
36
|
-
end
|
37
|
-
|
38
|
-
def repo_exists?
|
39
|
-
@client.repository?(@repo)
|
40
|
-
end
|
41
|
-
|
42
46
|
end
|
@@ -1,45 +1,46 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'trello'
|
3
3
|
|
4
|
-
|
5
|
-
class TrelloConnection
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
4
|
+
class Reviewlette
|
5
|
+
class TrelloConnection
|
6
|
+
|
7
|
+
attr_accessor :board
|
8
|
+
|
9
|
+
def initialize(config = nil)
|
10
|
+
config ||= YAML.load_file("#{File.dirname(__FILE__)}/../../config/trello.yml")
|
11
|
+
Trello.configure do |conf|
|
12
|
+
conf.developer_public_key = config['consumerkey']
|
13
|
+
conf.member_token = config['oauthtoken']
|
14
|
+
end
|
15
|
+
@board = Trello::Board.find(config['board_id'])
|
14
16
|
end
|
15
|
-
@board = Trello::Board.find(@trello['board_id'])
|
16
|
-
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def add_reviewer_to_card(reviewer, card)
|
19
|
+
reviewer = find_member_by_username(reviewer)
|
20
|
+
card.add_member(reviewer)
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def comment_reviewers(card, repo_name, issue_id, reviewers)
|
24
|
+
comment = reviewers.map { |r| "@#{r.trello_handle}" }.join(' and ')
|
25
|
+
comment += " will review https://github.com/#{repo_name}/issues/#{issue_id}"
|
26
|
+
card.add_comment(comment)
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def move_card_to_list(card, column_name)
|
30
|
+
column = find_column(column_name)
|
31
|
+
card.move_to_list(column)
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
def find_column(column_name)
|
35
|
+
@board.lists.find { |x| x.name == column_name }
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
def find_member_by_username(username)
|
39
|
+
@board.members.find { |m| m.username == username }
|
40
|
+
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
+
def find_card_by_id(id)
|
43
|
+
@board.cards.find { |c| c.short_id == id.to_i }
|
44
|
+
end
|
42
45
|
end
|
43
|
-
|
44
46
|
end
|
45
|
-
|
data/reviewlette.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'reviewlette'
|
4
|
+
require 'reviewlette/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "reviewlette"
|
8
|
-
spec.version = VERSION
|
8
|
+
spec.version = Reviewlette::VERSION
|
9
9
|
spec.authors = ["jschmid1"]
|
10
10
|
spec.email = ["jschmid@suse.de"]
|
11
11
|
spec.summary = %q{Randomly assignes a reviewer to your Pullrequest and corresponding Trello Card.}
|
@@ -18,9 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency 'ruby-trello'
|
22
|
-
spec.add_runtime_dependency 'octokit'
|
23
|
-
spec.add_runtime_dependency 'sequel', '=4.13.0'
|
24
|
-
spec.add_runtime_dependency 'sqlite3', '=1.3.9'
|
21
|
+
spec.add_runtime_dependency 'ruby-trello'
|
22
|
+
spec.add_runtime_dependency 'octokit'
|
25
23
|
end
|
26
|
-
|
@@ -1,46 +1,49 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe GithubConnection do
|
4
|
-
|
5
|
-
|
6
|
-
let(
|
7
|
-
let(
|
8
|
-
let(
|
3
|
+
describe Reviewlette::GithubConnection do
|
4
|
+
subject { described_class.new(repo, token) }
|
5
|
+
let(:repo) { 'test' }
|
6
|
+
let(:token) { 'foo' }
|
7
|
+
let(:member1) { double(name: 'test1', github_handle: 'githubtest1') }
|
8
|
+
let(:member2) { double(name: 'test2', github_handle: 'githubtest2') }
|
9
9
|
|
10
10
|
describe '.new' do
|
11
11
|
it 'initializes octokit client and repo' do
|
12
12
|
expect(Octokit::Client).to receive(:new).with(:access_token => token)
|
13
|
-
expect(
|
13
|
+
expect(subject.repo).to eq(repo)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
describe '#
|
18
|
-
it 'adds
|
19
|
-
expect(
|
20
|
-
|
17
|
+
describe '#add_assignees' do
|
18
|
+
it 'adds assignees to the GitHub issue' do
|
19
|
+
expect(subject.client).to receive(:update_issue).with(repo, 11, assignees: ['test'])
|
20
|
+
subject.add_assignees(11, ['test'])
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe '#
|
24
|
+
describe '#comment_reviewers' do
|
25
25
|
it 'comments on a given issue' do
|
26
|
-
card
|
26
|
+
card = Trello::Card.new
|
27
|
+
reviewers = [member1, member2]
|
28
|
+
|
27
29
|
allow(card).to receive(:url).and_return('url')
|
28
|
-
expect(
|
29
|
-
|
30
|
+
expect(subject.client).to receive(:add_comment).with(repo, 11, anything)
|
31
|
+
|
32
|
+
subject.comment_reviewers(11, reviewers, card)
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
describe '#
|
36
|
+
describe '#pull_requests' do
|
34
37
|
it 'lists all pullrequests for a given repository' do
|
35
|
-
expect(
|
36
|
-
|
38
|
+
expect(subject.client).to receive(:pull_requests).with(repo)
|
39
|
+
subject.pull_requests
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
43
|
describe '#repo_exists?' do
|
41
44
|
it 'checks if a certain repository exists' do
|
42
|
-
expect(
|
43
|
-
|
45
|
+
expect(subject.client).to receive(:repository?).with(repo)
|
46
|
+
subject.repo_exists?
|
44
47
|
end
|
45
48
|
|
46
49
|
end
|
data/spec/reviewlette_spec.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Reviewlette do
|
4
|
-
|
4
|
+
let(:instance) { described_class.new members: [member1, member2] }
|
5
|
+
subject { instance }
|
5
6
|
|
6
|
-
let(:
|
7
|
-
|
8
|
-
let(:members_config) { { 'members' => [member1, member2] } }
|
9
|
-
let(:member1) { { 'name' => 'test1', 'suse_username' => 'test1', 'trello_username' => 'trellotest1' } }
|
10
|
-
let(:member2) { { 'name' => 'test2', 'suse_username' => 'test2', 'trello_username' => 'trellotest2' } }
|
7
|
+
let(:member1) { double(name: 'test1', github_handle: 'pinky', trello_handle: 'trellotest1') }
|
8
|
+
let(:member2) { double(name: 'test2', github_handle: 'brain', trello_handle: 'trellotest2') }
|
11
9
|
|
12
10
|
let(:github_config) { { token: token, repos: [repo, repo2] } }
|
13
11
|
let(:token) { '1234' }
|
@@ -15,71 +13,171 @@ describe Reviewlette do
|
|
15
13
|
let(:repo2) { 'SUSE/foo' }
|
16
14
|
|
17
15
|
before do
|
18
|
-
allow(TrelloConnection).to receive(:new).and_return TrelloConnection
|
19
|
-
allow(GithubConnection).to receive(:new).with(repo, token).and_return GithubConnection
|
16
|
+
allow(described_class::TrelloConnection).to receive(:new).and_return described_class::TrelloConnection
|
17
|
+
allow(described_class::GithubConnection).to receive(:new).with(repo, token).and_return described_class::GithubConnection
|
20
18
|
allow(YAML).to receive(:load_file).with(/github\.yml/).and_return github_config
|
21
|
-
allow(YAML).to receive(:load_file).with(/members\.yml/).and_return members_config
|
22
19
|
end
|
23
20
|
|
24
21
|
describe '.new' do
|
25
22
|
it 'sets trello connections' do
|
26
|
-
expect(TrelloConnection).to receive(:new)
|
27
|
-
subject
|
23
|
+
expect(described_class::TrelloConnection).to receive(:new)
|
24
|
+
subject
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
28
|
describe '.run' do
|
32
29
|
it 'iterates over all open repositories and looks for unassigned pull requests' do
|
33
30
|
github_config[:repos].each do |r|
|
34
|
-
expect(
|
35
|
-
|
31
|
+
expect(subject).to receive(:check_repo).with(r, token)
|
32
|
+
subject.check_repo(r, token)
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
40
37
|
describe '.check_repo' do
|
38
|
+
context 'invalid repo' do
|
39
|
+
it 'skips the repo' do
|
40
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return false
|
41
|
+
expect { subject.check_repo(repo, token) }.to output(/does not exist/).to_stdout
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
41
45
|
it 'iterates over all open pull requests and extracts trello ids from name' do
|
42
|
-
expect(GithubConnection).to receive(:repo_exists?).and_return true
|
43
|
-
expect(GithubConnection).to receive(:
|
44
|
-
expect(
|
46
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
47
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([{number: 11, title: 'test_issue_12'}])
|
48
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return([])
|
49
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(12)
|
45
50
|
|
46
|
-
|
51
|
+
subject.check_repo(repo, token)
|
47
52
|
end
|
48
53
|
|
49
|
-
it '
|
50
|
-
|
54
|
+
it 'iterates over all pull requests and skips those with no card id' do
|
55
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
56
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([{ number: 11, title: 'no card id' }])
|
57
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return([])
|
51
58
|
|
52
|
-
expect(
|
53
|
-
|
54
|
-
|
55
|
-
|
59
|
+
expect { subject.check_repo(repo, token) }.to output(/Pull request not assigned to a trello card/).to_stdout
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'adds assignees and reviewers comment on github, adds comment on trello and moves card' do
|
63
|
+
card = Trello::Card.new
|
64
|
+
user = double(github_handle: 'testgit', trello_handle: 'testtrello')
|
65
|
+
pullrequest = { number: 11, title: 'test_issue_12', assignees: [] }
|
56
66
|
|
57
|
-
expect(GithubConnection).to receive(:
|
58
|
-
expect(GithubConnection).to receive(:
|
67
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
68
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([pullrequest])
|
69
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return([])
|
70
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(12).and_return(card)
|
71
|
+
expect(subject).to receive(:select_reviewers).and_return([user])
|
59
72
|
|
60
|
-
expect(
|
61
|
-
expect(
|
73
|
+
expect(described_class::GithubConnection).to receive(:add_assignees).with(11, ['testgit'])
|
74
|
+
expect(described_class::GithubConnection).to receive(:comment_reviewers).with(11, [user], card)
|
62
75
|
|
63
|
-
|
76
|
+
expect(described_class::TrelloConnection).to receive(:comment_reviewers).with(card, 'SUSE/test', 11, [user])
|
77
|
+
expect(described_class::TrelloConnection).to receive(:move_card_to_list).with(card, 'In review')
|
78
|
+
|
79
|
+
subject.check_repo(repo, token)
|
64
80
|
end
|
65
81
|
|
66
|
-
|
82
|
+
context 'pull request with one reviewer but two wanted' do
|
83
|
+
it 'selects a second reviewer' do
|
84
|
+
card = Trello::Card.new
|
85
|
+
pullrequest = { number: 11, title: 'pr title 42', assignees: [OpenStruct.new({login: 'pinky'})] }
|
86
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
87
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([pullrequest])
|
88
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return(['2 reviewers'])
|
89
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(42).and_return(card)
|
90
|
+
|
91
|
+
expect(described_class::GithubConnection).to receive(:add_assignees).with(11, ['pinky', 'brain'])
|
92
|
+
expect(described_class::GithubConnection).to receive(:comment_reviewers).with(11, [member1, member2], card)
|
93
|
+
expect(described_class::TrelloConnection).to receive(:comment_reviewers).with(card, repo, 11, [member1, member2])
|
94
|
+
expect(described_class::TrelloConnection).to receive(:move_card_to_list).with(card, 'In review')
|
95
|
+
expect(subject).to receive(:select_reviewers).with(card, 2, [member1]).and_return([member1, member2])
|
96
|
+
subject.check_repo(repo, token)
|
97
|
+
end
|
98
|
+
end
|
67
99
|
|
68
|
-
|
69
|
-
|
70
|
-
|
100
|
+
context 'pull request with two reviewers but no "2 reviewers" label' do
|
101
|
+
it 'keeps both reviewers' do
|
102
|
+
card = Trello::Card.new
|
103
|
+
pullrequest = { number: 11, title: 'pr title 42', assignees: [ OpenStruct.new({login: 'pinky'}), OpenStruct.new({login: 'brain'})] }
|
104
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
105
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([pullrequest])
|
106
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return([])
|
107
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(42).and_return(card)
|
108
|
+
|
109
|
+
expect(described_class::GithubConnection).not_to receive(:add_assignees)
|
110
|
+
expect(described_class::TrelloConnection).not_to receive(:move_card_to_list).with(card, 'In review')
|
111
|
+
subject.check_repo(repo, token)
|
112
|
+
end
|
113
|
+
end
|
71
114
|
|
72
|
-
|
73
|
-
|
74
|
-
|
115
|
+
context 'pull request with already correct number of reviewers' do
|
116
|
+
it 'does not assign nor comment in GitHub or Trello' do
|
117
|
+
card = Trello::Card.new
|
118
|
+
pullrequest = { number: 11, title: 'pr title 42', assignees: [ OpenStruct.new({login: 'pinky'}), OpenStruct.new({login: 'pinky'})] }
|
119
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
120
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([pullrequest])
|
121
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return(['2 reviewers'])
|
122
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(42).and_return(card)
|
123
|
+
|
124
|
+
expect(described_class::GithubConnection).not_to receive(:add_assignees)
|
125
|
+
expect(described_class::GithubConnection).not_to receive(:comment_reviewers)
|
126
|
+
expect(described_class::TrelloConnection).not_to receive(:comment_reviewers)
|
127
|
+
expect(described_class::TrelloConnection).not_to receive(:move_card_to_list).with(card, 'In review')
|
128
|
+
subject.check_repo(repo, token)
|
129
|
+
end
|
75
130
|
end
|
76
131
|
|
132
|
+
it 'does not find a reviewer' do
|
133
|
+
card = Trello::Card.new
|
134
|
+
|
135
|
+
expect(described_class::GithubConnection).to receive(:repo_exists?).and_return true
|
136
|
+
expect(described_class::GithubConnection).to receive(:pull_requests).and_return([{ number: 11, title: 'test_issue_12', assignees: [] }])
|
137
|
+
expect(described_class::GithubConnection).to receive(:labels).and_return([])
|
138
|
+
expect(described_class::TrelloConnection).to receive(:find_card_by_id).with(12).and_return(card)
|
139
|
+
expect(subject).to receive(:select_reviewers).and_return []
|
140
|
+
|
141
|
+
expect { subject.check_repo(repo, token) }.to output(/Could not find a reviewer/).to_stdout
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '.select_reviewers' do
|
77
147
|
it 'excludes the owner of the trello card' do
|
78
148
|
card = Trello::Card.new
|
149
|
+
reviewers = [member1, member2]
|
150
|
+
|
151
|
+
allow(card).to receive_message_chain(:members, :map).and_return(reviewers)
|
152
|
+
expect(subject.select_reviewers(card).size).to eq(1)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'selects n reviewers' do
|
156
|
+
card = Trello::Card.new
|
157
|
+
|
158
|
+
allow(card).to receive_message_chain(:members, :map).and_return([member1])
|
159
|
+
expect(subject.select_reviewers(card, 2)).to match_array([member1, member2])
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'selects only one reviewer if no second is available' do
|
163
|
+
card = Trello::Card.new
|
164
|
+
|
165
|
+
allow(card).to receive_message_chain(:members, :map).and_return([member2.trello_handle])
|
166
|
+
expect(subject.select_reviewers(card, 2)).to eq([member1])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '.how_many_should_review' do
|
171
|
+
subject { instance.how_many_should_review(labels) }
|
172
|
+
|
173
|
+
context 'with "2 reviewers" label' do
|
174
|
+
let(:labels) { ['foo', '2 reviewers', 'bar'] }
|
175
|
+
it { is_expected.to eq(2) }
|
176
|
+
end
|
79
177
|
|
80
|
-
|
81
|
-
|
82
|
-
|
178
|
+
context 'with no "2 reviewers" label' do
|
179
|
+
let(:labels) { ['foo', 'bar'] }
|
180
|
+
it { is_expected.to eq(1) }
|
83
181
|
end
|
84
182
|
end
|
85
183
|
end
|