mirror of
https://github.com/odoo/runbot.git
synced 2025-03-16 16:05:42 +07:00

The `statuses` field of a staging is always "live" because it's a computed non-stored field. This is an issue when a staging finishes in whatever state, then someone gets new statuses sent on one of the head commits, either by rebuilding (part of) the staging or by just using the same commit for one of their branches. This makes the reporting of the main dashboard confusing, as one might look at a failed staging and see all the required statuses successful. It also makes post-mortem analysis more complicated as the logs have to be trawled for what the statuses used to be (and they don't always tell). Solve this by storing a snapshot of the statuses the first time a staging moves away from `pending`, whether it's to success or failure. Fixes #667
180 lines
6.0 KiB
Python
180 lines
6.0 KiB
Python
import requests
|
|
|
|
from utils import Commit, to_pr, seen
|
|
|
|
|
|
def test_partner_merge(env):
|
|
p_src = env['res.partner'].create({
|
|
'name': "xxx",
|
|
'github_login': 'xxx'
|
|
})
|
|
# proper login with useful info
|
|
p_dest = env['res.partner'].create({
|
|
'name': 'Partner P. Partnersson',
|
|
'github_login': ''
|
|
})
|
|
|
|
env['base.partner.merge.automatic.wizard'].create({
|
|
'state': 'selection',
|
|
'partner_ids': (p_src + p_dest).ids,
|
|
'dst_partner_id': p_dest.id,
|
|
}).action_merge()
|
|
assert not p_src.exists()
|
|
assert p_dest.name == 'Partner P. Partnersson'
|
|
assert p_dest.github_login == 'xxx'
|
|
|
|
def test_name_search(env):
|
|
""" PRs should be findable by:
|
|
|
|
* number
|
|
* display_name (`repository#number`)
|
|
* label
|
|
|
|
This way we can find parents or sources by these informations.
|
|
"""
|
|
p = env['runbot_merge.project'].create({
|
|
'name': 'proj',
|
|
'github_token': 'no',
|
|
})
|
|
b = env['runbot_merge.branch'].create({
|
|
'name': 'target',
|
|
'project_id': p.id
|
|
})
|
|
r = env['runbot_merge.repository'].create({
|
|
'name': 'repo',
|
|
'project_id': p.id,
|
|
})
|
|
|
|
baseline = {'target': b.id, 'repository': r.id}
|
|
PRs = env['runbot_merge.pull_requests']
|
|
prs = PRs.create({**baseline, 'number': 1964, 'label': 'victor:thump', 'head': 'a', 'message': 'x'})\
|
|
| PRs.create({**baseline, 'number': 1959, 'label': 'marcus:frankenstein', 'head': 'b', 'message': 'y'})\
|
|
| PRs.create({**baseline, 'number': 1969, 'label': 'victor:patch-1', 'head': 'c', 'message': 'z'})
|
|
pr0, pr1, pr2 = prs.name_get()
|
|
|
|
assert PRs.name_search('1964') == [pr0]
|
|
assert PRs.name_search('1969') == [pr2]
|
|
|
|
assert PRs.name_search('frank') == [pr1]
|
|
assert PRs.name_search('victor') == [pr2, pr0]
|
|
|
|
assert PRs.name_search('thump') == [pr0]
|
|
|
|
assert PRs.name_search('repo') == [pr2, pr0, pr1]
|
|
assert PRs.name_search('repo#1959') == [pr1]
|
|
|
|
def test_message_desync(env, project, make_repo, users, setreviewers, config):
|
|
"""If the PR message gets desync'd (github misses sending an update), the
|
|
merge message should still match what's on github rather than what's in the
|
|
db
|
|
"""
|
|
repo = make_repo('repo')
|
|
env['runbot_merge.repository'].create({
|
|
'project_id': project.id,
|
|
'name': repo.name,
|
|
'status_ids': [(0, 0, {'context': 'status'})]
|
|
})
|
|
setreviewers(*project.repo_ids)
|
|
|
|
with repo:
|
|
[m] = repo.make_commits(None, Commit('root', tree={'a': '1'}), ref='heads/master')
|
|
|
|
[c] = repo.make_commits('master', Commit('whee', tree={'b': '2'}))
|
|
pr = repo.make_pr(title='title', body='body', target='master', head=c)
|
|
repo.post_status(c, 'success', 'status')
|
|
env.run_crons()
|
|
|
|
pr_id = to_pr(env, pr)
|
|
assert pr_id.message == 'title\n\nbody'
|
|
pr_id.message = "xxx"
|
|
|
|
with repo:
|
|
pr.post_comment('hansen merge r+', config['role_reviewer']['token'])
|
|
env.run_crons()
|
|
|
|
st = repo.commit('staging.master')
|
|
assert st.message.startswith('title\n\nbody'),\
|
|
"the stored PR message should have been ignored when staging"
|
|
assert st.parents == [m, c], "check the staging's ancestry is the right one"
|
|
|
|
def test_unreviewer(env, project, port):
|
|
repo = env['runbot_merge.repository'].create({
|
|
'project_id': project.id,
|
|
'name': 'a_test_repo',
|
|
'status_ids': [(0, 0, {'context': 'status'})]
|
|
})
|
|
p = env['res.partner'].create({
|
|
'name': 'George Pearce',
|
|
'github_login': 'emubitch',
|
|
'review_rights': [(0, 0, {'repository_id': repo.id, 'review': True})]
|
|
})
|
|
|
|
r = requests.post(f'http://localhost:{port}/runbot_merge/get_reviewers', json={
|
|
'jsonrpc': '2.0',
|
|
'id': None,
|
|
'method': 'call',
|
|
'params': {},
|
|
})
|
|
r.raise_for_status()
|
|
assert 'error' not in r.json()
|
|
assert r.json()['result'] == ['emubitch']
|
|
|
|
r = requests.post(f'http://localhost:{port}/runbot_merge/remove_reviewers', json={
|
|
'jsonrpc': '2.0',
|
|
'id': None,
|
|
'method': 'call',
|
|
'params': {'github_logins': ['emubitch']},
|
|
})
|
|
r.raise_for_status()
|
|
assert 'error' not in r.json()
|
|
|
|
assert p.review_rights == env['res.partner.review']
|
|
|
|
def test_staging_post_update(env, project, make_repo, setreviewers, users, config):
|
|
"""Because statuses come from commits, it's possible to update the commits
|
|
of a staging after that staging has completed (one way or the other), either
|
|
by sending statuses directly (e.g. rebuilding, for non-deterministic errors)
|
|
or just using the staging's head commit in a branch.
|
|
|
|
This makes post-mortem analysis quite confusing, so stagings should
|
|
"lock in" their statuses once they complete.
|
|
"""
|
|
repo = make_repo('repo')
|
|
project.write({'repo_ids': [(0, 0, {
|
|
'name': repo.name,
|
|
'group_id': False,
|
|
'required_statuses': 'legal/cla,ci/runbot'
|
|
})]})
|
|
setreviewers(*project.repo_ids)
|
|
|
|
with repo:
|
|
[m] = repo.make_commits(None, Commit('initial', tree={'m': 'm'}), ref='heads/master')
|
|
|
|
repo.make_commits(m, Commit('thing', tree={'m': 'c'}), ref='heads/other')
|
|
pr = repo.make_pr(target='master', head='other')
|
|
repo.post_status(pr.head, 'success', 'ci/runbot')
|
|
repo.post_status(pr.head, 'success', 'legal/cla')
|
|
pr.post_comment('hansen r+ rebase-merge', config['role_reviewer']['token'])
|
|
env.run_crons()
|
|
pr_id = to_pr(env, pr)
|
|
staging_id = pr_id.staging_id
|
|
assert staging_id
|
|
|
|
staging_head = repo.commit('staging.master')
|
|
with repo:
|
|
repo.post_status(staging_head, 'failure', 'ci/runbot')
|
|
env.run_crons()
|
|
assert pr_id.state == 'error'
|
|
assert staging_id.state == 'failure'
|
|
assert staging_id.statuses == [
|
|
[repo.name, 'ci/runbot', 'failure', ''],
|
|
]
|
|
|
|
with repo:
|
|
repo.post_status(staging_head, 'success', 'ci/runbot')
|
|
env.run_crons()
|
|
assert staging_id.state == 'failure'
|
|
assert staging_id.statuses == [
|
|
[repo.name, 'ci/runbot', 'failure', ''],
|
|
]
|