8e9d450197645f7914d7321d5149259d95dbd984
[margebot.git] / tests / test_marge.py
1 """
2 Mrgebot tests
3 """
4 # pragma pylint: disable=invalid-name
5 # pragma pylint: disable=missing-docstring
6 # pragma pylint: disable=no-else-return
7 # pragma pylint: disable=protected-access
8 # pragma pylint: disable=redefined-outer-name
9 # pragma pylint: disable=too-many-public-methods
10 # pragma pylint: disable=unused-argument
11
12 from datetime import datetime
13 import logging
14 import json
15
16 import errbot
17 from errbot.backends.test import testbot # pylint: disable=unused-import
18 import gitlab
19 from dateutil.relativedelta import relativedelta
20 import pytest
21 from plugins.marge import deltastr
22
23
24 class TestMarge(object):
25 """
26 Margebot Tests
27
28 Run 'py.test' from the margebot root directory
29 """
30
31 extra_plugin_dir = "./plugins/"
32 loglevel = logging.INFO
33
34 @pytest.fixture
35 def margebot(self, testbot, monkeypatch, mocker):
36 testbot.push_message("!plugin config Marge {'CHATROOM_HOST': 'conference.test.com', 'GITLAB_HOST': 'gitlab.test.com', 'GITLAB_ADMIN_TOKEN': 'fake-token', 'CRONTAB': '0 * * * *', 'VERIFY_SSL': True, 'CRONTAB_SOAK_HOURS': 1, 'WEBHOOK_URL': 'https://webhood.errbot.com:3142/margebot'}")
37 testbot.pop_message()
38 testbot.push_message("!plugin config Webserver {'HOST': '0.0.0.0', 'PORT':3141, 'SSL': {'certificate': '', 'enabled': False, 'host': '0.0.0.0', 'key': '', 'port': 3142}}")
39 testbot.pop_message()
40
41 def mock_get(self, key):
42 return {}
43 monkeypatch.setattr(errbot.storage.StoreMixin, '__getitem__', mock_get, raising=False)
44
45 def mock_keys(self):
46 return ['OPEN_MRS']
47 monkeypatch.setattr(errbot.storage.StoreMixin, 'keys', mock_keys, raising=False)
48
49 return testbot
50
51 @pytest.fixture
52 def margebot_no_reviews(self, margebot, monkeypatch):
53 return margebot
54
55 @pytest.fixture
56 def margebot_one_review(self, margebot, monkeypatch):
57
58 def mock_get(self, key):
59 return {(1001, 2001, 'room1,room2'): True} # [(project, iid, notify_rooms)]
60
61 monkeypatch.setattr(errbot.storage.StoreMixin, '__getitem__', mock_get, raising=False)
62 return margebot
63
64 @pytest.fixture
65 def one_wip_review(self, margebot_one_review, monkeypatch):
66
67 def mock_getmergerequest(self, project, iid):
68 return {'author': {'id': 2001},
69 'created_at': 'Oct 29, 2017 2:37am',
70 'merge_status': 'can_be_merged',
71 'state': 'opened',
72 'upvotes': 0,
73 'web_url': 'http://gitlab.example.com/sample/mr/2001',
74 'work_in_progress': True}
75 monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest)
76 return margebot_one_review
77
78 @pytest.fixture
79 def one_waiting_review(self, margebot_one_review, monkeypatch):
80
81 def mock_getmergerequest(self, project, iid):
82 return {'author': {'id': 2001},
83 'created_at': 'Oct 29, 2017 2:37am',
84 'merge_status': 'can_be_merged',
85 'state': 'opened',
86 'upvotes': 1,
87 'web_url': 'http://gitlab.example.com/sample/mr/2001',
88 'work_in_progress': False}
89 monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest)
90 return margebot_one_review
91
92 @pytest.fixture
93 def one_conflicted_review(self, margebot_one_review, monkeypatch):
94
95 def mock_getmergerequest(self, project, iid):
96 return {'author': {'id': 2001},
97 'created_at': 'Oct 29, 2017 2:37am',
98 'merge_status': 'merge_conflicts',
99 'state': 'opened',
100 'upvotes': 2,
101 'web_url': 'http://gitlab.example.com/sample/mr/2001',
102 'work_in_progress': False}
103 monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest)
104 return margebot_one_review
105
106 @pytest.fixture
107 def one_mergable_review(self, margebot_one_review, monkeypatch):
108
109 def mock_getmergerequest(self, project, iid):
110 return {'author': {'id': 2001},
111 'created_at': 'Oct 29, 2017 2:37am',
112 'merge_status': 'can_be_merged',
113 'state': 'opened',
114 'upvotes': 2,
115 'web_url': 'http://gitlab.example.com/sample/mr/2001',
116 'work_in_progress': False}
117 monkeypatch.setattr(gitlab.Gitlab, 'getmergerequest', mock_getmergerequest)
118 return margebot_one_review
119
120 @pytest.fixture
121 def gitlab(self, monkeypatch):
122
123 def mock_getuser(self, user):
124 if user == '(missing)':
125 return False
126 else:
127 return {'username': user + ' username'}
128 monkeypatch.setattr(gitlab.Gitlab, 'getuser', mock_getuser)
129
130 def mock_getusers(self, search=None):
131 if search:
132 ((_, val)) = search
133 if val == "(missing)":
134 return []
135 else:
136 return [{'user': val, 'id': 3001}]
137 else:
138 return [{'user': 'user1', 'id': 3001}, {'user': 'user2', 'id': 3002}]
139 monkeypatch.setattr(gitlab.Gitlab, 'getusers', mock_getusers)
140
141 # Waiting on Gitlab 8.9 or greater
142 # def mock_getapprovals(id):
143 # return []
144 # monkeypatch.setattr(gitlab.Gitlab, 'getapprovals', mock_getapprovals)
145
146 return gitlab.Gitlab
147
148 @pytest.fixture
149 def gitlab_no_reviews(self, gitlab, monkeypatch):
150 def mock_getmergerequest(self, project, iid):
151 return {}
152 monkeypatch.setattr(gitlab, 'getuser', mock_getmergerequest)
153 return gitlab
154
155 @pytest.fixture
156 def gitlab_one_review(self, gitlab, monkeypatch):
157 def mock_getmergerequest(self, project, iid):
158 return {'author': {'id': 2001},
159 'created_at': 'Oct 29, 2017 2:37am',
160 'merge_status': 'can_be_merged',
161 'state': 'opened',
162 'upvotes': 0,
163 'web_url': 'http://gitlab.example.com/sample/mr/2001',
164 'work_in_progress': False}
165 monkeypatch.setattr(gitlab, 'getmergerequest', mock_getmergerequest)
166 return gitlab
167
168 # ============================================================================
169
170 @pytest.mark.parametrize("rdelta,expected", [
171 (relativedelta(days=1), "1 day ago"),
172 (relativedelta(days=2), "2 days ago"),
173 (relativedelta(days=1, hours=1), "1 day, 1 hour ago"),
174 (relativedelta(days=2, hours=2), "2 days, 2 hours ago"),
175 (relativedelta(hours=2), "2 hours ago"),
176 (relativedelta(days=1, minutes=23), "1 day, 23 minutes ago"),
177 (relativedelta(minutes=2), "2 minutes ago"),
178 (relativedelta(minutes=1), "1 minute ago"),
179 (relativedelta(minutes=0), "now"), ])
180 def test_deltastr(self, rdelta, expected):
181 now = datetime.now()
182 tdelta = (now + rdelta) - now
183 assert deltastr(tdelta) == expected
184
185 def test_help(self, margebot):
186 margebot.push_message('!help')
187 help_message = margebot.pop_message()
188 assert "Marge" in help_message
189 assert '!reviews' in help_message
190 assert '!hello' in help_message
191 assert '!xyzzy' in help_message
192
193 def test_hello(self, margebot):
194 margebot.push_message('!hello')
195 assert 'Hi there' in margebot.pop_message()
196
197 # def test_xyzzy(self, margebot):
198 # margebot.push_message('!xyzzy')
199 # assert 'All open MRs have been merged into master' in margebot.pop_message()
200 # time.sleep(6)
201 # assert 'just kidding' in margebot.pop_message()
202
203 def test_webstatus(self, margebot):
204 margebot.push_message('!webstatus')
205 assert 'margebot/<rooms>' in margebot.pop_message()
206
207 def test_gitlab_hook(self, margebot, gitlab):
208 request = json.dumps({'object_kind': 'merge_request',
209 'object_attributes': {
210 'state': 'opened',
211 'work_in_progress': '',
212 'title': 'title',
213 'author_id': 'author_id',
214 'target_project_id': 'project_id',
215 'id': 'id',
216 'iid': 'iid'},
217 'project': {
218 'homepage': 'url'}})
219 margebot.push_message("!webhook test /margebot/room1,room2 " + request)
220 assert 'Hi there ! author_id username has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
221 margebot.push_message('!reviews')
222 assert 'Hi there ! author_id username has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
223
224 def test_gitlab_hook_wip(self, margebot, gitlab):
225 request = json.dumps({'object_kind': 'merge_request',
226 'object_attributes': {
227 'state': 'opened',
228 'work_in_progress': 'true',
229 'title': 'title',
230 'author_id': 'author_id',
231 'target_project_id': 'project_id',
232 'id': 'id',
233 'iid': 'iid'},
234 'project': {
235 'homepage': 'url'}})
236 margebot.push_message("!webhook test /margebot/room1,room2 " + request)
237 assert 'Hi there ! author_id username has opened a new WIP MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
238 margebot.push_message('!reviews')
239 assert 'Hi there ! author_id username has opened a new WIP MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
240
241 def test_gitlab_hook_unexpected_user(self, margebot, gitlab):
242 request = json.dumps({'object_kind': 'merge_request',
243 'object_attributes': {
244 'state': 'opened',
245 'work_in_progress': '',
246 'title': 'title',
247 'author_id': '(missing)',
248 'target_project_id': 'project_id',
249 'id': 'id',
250 'iid': 'iid'},
251 'project': {
252 'homepage': 'url'}})
253
254 margebot.push_message("!webhook test /margebot/room1,room2 " + request)
255 assert 'Hi there ! (missing) has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
256 margebot.push_message('!reviews')
257 assert 'Hi there ! (missing) has opened a new MR: \"title\"\nurl/merge_requests/iid' in margebot.pop_message()
258
259 def test_gitlab_hook_unexpected_object_kind(self, margebot, gitlab, caplog):
260 request = json.dumps({'object_kind': 'not_merge_request',
261 'object_attributes': {
262 'state': 'opened',
263 'work_in_progress': '',
264 'title': 'title',
265 'author_id': 'author_id',
266 'target_project_id': 'project_id',
267 'id': 'id',
268 'iid': 'iid'},
269 'project': {
270 'homepage': 'url'}})
271
272 margebot.push_message("!webhook test /margebot/room1,room2 " + request)
273 margebot.pop_message()
274 margebot.push_message('!reviews')
275 assert 'Hi gbin: I found no open MRs for you.' in margebot.pop_message()
276 assert 'unexpecting object_kind: not_merge_request' in caplog.text # Has to be at end of method
277
278 def test_nothing_to_review(self, margebot, gitlab_no_reviews):
279 margebot.push_message('!reviews')
280 assert 'Hi gbin: I found no open MRs for you.' in margebot.pop_message()
281
282 def test_get_one_open_mr(self, margebot_one_review, gitlab_one_review):
283 margebot_one_review.push_message('!reviews')
284 output = margebot_one_review.pop_message()
285 assert 'These MRs need some attention' in output
286 assert 'http://gitlab.example.com/sample/mr/2001' in output
287 assert 'No upvotes, please review.' in output
288
289 def test_get_one_wip_mr(self, one_wip_review, gitlab):
290 one_wip_review.push_message('!reviews')
291 output = one_wip_review.pop_message()
292 assert 'These MRs need some attention' in output
293 assert 'http://gitlab.example.com/sample/mr/2001' in output
294 assert 'No upvotes, please review but still WIP.' in output
295
296 def test_get_one_waiting_mr(self, margebot, one_waiting_review, gitlab):
297 one_waiting_review.push_message('!reviews')
298 output = margebot.pop_message()
299 assert 'These MRs need some attention' in output
300 assert 'http://gitlab.example.com/sample/mr/2001' in output
301 assert 'Waiting for another upvote.' in output
302
303 def test_get_one_mergable_mr(self, margebot, one_mergable_review, gitlab):
304 one_mergable_review.push_message('!reviews')
305 output = margebot.pop_message()
306 assert 'These MRs need some attention' in output
307 assert 'http://gitlab.example.com/sample/mr/2001' in output
308 assert 'Has 2+ upvotes and could be merged in now.' in output
309
310 def test_get_one_conflicted_mr(self, margebot, one_conflicted_review, gitlab):
311 one_conflicted_review.push_message('!reviews')
312 output = margebot.pop_message(timeout=1)
313 assert 'These MRs need some attention' in output
314 assert 'http://gitlab.example.com/sample/mr/2001' in output
315 assert 'Has 2+ upvotes and could be merged in now except there are merge conflicts.' in output
316
317 def test_crontab_hook(self, one_waiting_review, gitlab, monkeypatch, mocker):
318 plugin = one_waiting_review._bot.plugin_manager.get_plugin_obj_by_name('Marge')
319
320 def mock_rooms():
321 return [mocker.MagicMock(node='room1'), mocker.MagicMock(node='room2')]
322 monkeypatch.setattr(plugin, 'rooms', mock_rooms)
323
324 plugin.crontab_hook("unused")
325 output = one_waiting_review.pop_message()
326 assert 'These MRs need some attention' in output
327 assert 'http://gitlab.example.com/sample/mr/2001 (opened' in output