mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 15:35:46 +07:00

In that case, ignore the reopen, close the PR, and tell the idiot to
fuck off.
Also the case where a PR is reopened while its batch is staged was
already handled, but probably not tested: it was implicit in
forcefully updating the HEAD of the PR, which triggers an unstage
since c8a06601a7
.
Now that scenario is tested, which should lower the odds of breaking
it in the future.
Fixes #965
309 lines
9.7 KiB
Python
309 lines
9.7 KiB
Python
"""This module tests edge cases specific to the batch objects themselves,
|
|
without wider relevance and thus other location.
|
|
"""
|
|
import pytest
|
|
|
|
from utils import Commit, to_pr, pr_page, seen
|
|
|
|
|
|
def test_close_single(env, repo):
|
|
"""If a batch has a single PR and that PR gets closed, the batch should be
|
|
inactive *and* blocked.
|
|
"""
|
|
with repo:
|
|
repo.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
[c] = repo.make_commits('master', Commit('b', tree={"b": "b"}))
|
|
pr = repo.make_pr(head=c, target='master')
|
|
env.run_crons()
|
|
|
|
pr_id = to_pr(env, pr)
|
|
batch_id = pr_id.batch_id
|
|
assert pr_id.state == 'opened'
|
|
assert batch_id.blocked
|
|
Batches = env['runbot_merge.batch']
|
|
assert Batches.search_count([]) == 1
|
|
|
|
with repo:
|
|
pr.close()
|
|
|
|
assert pr_id.state == 'closed'
|
|
assert batch_id.all_prs == pr_id
|
|
assert batch_id.prs == pr_id.browse(())
|
|
assert batch_id.blocked == "all prs are closed"
|
|
assert not batch_id.active
|
|
|
|
assert Batches.search_count([]) == 0
|
|
|
|
def test_close_multiple(env, make_repo2):
|
|
Batches = env['runbot_merge.batch']
|
|
repo1 = make_repo2('wheee')
|
|
repo2 = make_repo2('wheeee')
|
|
|
|
with repo1:
|
|
repo1.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
repo1.make_commits('master', Commit('b', tree={"b": "b"}), ref='heads/a_pr')
|
|
pr1 = repo1.make_pr(head='a_pr', target='master')
|
|
|
|
with repo2:
|
|
repo2.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
repo2.make_commits('master', Commit('b', tree={"b": "b"}), ref='heads/a_pr')
|
|
pr2 = repo2.make_pr(head='a_pr', target='master')
|
|
|
|
pr1_id = to_pr(env, pr1)
|
|
pr2_id = to_pr(env, pr2)
|
|
batch_id = pr1_id.batch_id
|
|
assert pr2_id.batch_id == batch_id
|
|
|
|
assert pr1_id.state == 'opened'
|
|
assert pr2_id.state == 'opened'
|
|
assert batch_id.all_prs == pr1_id | pr2_id
|
|
assert batch_id.prs == pr1_id | pr2_id
|
|
assert batch_id.active
|
|
assert Batches.search_count([]) == 1
|
|
|
|
with repo1:
|
|
pr1.close()
|
|
|
|
assert pr1_id.state == 'closed'
|
|
assert pr2_id.state == 'opened'
|
|
assert batch_id.all_prs == pr1_id | pr2_id
|
|
assert batch_id.prs == pr2_id
|
|
assert batch_id.active
|
|
assert Batches.search_count([]) == 1
|
|
|
|
with repo2:
|
|
pr2.close()
|
|
|
|
assert pr1_id.state == 'closed'
|
|
assert pr2_id.state == 'closed'
|
|
assert batch_id.all_prs == pr1_id | pr2_id
|
|
assert batch_id.prs == env['runbot_merge.pull_requests'].browse(())
|
|
assert not batch_id.active
|
|
assert Batches.search_count([]) == 0
|
|
|
|
def test_inconsistent_target(env, project, make_repo2, users, page, config):
|
|
"""If a batch's PRs have inconsistent targets,
|
|
|
|
- only open PRs should count
|
|
- it should be clearly notified on the dash
|
|
- the dash should not get hopelessly lost
|
|
- there should be a wizard to split the batch / move a PR to a separate batch
|
|
"""
|
|
# region setup
|
|
Batches = env['runbot_merge.batch']
|
|
repo1 = make_repo2('whe')
|
|
repo2 = make_repo2('whee')
|
|
repo3 = make_repo2('wheee')
|
|
project.write({'branch_ids': [(0, 0, {'name': 'other'})]})
|
|
|
|
with repo1:
|
|
[m] = repo1.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
repo1.make_ref('heads/other', m)
|
|
repo1.make_commits('master', Commit('b', tree={"b": "b"}), ref='heads/a_pr')
|
|
pr1 = repo1.make_pr(head='a_pr', target='master')
|
|
|
|
repo1.make_commits('master', Commit('b', tree={"c": "c"}), ref='heads/something_else')
|
|
pr_other = repo1.make_pr(head='something_else', target='master')
|
|
|
|
with repo2:
|
|
[m] = repo2.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
repo2.make_ref("heads/other", m)
|
|
repo2.make_commits('master', Commit('b', tree={"b": "b"}), ref='heads/a_pr')
|
|
pr2 = repo2.make_pr(head='a_pr', target='master')
|
|
|
|
with repo3:
|
|
[m] = repo3.make_commits(None, Commit("a", tree={"a": "a"}), ref='heads/master')
|
|
repo3.make_ref("heads/other", m)
|
|
repo3.make_commits('master', Commit('b', tree={"b": "b"}), ref='heads/a_pr')
|
|
pr3 = repo3.make_pr(head='a_pr', target='master')
|
|
|
|
assert repo1.owner == repo2.owner == repo3.owner
|
|
owner = repo1.owner
|
|
# endregion
|
|
|
|
# region closeable consistency
|
|
|
|
[b] = Batches.search([('all_prs.label', '=', f'{owner}:a_pr')])
|
|
assert b.target.name == 'master'
|
|
assert len(b.prs) == 3
|
|
assert len(b.all_prs) == 3
|
|
|
|
with repo3:
|
|
pr3.base = 'other'
|
|
assert b.target.name == False
|
|
assert len(b.prs) == 3
|
|
assert len(b.all_prs) == 3
|
|
|
|
with repo3:
|
|
pr3.close()
|
|
assert b.target.name == 'master'
|
|
assert len(b.prs) == 2
|
|
assert len(b.all_prs) == 3
|
|
# endregion
|
|
|
|
# region split batch
|
|
pr1_id = to_pr(env, pr1)
|
|
pr2_id = to_pr(env, pr2)
|
|
with repo2:
|
|
pr2.base = 'other'
|
|
|
|
pr2_dashboard = pr_page(page, pr2)
|
|
# The dashboard should have an alert
|
|
s = pr2_dashboard.cssselect('.alert.alert-danger')
|
|
assert s, "the dashboard should have an alert"
|
|
assert s[0].text_content().strip() == f"""\
|
|
Inconsistent targets:
|
|
|
|
{pr1_id.display_name} has target 'master'
|
|
{pr2_id.display_name} has target 'other'\
|
|
"""
|
|
assert not pr2_dashboard.cssselect('table'), "the batches table should be suppressed"
|
|
|
|
assert b.target.name == False
|
|
assert to_pr(env, pr_other).label == f'{owner}:something_else'
|
|
# try staging
|
|
with repo1:
|
|
pr1.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
repo1.post_status(pr1.head, "success")
|
|
with repo2:
|
|
pr2.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
repo2.post_status(pr2.head, "success")
|
|
env.run_crons()
|
|
assert not pr1_id.blocked
|
|
assert not pr2_id.blocked
|
|
assert b.blocked == "Multiple target branches: 'other, master'"
|
|
assert env['runbot_merge.stagings'].search_count([]) == 0
|
|
|
|
act = pr2_id.button_split()
|
|
assert act['type'] == 'ir.actions.act_window'
|
|
assert act['views'] == [[False, 'form']]
|
|
assert act['target'] == 'new'
|
|
w = env[act['res_model']].browse([act['res_id']])
|
|
w.new_label = f"{owner}:something_else"
|
|
with pytest.raises(Exception):
|
|
w.button_apply()
|
|
w.new_label = f"{owner}:blah-blah-blah"
|
|
w.button_apply()
|
|
|
|
assert pr2_id.label == f"{owner}:blah-blah-blah"
|
|
assert pr2_id.batch_id != to_pr(env, pr1).batch_id
|
|
assert b.target.name == 'master'
|
|
assert len(b.prs) == 1, "the PR has been moved off of this batch entirely"
|
|
assert len(b.all_prs) == 2
|
|
# endregion
|
|
|
|
assert not pr1_id.blocked
|
|
assert not pr1_id.batch_id.blocked
|
|
assert not pr2_id.blocked
|
|
assert not pr2_id.batch_id.blocked
|
|
env.run_crons()
|
|
|
|
assert env['runbot_merge.stagings'].search_count([])
|
|
|
|
def test_reopen_pr_in_staged_batch(env, project, make_repo2, config):
|
|
"""Reopening a closed PR from a staged batch should cancel the staging
|
|
"""
|
|
repo1 = make_repo2('a')
|
|
repo2 = make_repo2('b')
|
|
|
|
with repo1:
|
|
[m1, _] = repo1.make_commits(
|
|
None,
|
|
Commit('a', tree={'a': 'a'}),
|
|
Commit('b', tree={'b': 'b'}),
|
|
ref='heads/p',
|
|
)
|
|
repo1.make_ref('heads/master', m1)
|
|
pr1 = repo1.make_pr(target='master', head='p')
|
|
with repo2:
|
|
[m2, _] = repo2.make_commits(
|
|
None,
|
|
Commit('a', tree={'a': 'a'}),
|
|
Commit('b', tree={'b': 'b'}),
|
|
ref='heads/p',
|
|
)
|
|
repo2.make_ref('heads/master', m2)
|
|
pr2 = repo2.make_pr(target='master', head='p')
|
|
|
|
pr1_id = to_pr(env, pr1)
|
|
pr2_id = to_pr(env, pr2)
|
|
batch_id = pr1_id.batch_id
|
|
assert batch_id
|
|
assert batch_id == pr2_id.batch_id
|
|
|
|
with repo1:
|
|
repo1.post_status(pr1.head, 'success')
|
|
pr1.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
with repo2:
|
|
pr2.close()
|
|
|
|
env.run_crons(None)
|
|
|
|
assert pr2_id.state == 'closed'
|
|
assert batch_id.staging_ids.filtered(lambda s: s.active)
|
|
|
|
with repo2:
|
|
pr2.open()
|
|
assert pr2_id.state == 'opened'
|
|
assert not batch_id.staging_ids.filtered(lambda s: s.active)
|
|
assert batch_id.blocked
|
|
|
|
def test_reopen_pr_in_merged_batch(env, project, make_repo2, config, users):
|
|
"""If the batch is merged, the pr should just be re-closed with a message
|
|
"""
|
|
repo1 = make_repo2('a')
|
|
repo2 = make_repo2('b')
|
|
|
|
with repo1:
|
|
[m1, _] = repo1.make_commits(
|
|
None,
|
|
Commit('a', tree={'a': 'a'}),
|
|
Commit('b', tree={'b': 'b'}),
|
|
ref='heads/p',
|
|
)
|
|
repo1.make_ref('heads/master', m1)
|
|
pr1 = repo1.make_pr(target='master', head='p')
|
|
with repo2:
|
|
[m2, _] = repo2.make_commits(
|
|
None,
|
|
Commit('a', tree={'a': 'a'}),
|
|
Commit('b', tree={'b': 'b'}),
|
|
ref='heads/p',
|
|
)
|
|
repo2.make_ref('heads/master', m2)
|
|
pr2 = repo2.make_pr(target='master', head='p')
|
|
|
|
pr1_id = to_pr(env, pr1)
|
|
pr2_id = to_pr(env, pr2)
|
|
batch_id = pr1_id.batch_id
|
|
assert batch_id
|
|
assert batch_id == pr2_id.batch_id
|
|
|
|
with repo1:
|
|
repo1.post_status(pr1.head, 'success')
|
|
pr1.post_comment("hansen r+", config['role_reviewer']['token'])
|
|
with repo2:
|
|
pr2.close()
|
|
|
|
env.run_crons(None)
|
|
|
|
with repo1, repo2:
|
|
repo1.post_status('staging.master', 'success')
|
|
repo2.post_status('staging.master', 'success')
|
|
env.run_crons(None)
|
|
|
|
|
|
assert pr1_id.state == 'merged'
|
|
assert pr2_id.state == 'closed'
|
|
|
|
with repo2:
|
|
pr2.open()
|
|
env.run_crons(None)
|
|
|
|
assert pr2_id.closed
|
|
assert pr2_id.state == 'closed'
|
|
assert pr2.comments == [
|
|
seen(env, pr2, users),
|
|
(users['user'], 'Reopening a PR in a merged batch is not allowed, create a new PR.'),
|
|
]
|