blob: 6ab1a2650cc2964bc1efb99ec748cf1fd23029b2 [file] [log] [blame]
Gregory Haynes4fc12542015-04-22 20:38:06 -07001# Copyright 2015 GoodData
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
James E. Blair21037782017-07-19 11:56:55 -070015import os
Jan Hrubane252a732017-01-03 15:03:09 +010016import re
Jamie Lennox3f16de52017-05-09 14:24:11 +100017from testtools.matchers import MatchesRegex, StartsWith
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +000018import urllib
Jan Hruban324ca5b2015-11-05 19:28:54 +010019import time
Jesse Keating64d29012017-09-06 12:27:49 -070020from unittest import skip
Gregory Haynes4fc12542015-04-22 20:38:06 -070021
James E. Blair21037782017-07-19 11:56:55 -070022import git
23
Wayne1a78c612015-06-11 17:14:13 -070024from tests.base import ZuulTestCase, simple_layout, random_sha1
Gregory Haynes4fc12542015-04-22 20:38:06 -070025
Gregory Haynes4fc12542015-04-22 20:38:06 -070026
27class TestGithubDriver(ZuulTestCase):
28 config_file = 'zuul-github-driver.conf'
29
30 @simple_layout('layouts/basic-github.yaml', driver='github')
31 def test_pull_event(self):
32 self.executor_server.hold_jobs_in_build = True
33
Jan Hruban37615e52015-11-19 14:30:49 +010034 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
35 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Gregory Haynes4fc12542015-04-22 20:38:06 -070036 self.waitUntilSettled()
37
Gregory Haynes4fc12542015-04-22 20:38:06 -070038 self.executor_server.hold_jobs_in_build = False
39 self.executor_server.release()
40 self.waitUntilSettled()
41
42 self.assertEqual('SUCCESS',
43 self.getJobFromHistory('project-test1').result)
44 self.assertEqual('SUCCESS',
45 self.getJobFromHistory('project-test2').result)
46
47 job = self.getJobFromHistory('project-test2')
Monty Taylord13bc362017-06-30 13:11:37 -050048 zuulvars = job.parameters['zuul']
James E. Blaire3db2952017-07-21 15:03:36 -070049 self.assertEqual(str(A.number), zuulvars['change'])
50 self.assertEqual(str(A.head_sha), zuulvars['patchset'])
James E. Blair3b222492017-07-21 15:17:37 -070051 self.assertEqual('master', zuulvars['branch'])
Jan Hruban37615e52015-11-19 14:30:49 +010052 self.assertEqual(1, len(A.comments))
Jan Hruband4edee82015-12-16 12:49:51 +010053 self.assertEqual(2, len(self.history))
54
55 # test_pull_unmatched_branch_event(self):
56 self.create_branch('org/project', 'unmatched_branch')
57 B = self.fake_github.openFakePullRequest(
58 'org/project', 'unmatched_branch', 'B')
59 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
60 self.waitUntilSettled()
61
62 self.assertEqual(2, len(self.history))
Wayne1a78c612015-06-11 17:14:13 -070063
Jan Hruban570d01c2016-03-10 21:51:32 +010064 @simple_layout('layouts/files-github.yaml', driver='github')
65 def test_pull_matched_file_event(self):
66 A = self.fake_github.openFakePullRequest(
67 'org/project', 'master', 'A',
68 files=['random.txt', 'build-requires'])
69 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
70 self.waitUntilSettled()
71 self.assertEqual(1, len(self.history))
72
73 # test_pull_unmatched_file_event
74 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B',
75 files=['random.txt'])
76 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
77 self.waitUntilSettled()
78 self.assertEqual(1, len(self.history))
79
Jan Hrubanc7ab1602015-10-14 15:29:33 +020080 @simple_layout('layouts/basic-github.yaml', driver='github')
81 def test_comment_event(self):
Jan Hruban37615e52015-11-19 14:30:49 +010082 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
83 self.fake_github.emitEvent(A.getCommentAddedEvent('test me'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020084 self.waitUntilSettled()
85 self.assertEqual(2, len(self.history))
86
87 # Test an unmatched comment, history should remain the same
Jan Hruban37615e52015-11-19 14:30:49 +010088 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
89 self.fake_github.emitEvent(B.getCommentAddedEvent('casual comment'))
Jan Hrubanc7ab1602015-10-14 15:29:33 +020090 self.waitUntilSettled()
91 self.assertEqual(2, len(self.history))
92
Wayne1a78c612015-06-11 17:14:13 -070093 @simple_layout('layouts/push-tag-github.yaml', driver='github')
94 def test_tag_event(self):
95 self.executor_server.hold_jobs_in_build = True
96
James E. Blair21037782017-07-19 11:56:55 -070097 self.create_branch('org/project', 'tagbranch')
98 files = {'README.txt': 'test'}
99 self.addCommitToRepo('org/project', 'test tag',
100 files, branch='tagbranch', tag='newtag')
101 path = os.path.join(self.upstream_root, 'org/project')
102 repo = git.Repo(path)
103 tag = repo.tags['newtag']
104 sha = tag.commit.hexsha
105 del repo
106
Wayne1a78c612015-06-11 17:14:13 -0700107 self.fake_github.emitEvent(
108 self.fake_github.getPushEvent('org/project', 'refs/tags/newtag',
109 new_rev=sha))
110 self.waitUntilSettled()
111
112 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700113 self.assertEqual('refs/tags/newtag', build_params['zuul']['ref'])
James E. Blairb8203e42017-08-02 17:00:14 -0700114 self.assertFalse('oldrev' in build_params['zuul'])
James E. Blaired8b0012017-07-21 14:49:29 -0700115 self.assertEqual(sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700116 self.executor_server.hold_jobs_in_build = False
117 self.executor_server.release()
118 self.waitUntilSettled()
119
120 self.assertEqual('SUCCESS',
121 self.getJobFromHistory('project-tag').result)
122
123 @simple_layout('layouts/push-tag-github.yaml', driver='github')
124 def test_push_event(self):
125 self.executor_server.hold_jobs_in_build = True
126
James E. Blair289f5932017-07-27 15:02:29 -0700127 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
128 old_sha = '0' * 40
129 new_sha = A.head_sha
130 A.setMerged("merging A")
131 pevent = self.fake_github.getPushEvent(project='org/project',
132 ref='refs/heads/master',
133 old_rev=old_sha,
134 new_rev=new_sha)
135 self.fake_github.emitEvent(pevent)
Wayne1a78c612015-06-11 17:14:13 -0700136 self.waitUntilSettled()
137
138 build_params = self.builds[0].parameters
James E. Blaira438c172017-07-21 14:54:42 -0700139 self.assertEqual('refs/heads/master', build_params['zuul']['ref'])
James E. Blair289f5932017-07-27 15:02:29 -0700140 self.assertFalse('oldrev' in build_params['zuul'])
James E. Blaired8b0012017-07-21 14:49:29 -0700141 self.assertEqual(new_sha, build_params['zuul']['newrev'])
Wayne1a78c612015-06-11 17:14:13 -0700142
143 self.executor_server.hold_jobs_in_build = False
144 self.executor_server.release()
145 self.waitUntilSettled()
146
147 self.assertEqual('SUCCESS',
148 self.getJobFromHistory('project-post').result)
Jan Hruband4edee82015-12-16 12:49:51 +0100149 self.assertEqual(1, len(self.history))
150
151 # test unmatched push event
152 old_sha = random_sha1()
153 new_sha = random_sha1()
154 self.fake_github.emitEvent(
155 self.fake_github.getPushEvent('org/project',
156 'refs/heads/unmatched_branch',
157 old_sha, new_sha))
158 self.waitUntilSettled()
159
160 self.assertEqual(1, len(self.history))
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200161
Jan Hruban16ad31f2015-11-07 14:39:07 +0100162 @simple_layout('layouts/labeling-github.yaml', driver='github')
163 def test_labels(self):
Jan Hruban37615e52015-11-19 14:30:49 +0100164 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100165 self.fake_github.emitEvent(A.addLabel('test'))
166 self.waitUntilSettled()
167 self.assertEqual(1, len(self.history))
168 self.assertEqual('project-labels', self.history[0].name)
169 self.assertEqual(['tests passed'], A.labels)
170
171 # test label removed
Jan Hruban37615e52015-11-19 14:30:49 +0100172 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100173 B.addLabel('do not test')
174 self.fake_github.emitEvent(B.removeLabel('do not test'))
175 self.waitUntilSettled()
176 self.assertEqual(2, len(self.history))
177 self.assertEqual('project-labels', self.history[1].name)
178 self.assertEqual(['tests passed'], B.labels)
179
180 # test unmatched label
Jan Hruban37615e52015-11-19 14:30:49 +0100181 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban16ad31f2015-11-07 14:39:07 +0100182 self.fake_github.emitEvent(C.addLabel('other label'))
183 self.waitUntilSettled()
184 self.assertEqual(2, len(self.history))
185 self.assertEqual(['other label'], C.labels)
186
Jesse Keating5c05a9f2017-01-12 14:44:58 -0800187 @simple_layout('layouts/reviews-github.yaml', driver='github')
188 def test_review_event(self):
189 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
190 self.fake_github.emitEvent(A.getReviewAddedEvent('approve'))
191 self.waitUntilSettled()
192 self.assertEqual(1, len(self.history))
193 self.assertEqual('project-reviews', self.history[0].name)
194 self.assertEqual(['tests passed'], A.labels)
195
196 # test_review_unmatched_event
197 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
198 self.fake_github.emitEvent(B.getReviewAddedEvent('comment'))
199 self.waitUntilSettled()
200 self.assertEqual(1, len(self.history))
201
Jan Hruban324ca5b2015-11-05 19:28:54 +0100202 @simple_layout('layouts/dequeue-github.yaml', driver='github')
203 def test_dequeue_pull_synchronized(self):
204 self.executor_server.hold_jobs_in_build = True
205
Jan Hruban37615e52015-11-19 14:30:49 +0100206 A = self.fake_github.openFakePullRequest(
207 'org/one-job-project', 'master', 'A')
208 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100209 self.waitUntilSettled()
210
211 # event update stamp has resolution one second, wait so the latter
212 # one has newer timestamp
213 time.sleep(1)
Jan Hruban37615e52015-11-19 14:30:49 +0100214 A.addCommit()
215 self.fake_github.emitEvent(A.getPullRequestSynchronizeEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100216 self.waitUntilSettled()
217
218 self.executor_server.hold_jobs_in_build = False
219 self.executor_server.release()
220 self.waitUntilSettled()
221
222 self.assertEqual(2, len(self.history))
223 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
224
225 @simple_layout('layouts/dequeue-github.yaml', driver='github')
226 def test_dequeue_pull_abandoned(self):
227 self.executor_server.hold_jobs_in_build = True
228
Jan Hruban37615e52015-11-19 14:30:49 +0100229 A = self.fake_github.openFakePullRequest(
230 'org/one-job-project', 'master', 'A')
231 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100232 self.waitUntilSettled()
Jan Hruban37615e52015-11-19 14:30:49 +0100233 self.fake_github.emitEvent(A.getPullRequestClosedEvent())
Jan Hruban324ca5b2015-11-05 19:28:54 +0100234 self.waitUntilSettled()
235
236 self.executor_server.hold_jobs_in_build = False
237 self.executor_server.release()
238 self.waitUntilSettled()
239
240 self.assertEqual(1, len(self.history))
241 self.assertEqual(1, self.countJobResults(self.history, 'ABORTED'))
242
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200243 @simple_layout('layouts/basic-github.yaml', driver='github')
244 def test_git_https_url(self):
245 """Test that git_ssh option gives git url with ssh"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100246 tenant = self.sched.abide.tenants.get('tenant-one')
247 _, project = tenant.getProject('org/project')
248
249 url = self.fake_github.real_getGitUrl(project)
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200250 self.assertEqual('https://github.com/org/project', url)
251
252 @simple_layout('layouts/basic-github.yaml', driver='github')
253 def test_git_ssh_url(self):
254 """Test that git_ssh option gives git url with ssh"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100255 tenant = self.sched.abide.tenants.get('tenant-one')
256 _, project = tenant.getProject('org/project')
257
258 url = self.fake_github_ssh.real_getGitUrl(project)
Jan Hruban6d53c5e2015-10-24 03:03:34 +0200259 self.assertEqual('ssh://git@github.com/org/project.git', url)
Jan Hrubane252a732017-01-03 15:03:09 +0100260
Jesse Keatingbe4ef8a2016-12-06 11:29:13 -0800261 @simple_layout('layouts/basic-github.yaml', driver='github')
262 def test_git_enterprise_url(self):
263 """Test that git_url option gives git url with proper host"""
Tobias Henkel42f7d6f2017-12-15 16:09:43 +0100264 tenant = self.sched.abide.tenants.get('tenant-one')
265 _, project = tenant.getProject('org/project')
266
267 url = self.fake_github_ent.real_getGitUrl(project)
Jesse Keatingbe4ef8a2016-12-06 11:29:13 -0800268 self.assertEqual('ssh://git@github.enterprise.io/org/project.git', url)
269
Jan Hrubane252a732017-01-03 15:03:09 +0100270 @simple_layout('layouts/reporting-github.yaml', driver='github')
271 def test_reporting(self):
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700272 project = 'org/project'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200273 github = self.fake_github.github_client
274
Jan Hrubane252a732017-01-03 15:03:09 +0100275 # pipeline reports pull status both on start and success
276 self.executor_server.hold_jobs_in_build = True
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700277 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
Jan Hruban37615e52015-11-19 14:30:49 +0100278 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
Jan Hrubane252a732017-01-03 15:03:09 +0100279 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200280
Jesse Keatingd96e5882017-01-19 13:55:50 -0800281 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200282 self.assertIn(
283 A.head_sha, github.repo_from_project(project)._commits.keys())
284 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
285
Jesse Keatingd96e5882017-01-19 13:55:50 -0800286 # We should only have one status for the head sha
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700287 self.assertEqual(1, len(statuses))
288 check_status = statuses[0]
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100289 check_url = ('http://zuul.example.com/status/#%s,%s' %
290 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000291 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700292 self.assertEqual('check status: pending',
293 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100294 self.assertEqual('pending', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100295 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100296 self.assertEqual(0, len(A.comments))
Jan Hrubane252a732017-01-03 15:03:09 +0100297
298 self.executor_server.hold_jobs_in_build = False
299 self.executor_server.release()
300 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800301 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200302 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700303 self.assertEqual(2, len(statuses))
304 check_status = statuses[0]
Jesse Keatingd96e5882017-01-19 13:55:50 -0800305 check_url = ('http://zuul.example.com/status/#%s,%s' %
306 (A.number, A.head_sha))
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000307 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700308 self.assertEqual('check status: success',
309 check_status['description'])
Jan Hrubane252a732017-01-03 15:03:09 +0100310 self.assertEqual('success', check_status['state'])
Jan Hrubanddeb95a2017-01-03 15:12:41 +0100311 self.assertEqual(check_url, check_status['url'])
Jan Hruban37615e52015-11-19 14:30:49 +0100312 self.assertEqual(1, len(A.comments))
313 self.assertThat(A.comments[0],
Jan Hrubane252a732017-01-03 15:03:09 +0100314 MatchesRegex('.*Build succeeded.*', re.DOTALL))
315
316 # pipeline does not report any status but does comment
317 self.executor_server.hold_jobs_in_build = True
318 self.fake_github.emitEvent(
Jan Hruban37615e52015-11-19 14:30:49 +0100319 A.getCommentAddedEvent('reporting check'))
Jan Hrubane252a732017-01-03 15:03:09 +0100320 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200321 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700322 self.assertEqual(2, len(statuses))
Jan Hrubane252a732017-01-03 15:03:09 +0100323 # comments increased by one for the start message
Jan Hruban37615e52015-11-19 14:30:49 +0100324 self.assertEqual(2, len(A.comments))
325 self.assertThat(A.comments[1],
Jan Hrubane252a732017-01-03 15:03:09 +0100326 MatchesRegex('.*Starting reporting jobs.*', re.DOTALL))
327 self.executor_server.hold_jobs_in_build = False
328 self.executor_server.release()
329 self.waitUntilSettled()
Jesse Keatingd96e5882017-01-19 13:55:50 -0800330 # pipeline reports success status
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200331 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating1f7ebe92017-06-12 17:21:00 -0700332 self.assertEqual(3, len(statuses))
333 report_status = statuses[0]
Jamie Lennox18bc7ed2017-05-10 10:37:55 +1000334 self.assertEqual('tenant-one/reporting', report_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700335 self.assertEqual('reporting status: success',
336 report_status['description'])
Jesse Keatingd96e5882017-01-19 13:55:50 -0800337 self.assertEqual('success', report_status['state'])
Jan Hruban37615e52015-11-19 14:30:49 +0100338 self.assertEqual(2, len(A.comments))
Jamie Lennox3f16de52017-05-09 14:24:11 +1000339
340 base = 'http://logs.example.com/tenant-one/reporting/%s/%s/' % (
341 A.project, A.number)
342
343 # Deconstructing the URL because we don't save the BuildSet UUID
344 # anywhere to do a direct comparison and doing regexp matches on a full
345 # URL is painful.
346
347 # The first part of the URL matches the easy base string
348 self.assertThat(report_status['url'], StartsWith(base))
349
350 # The rest of the URL is a UUID and a trailing slash.
351 self.assertThat(report_status['url'][len(base):],
352 MatchesRegex('^[a-fA-F0-9]{32}\/$'))
Jan Hruban49bff072015-11-03 11:45:46 +0100353
Jesse Keating08dab8f2017-06-21 12:59:23 +0100354 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700355 def test_truncated_status_description(self):
356 project = 'org/project'
357 # pipeline reports pull status both on start and success
358 self.executor_server.hold_jobs_in_build = True
359 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
360 self.fake_github.emitEvent(
361 A.getCommentAddedEvent('long pipeline'))
362 self.waitUntilSettled()
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200363 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700364 self.assertEqual(1, len(statuses))
365 check_status = statuses[0]
366 # Status is truncated due to long pipeline name
367 self.assertEqual('status: pending',
368 check_status['description'])
369
370 self.executor_server.hold_jobs_in_build = False
371 self.executor_server.release()
372 self.waitUntilSettled()
373 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200374 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700375 self.assertEqual(2, len(statuses))
376 check_status = statuses[0]
377 # Status is truncated due to long pipeline name
378 self.assertEqual('status: success',
379 check_status['description'])
380
381 @simple_layout('layouts/reporting-github.yaml', driver='github')
Jesse Keating08dab8f2017-06-21 12:59:23 +0100382 def test_push_reporting(self):
383 project = 'org/project2'
384 # pipeline reports pull status both on start and success
385 self.executor_server.hold_jobs_in_build = True
Jesse Keating08dab8f2017-06-21 12:59:23 +0100386
James E. Blair289f5932017-07-27 15:02:29 -0700387 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
388 old_sha = '0' * 40
389 new_sha = A.head_sha
390 A.setMerged("merging A")
391 pevent = self.fake_github.getPushEvent(project=project,
392 ref='refs/heads/master',
393 old_rev=old_sha,
394 new_rev=new_sha)
Jesse Keating08dab8f2017-06-21 12:59:23 +0100395 self.fake_github.emitEvent(pevent)
396 self.waitUntilSettled()
397
398 # there should only be one report, a status
399 self.assertEqual(1, len(self.fake_github.reports))
400 # Verify the user/context/state of the status
401 status = ('zuul', 'tenant-one/push-reporting', 'pending')
402 self.assertEqual(status, self.fake_github.reports[0][-1])
403
404 # free the executor, allow the build to finish
405 self.executor_server.hold_jobs_in_build = False
406 self.executor_server.release()
407 self.waitUntilSettled()
408
409 # Now there should be a second report, the success of the build
410 self.assertEqual(2, len(self.fake_github.reports))
411 # Verify the user/context/state of the status
412 status = ('zuul', 'tenant-one/push-reporting', 'success')
413 self.assertEqual(status, self.fake_github.reports[-1][-1])
414
415 # now make a PR which should also comment
416 self.executor_server.hold_jobs_in_build = True
417 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
418 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
419 self.waitUntilSettled()
420
421 # Now there should be a four reports, a new comment
422 # and status
423 self.assertEqual(4, len(self.fake_github.reports))
424 self.executor_server.release()
425 self.waitUntilSettled()
426
Jan Hruban49bff072015-11-03 11:45:46 +0100427 @simple_layout('layouts/merging-github.yaml', driver='github')
428 def test_report_pull_merge(self):
429 # pipeline merges the pull request on success
Jan Hruban3b415922016-02-03 13:10:22 +0100430 A = self.fake_github.openFakePullRequest('org/project', 'master',
431 'PR title')
Jan Hruban49bff072015-11-03 11:45:46 +0100432 self.fake_github.emitEvent(A.getCommentAddedEvent('merge me'))
433 self.waitUntilSettled()
434 self.assertTrue(A.is_merged)
Jan Hruban3b415922016-02-03 13:10:22 +0100435 self.assertThat(A.merge_message,
436 MatchesRegex('.*PR title.*Reviewed-by.*', re.DOTALL))
Jan Hruban49bff072015-11-03 11:45:46 +0100437
438 # pipeline merges the pull request on success after failure
439 self.fake_github.merge_failure = True
Jan Hruban37615e52015-11-19 14:30:49 +0100440 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
Jan Hruban49bff072015-11-03 11:45:46 +0100441 self.fake_github.emitEvent(B.getCommentAddedEvent('merge me'))
442 self.waitUntilSettled()
443 self.assertFalse(B.is_merged)
444 self.fake_github.merge_failure = False
445
446 # pipeline merges the pull request on second run of merge
447 # first merge failed on 405 Method Not Allowed error
448 self.fake_github.merge_not_allowed_count = 1
Jan Hruban37615e52015-11-19 14:30:49 +0100449 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
Jan Hruban49bff072015-11-03 11:45:46 +0100450 self.fake_github.emitEvent(C.getCommentAddedEvent('merge me'))
451 self.waitUntilSettled()
452 self.assertTrue(C.is_merged)
453
454 # pipeline does not merge the pull request
455 # merge failed on 405 Method Not Allowed error - twice
456 self.fake_github.merge_not_allowed_count = 2
Jan Hruban37615e52015-11-19 14:30:49 +0100457 D = self.fake_github.openFakePullRequest('org/project', 'master', 'D')
Jan Hruban49bff072015-11-03 11:45:46 +0100458 self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
459 self.waitUntilSettled()
460 self.assertFalse(D.is_merged)
Adam Gandelman62198cb2017-02-14 16:11:02 -0800461 self.assertEqual(len(D.comments), 1)
462 self.assertEqual(D.comments[0], 'Merge failed')
Jan Hruban37615e52015-11-19 14:30:49 +0100463
Jesse Keating28434312017-07-31 11:32:48 -0700464 @simple_layout('layouts/reporting-multiple-github.yaml', driver='github')
465 def test_reporting_multiple_github(self):
466 project = 'org/project1'
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200467 github = self.fake_github.github_client
468
Jesse Keating28434312017-07-31 11:32:48 -0700469 # pipeline reports pull status both on start and success
470 self.executor_server.hold_jobs_in_build = True
471 A = self.fake_github.openFakePullRequest(project, 'master', 'A')
472 self.fake_github.emitEvent(A.getPullRequestOpenedEvent())
473 # open one on B as well, which should not effect A reporting
474 B = self.fake_github.openFakePullRequest('org/project2', 'master',
475 'B')
476 self.fake_github.emitEvent(B.getPullRequestOpenedEvent())
477 self.waitUntilSettled()
478 # We should have a status container for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200479 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
480 self.assertIn(
481 A.head_sha, github.repo_from_project(project)._commits.keys())
Jesse Keating28434312017-07-31 11:32:48 -0700482 # We should only have one status for the head sha
483 self.assertEqual(1, len(statuses))
484 check_status = statuses[0]
485 check_url = ('http://zuul.example.com/status/#%s,%s' %
486 (A.number, A.head_sha))
487 self.assertEqual('tenant-one/check', check_status['context'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700488 self.assertEqual('check status: pending', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700489 self.assertEqual('pending', check_status['state'])
490 self.assertEqual(check_url, check_status['url'])
491 self.assertEqual(0, len(A.comments))
492
493 self.executor_server.hold_jobs_in_build = False
494 self.executor_server.release()
495 self.waitUntilSettled()
496 # We should only have two statuses for the head sha
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200497 statuses = self.fake_github.getCommitStatuses(project, A.head_sha)
Jesse Keating28434312017-07-31 11:32:48 -0700498 self.assertEqual(2, len(statuses))
499 check_status = statuses[0]
500 check_url = ('http://zuul.example.com/status/#%s,%s' %
501 (A.number, A.head_sha))
502 self.assertEqual('tenant-one/check', check_status['context'])
503 self.assertEqual('success', check_status['state'])
Jesse Keatingfb6cc992017-08-01 14:18:13 -0700504 self.assertEqual('check status: success', check_status['description'])
Jesse Keating28434312017-07-31 11:32:48 -0700505 self.assertEqual(check_url, check_status['url'])
506 self.assertEqual(1, len(A.comments))
507 self.assertThat(A.comments[0],
508 MatchesRegex('.*Build succeeded.*', re.DOTALL))
509
Jan Hruban37615e52015-11-19 14:30:49 +0100510 @simple_layout('layouts/dependent-github.yaml', driver='github')
511 def test_parallel_changes(self):
512 "Test that changes are tested in parallel and merged in series"
513
514 self.executor_server.hold_jobs_in_build = True
515 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
516 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
517 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
518
519 self.fake_github.emitEvent(A.addLabel('merge'))
520 self.fake_github.emitEvent(B.addLabel('merge'))
521 self.fake_github.emitEvent(C.addLabel('merge'))
522
523 self.waitUntilSettled()
524 self.assertEqual(len(self.builds), 1)
525 self.assertEqual(self.builds[0].name, 'project-merge')
526 self.assertTrue(self.builds[0].hasChanges(A))
527
528 self.executor_server.release('.*-merge')
529 self.waitUntilSettled()
530 self.assertEqual(len(self.builds), 3)
531 self.assertEqual(self.builds[0].name, 'project-test1')
532 self.assertTrue(self.builds[0].hasChanges(A))
533 self.assertEqual(self.builds[1].name, 'project-test2')
534 self.assertTrue(self.builds[1].hasChanges(A))
535 self.assertEqual(self.builds[2].name, 'project-merge')
536 self.assertTrue(self.builds[2].hasChanges(A, B))
537
538 self.executor_server.release('.*-merge')
539 self.waitUntilSettled()
540 self.assertEqual(len(self.builds), 5)
541 self.assertEqual(self.builds[0].name, 'project-test1')
542 self.assertTrue(self.builds[0].hasChanges(A))
543 self.assertEqual(self.builds[1].name, 'project-test2')
544 self.assertTrue(self.builds[1].hasChanges(A))
545
546 self.assertEqual(self.builds[2].name, 'project-test1')
547 self.assertTrue(self.builds[2].hasChanges(A))
548 self.assertEqual(self.builds[3].name, 'project-test2')
549 self.assertTrue(self.builds[3].hasChanges(A, B))
550
551 self.assertEqual(self.builds[4].name, 'project-merge')
552 self.assertTrue(self.builds[4].hasChanges(A, B, C))
553
554 self.executor_server.release('.*-merge')
555 self.waitUntilSettled()
556 self.assertEqual(len(self.builds), 6)
557 self.assertEqual(self.builds[0].name, 'project-test1')
558 self.assertTrue(self.builds[0].hasChanges(A))
559 self.assertEqual(self.builds[1].name, 'project-test2')
560 self.assertTrue(self.builds[1].hasChanges(A))
561
562 self.assertEqual(self.builds[2].name, 'project-test1')
563 self.assertTrue(self.builds[2].hasChanges(A, B))
564 self.assertEqual(self.builds[3].name, 'project-test2')
565 self.assertTrue(self.builds[3].hasChanges(A, B))
566
567 self.assertEqual(self.builds[4].name, 'project-test1')
568 self.assertTrue(self.builds[4].hasChanges(A, B, C))
569 self.assertEqual(self.builds[5].name, 'project-test2')
570 self.assertTrue(self.builds[5].hasChanges(A, B, C))
571
572 all_builds = self.builds[:]
573 self.release(all_builds[2])
574 self.release(all_builds[3])
575 self.waitUntilSettled()
576 self.assertFalse(A.is_merged)
577 self.assertFalse(B.is_merged)
578 self.assertFalse(C.is_merged)
579
580 self.release(all_builds[0])
581 self.release(all_builds[1])
582 self.waitUntilSettled()
583 self.assertTrue(A.is_merged)
584 self.assertTrue(B.is_merged)
585 self.assertFalse(C.is_merged)
586
587 self.executor_server.hold_jobs_in_build = False
588 self.executor_server.release()
589 self.waitUntilSettled()
590 self.assertEqual(len(self.builds), 0)
591 self.assertEqual(len(self.history), 9)
592 self.assertTrue(C.is_merged)
593
594 self.assertNotIn('merge', A.labels)
595 self.assertNotIn('merge', B.labels)
596 self.assertNotIn('merge', C.labels)
597
598 @simple_layout('layouts/dependent-github.yaml', driver='github')
599 def test_failed_changes(self):
600 "Test that a change behind a failed change is retested"
601 self.executor_server.hold_jobs_in_build = True
602
603 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
604 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
605
606 self.executor_server.failJob('project-test1', A)
607
608 self.fake_github.emitEvent(A.addLabel('merge'))
609 self.fake_github.emitEvent(B.addLabel('merge'))
610 self.waitUntilSettled()
611
612 self.executor_server.release('.*-merge')
613 self.waitUntilSettled()
614
615 self.executor_server.hold_jobs_in_build = False
616 self.executor_server.release()
617
618 self.waitUntilSettled()
619 # It's certain that the merge job for change 2 will run, but
620 # the test1 and test2 jobs may or may not run.
621 self.assertTrue(len(self.history) > 6)
622 self.assertFalse(A.is_merged)
623 self.assertTrue(B.is_merged)
624 self.assertNotIn('merge', A.labels)
625 self.assertNotIn('merge', B.labels)
626
627 @simple_layout('layouts/dependent-github.yaml', driver='github')
628 def test_failed_change_at_head(self):
629 "Test that if a change at the head fails, jobs behind it are canceled"
630
631 self.executor_server.hold_jobs_in_build = True
632 A = self.fake_github.openFakePullRequest('org/project', 'master', 'A')
633 B = self.fake_github.openFakePullRequest('org/project', 'master', 'B')
634 C = self.fake_github.openFakePullRequest('org/project', 'master', 'C')
635
636 self.executor_server.failJob('project-test1', A)
637
638 self.fake_github.emitEvent(A.addLabel('merge'))
639 self.fake_github.emitEvent(B.addLabel('merge'))
640 self.fake_github.emitEvent(C.addLabel('merge'))
641
642 self.waitUntilSettled()
643
644 self.assertEqual(len(self.builds), 1)
645 self.assertEqual(self.builds[0].name, 'project-merge')
646 self.assertTrue(self.builds[0].hasChanges(A))
647
648 self.executor_server.release('.*-merge')
649 self.waitUntilSettled()
650 self.executor_server.release('.*-merge')
651 self.waitUntilSettled()
652 self.executor_server.release('.*-merge')
653 self.waitUntilSettled()
654
655 self.assertEqual(len(self.builds), 6)
656 self.assertEqual(self.builds[0].name, 'project-test1')
657 self.assertEqual(self.builds[1].name, 'project-test2')
658 self.assertEqual(self.builds[2].name, 'project-test1')
659 self.assertEqual(self.builds[3].name, 'project-test2')
660 self.assertEqual(self.builds[4].name, 'project-test1')
661 self.assertEqual(self.builds[5].name, 'project-test2')
662
663 self.release(self.builds[0])
664 self.waitUntilSettled()
665
666 # project-test2, project-merge for B
667 self.assertEqual(len(self.builds), 2)
668 self.assertEqual(self.countJobResults(self.history, 'ABORTED'), 4)
669
670 self.executor_server.hold_jobs_in_build = False
671 self.executor_server.release()
672 self.waitUntilSettled()
673
674 self.assertEqual(len(self.builds), 0)
675 self.assertEqual(len(self.history), 15)
676 self.assertFalse(A.is_merged)
677 self.assertTrue(B.is_merged)
678 self.assertTrue(C.is_merged)
679 self.assertNotIn('merge', A.labels)
680 self.assertNotIn('merge', B.labels)
681 self.assertNotIn('merge', C.labels)
Jesse Keating71a47ff2017-06-06 11:36:43 -0700682
683 @simple_layout('layouts/basic-github.yaml', driver='github')
684 def test_push_event_reconfigure(self):
Tobias Henkel3c17d5f2017-08-03 11:46:54 +0200685 pevent = self.fake_github.getPushEvent(project='org/common-config',
Jesse Keating71a47ff2017-06-06 11:36:43 -0700686 ref='refs/heads/master',
687 modified_files=['zuul.yaml'])
688
689 # record previous tenant reconfiguration time, which may not be set
690 old = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
691 time.sleep(1)
692 self.fake_github.emitEvent(pevent)
693 self.waitUntilSettled()
694 new = self.sched.tenant_last_reconfigured.get('tenant-one', 0)
695 # New timestamp should be greater than the old timestamp
696 self.assertLess(old, new)
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +0000697
Jesse Keating64d29012017-09-06 12:27:49 -0700698 # TODO(jlk): Make this a more generic test for unknown project
699 @skip("Skipped for rewrite of webhook handler")
Tristan Cacqueray2bafb1f2017-06-12 07:10:26 +0000700 @simple_layout('layouts/basic-github.yaml', driver='github')
701 def test_ping_event(self):
702 # Test valid ping
703 pevent = {'repository': {'full_name': 'org/project'}}
704 req = self.fake_github.emitEvent(('ping', pevent))
705 self.assertEqual(req.status, 200, "Ping event didn't succeed")
706
707 # Test invalid ping
708 pevent = {'repository': {'full_name': 'unknown-project'}}
709 self.assertRaises(
710 urllib.error.HTTPError,
711 self.fake_github.emitEvent,
712 ('ping', pevent),
713 )
Tobias Henkeleca46202017-08-02 20:27:10 +0200714
715
716class TestGithubUnprotectedBranches(ZuulTestCase):
717 config_file = 'zuul-github-driver.conf'
718 tenant_config_file = 'config/unprotected-branches/main.yaml'
719
720 def test_unprotected_branches(self):
721 tenant = self.sched.abide.tenants.get('tenant-one')
722
723 project1 = tenant.untrusted_projects[0]
724 project2 = tenant.untrusted_projects[1]
725
726 # project1 should have parsed master
727 self.assertIn('master', project1.unparsed_branch_config.keys())
728
729 # project2 should have no parsed branch
730 self.assertEqual(0, len(project2.unparsed_branch_config.keys()))