[IMP] runbot_merge: pluginify github mock

This is the preparation of an attempt to make these tests work with
both a local github mock (in-memory) and a remote actual github.

Move a bunch of fixtures relying on the specific github
implementation (and odoo-as-library access) to the "local" plugin,
including splitting the "repo" fixture.

The specific fixtures will likely have to be adjusted as the
remote endpoint is fleshed out.
This commit is contained in:
Xavier Morel 2018-06-05 10:10:32 +02:00 committed by xmo-odoo
parent 5e64f79730
commit d98d40389d
6 changed files with 174 additions and 193 deletions

View File

View File

@ -1,57 +1,5 @@
import odoo pytest_plugins = ["local"]
import pytest
import fake_github
@pytest.fixture
def gh():
with fake_github.Github() as gh:
yield gh
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption("--db", action="store", help="Odoo DB to run tests with") parser.addoption("--db", action="store", help="Odoo DB to run tests with")
parser.addoption('--addons-path', action='store', help="Odoo's addons path") parser.addoption('--addons-path', action='store', help="Odoo's addons path")
@pytest.fixture(scope='session')
def registry(request):
""" Set up Odoo & yields a registry to the specified db
"""
db = request.config.getoption('--db')
addons = request.config.getoption('--addons-path')
odoo.tools.config.parse_config(['--addons-path', addons, '-d', db, '--db-filter', db])
try:
odoo.service.db._create_empty_database(db)
except odoo.service.db.DatabaseExists:
pass
#odoo.service.server.load_server_wide_modules()
#odoo.service.server.preload_registries([db])
with odoo.api.Environment.manage():
# ensure module is installed
r0 = odoo.registry(db)
with r0.cursor() as cr:
env = odoo.api.Environment(cr, 1, {})
[mod] = env['ir.module.module'].search([('name', '=', 'runbot_merge')])
mod.button_immediate_install()
yield odoo.registry(db)
@pytest.fixture
def env(request, registry):
"""Generates an environment, can be parameterized on a user's login
"""
with registry.cursor() as cr:
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
login = getattr(request, 'param', 'admin')
if login != 'admin':
user = env['res.users'].search([('login', '=', login)], limit=1)
env = odoo.api.Environment(cr, user.id, {})
ctx = env['res.users'].context_get()
registry.enter_test_mode(cr)
yield env(context=ctx)
registry.leave_test_mode()
cr.rollback()

View File

@ -192,6 +192,12 @@ class Repo(object):
return handler(self, request, **m.groupdict()) return handler(self, request, **m.groupdict())
return (404, {'message': "No match for {} {}".format(request.method, path)}) return (404, {'message': "No match for {} {}".format(request.method, path)})
def read_tree(self, commit):
return git.read_object(self.objects, commit.tree)
def is_ancestor(self, sha, of):
assert not git.is_ancestor(self.objects, sha, of=of)
def _read_ref(self, r, ref): def _read_ref(self, r, ref):
obj = self.refs.get(ref) obj = self.refs.get(ref)
if obj is None: if obj is None:

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
import odoo
import pytest
import fake_github
@pytest.fixture
def gh():
with fake_github.Github() as gh:
yield gh
@pytest.fixture(scope='session')
def registry(request):
""" Set up Odoo & yields a registry to the specified db
"""
db = request.config.getoption('--db')
addons = request.config.getoption('--addons-path')
odoo.tools.config.parse_config(['--addons-path', addons, '-d', db, '--db-filter', db])
try:
odoo.service.db._create_empty_database(db)
except odoo.service.db.DatabaseExists:
pass
#odoo.service.server.load_server_wide_modules()
#odoo.service.server.preload_registries([db])
with odoo.api.Environment.manage():
# ensure module is installed
r0 = odoo.registry(db)
with r0.cursor() as cr:
env = odoo.api.Environment(cr, 1, {})
[mod] = env['ir.module.module'].search([('name', '=', 'runbot_merge')])
mod.button_immediate_install()
yield odoo.registry(db)
@pytest.fixture
def env(registry):
with registry.cursor() as cr:
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
ctx = env['res.users'].context_get()
registry.enter_test_mode(cr)
yield env(context=ctx)
registry.leave_test_mode()
cr.rollback()
@pytest.fixture
def project(env):
env['res.partner'].create({
'name': "Reviewer",
'github_login': 'reviewer',
'reviewer': True,
})
env['res.partner'].create({
'name': "Self Reviewer",
'github_login': 'self-reviewer',
'self_reviewer': True,
})
env['res.partner'].create({
'name': "Other",
'github_login': 'other',
})
return env['runbot_merge.project'].create({
'name': 'odoo',
'github_token': 'okokok',
'github_prefix': 'hansen',
'branch_ids': [(0, 0, {'name': 'master'})],
'required_statuses': 'legal/cla,ci/runbot',
})
@pytest.fixture
def make_repo(gh, project):
def make_repo(name):
fullname = 'org/' + name
project.write({'repo_ids': [(0, 0, {'name': fullname})]})
return gh.repo(fullname, hooks=[
((odoo.http.root, '/runbot_merge/hooks'), [
'pull_request', 'issue_comment', 'status', 'pull_request_review'
])
])
return make_repo
# TODO: project fixture
# TODO: repos (indirect/parameterize?) w/ WS hook
# + repo proxy object

View File

@ -7,35 +7,8 @@ import odoo
from fake_github import git from fake_github import git
@pytest.fixture @pytest.fixture
def repo(gh, env): def repo(make_repo):
env['res.partner'].create({ return make_repo('repo')
'name': "Reviewer",
'github_login': 'reviewer',
'reviewer': True,
})
env['res.partner'].create({
'name': "Self Reviewer",
'github_login': 'self-reviewer',
'self_reviewer': True,
})
env['res.partner'].create({
'name': "Other",
'github_login': 'other',
})
env['runbot_merge.project'].create({
'name': 'odoo',
'github_token': 'okokok',
'github_prefix': 'hansen',
'repo_ids': [(0, 0, {'name': 'odoo/odoo'})],
'branch_ids': [(0, 0, {'name': 'master'})],
'required_statuses': 'legal/cla,ci/runbot',
})
# need to create repo & branch in env so hook will allow processing them
return gh.repo('odoo/odoo', hooks=[
((odoo.http.root, '/runbot_merge/hooks'), [
'pull_request', 'issue_comment', 'status', 'pull_request_review',
])
])
def test_trivial_flow(env, repo): def test_trivial_flow(env, repo):
# create base branch # create base branch
@ -48,7 +21,7 @@ def test_trivial_flow(env, repo):
pr1 = repo.make_pr("gibberish", "blahblah", target='master', ctid=c1, user='user') pr1 = repo.make_pr("gibberish", "blahblah", target='master', ctid=c1, user='user')
[pr] = env['runbot_merge.pull_requests'].search([ [pr] = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', pr1.number), ('number', '=', pr1.number),
]) ])
assert pr.state == 'opened' assert pr.state == 'opened'
@ -83,7 +56,7 @@ def test_trivial_flow(env, repo):
master = repo.commit('heads/master') master = repo.commit('heads/master')
assert master.parents == [m, pr1.head],\ assert master.parents == [m, pr1.head],\
"master's parents should be the old master & the PR head" "master's parents should be the old master & the PR head"
assert git.read_object(repo.objects, master.tree) == { assert repo.read_tree(master) == {
'a': b'some other content', 'a': b'some other content',
'b': b'a second file', 'b': b'a second file',
} }
@ -103,7 +76,7 @@ def test_staging_conflict(env, repo):
pr1.post_comment("hansen r+", "reviewer") pr1.post_comment("hansen r+", "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr1 = env['runbot_merge.pull_requests'].search([ pr1 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', 1) ('number', '=', 1)
]) ])
assert pr1.staging_id assert pr1.staging_id
@ -117,7 +90,7 @@ def test_staging_conflict(env, repo):
pr2.post_comment('hansen r+', "reviewer") pr2.post_comment('hansen r+', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
p_2 = env['runbot_merge.pull_requests'].search([ p_2 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', 2) ('number', '=', 2)
]) ])
assert p_2.state == 'ready', "PR2 should not have been staged since there is a pending staging for master" assert p_2.state == 'ready', "PR2 should not have been staged since there is a pending staging for master"
@ -162,12 +135,12 @@ def test_staging_concurrent(env, repo):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr1 = env['runbot_merge.pull_requests'].search([ pr1 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', pr1.number) ('number', '=', pr1.number)
]) ])
assert pr1.staging_id assert pr1.staging_id
pr2 = env['runbot_merge.pull_requests'].search([ pr2 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', pr2.number) ('number', '=', pr2.number)
]) ])
assert pr2.staging_id assert pr2.staging_id
@ -188,7 +161,7 @@ def test_staging_merge_fail(env, repo):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr1 = env['runbot_merge.pull_requests'].search([ pr1 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])
assert pr1.state == 'error' assert pr1.state == 'error'
@ -214,7 +187,7 @@ def test_staging_ci_timeout(env, repo):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr1 = env['runbot_merge.pull_requests'].search([ pr1 = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])
assert pr1.staging_id assert pr1.staging_id
@ -238,7 +211,7 @@ def test_staging_ci_failure_single(env, repo):
prx.post_comment('hansen r+', "reviewer") prx.post_comment('hansen r+', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
@ -247,7 +220,7 @@ def test_staging_ci_failure_single(env, repo):
repo.post_status(staging_head.id, 'failure', 'ci/runbot') # stable genius repo.post_status(staging_head.id, 'failure', 'ci/runbot') # stable genius
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'error' ]).state == 'error'
@ -269,7 +242,7 @@ def test_ff_failure(env, repo):
prx.post_comment('hansen r+', "reviewer") prx.post_comment('hansen r+', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
@ -283,7 +256,7 @@ def test_ff_failure(env, repo):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id, "merge should not have succeeded" ]).staging_id, "merge should not have succeeded"
assert repo.commit('heads/staging.master').id != staging.id,\ assert repo.commit('heads/staging.master').id != staging.id,\
@ -310,7 +283,7 @@ def test_edit(env, repo):
c2 = repo.make_commit(c1, 'second', None, tree={'m': 'c2'}) c2 = repo.make_commit(c1, 'second', None, tree={'m': 'c2'})
prx = repo.make_pr('title', 'body', target='master', ctid=c2, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c2, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])
assert pr.message == 'title\n\nbody' assert pr.message == 'title\n\nbody'
@ -326,7 +299,7 @@ def test_edit(env, repo):
prx.base = '1.0' prx.base = '1.0'
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).target == branch_1 ]).target == branch_1
@ -355,7 +328,7 @@ def test_close_staged(env, repo):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
@ -381,7 +354,7 @@ class TestRetry:
prx.post_comment('hansen r+', "reviewer") prx.post_comment('hansen r+', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
@ -390,7 +363,7 @@ class TestRetry:
repo.post_status(staging_head.id, 'failure', 'ci/runbot') repo.post_status(staging_head.id, 'failure', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])
assert pr.state == 'error' assert pr.state == 'error'
@ -427,7 +400,7 @@ class TestRetry:
prx.post_comment('hansen r+ delegate=other', "reviewer") prx.post_comment('hansen r+ delegate=other', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
@ -436,13 +409,13 @@ class TestRetry:
repo.post_status(staging_head.id, 'failure', 'ci/runbot') repo.post_status(staging_head.id, 'failure', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'error' ]).state == 'error'
prx.post_comment('hansen retry', retrier) prx.post_comment('hansen retry', retrier)
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'ready' ]).state == 'ready'
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
@ -453,7 +426,7 @@ class TestRetry:
repo.post_status(staging_head2.id, 'success', 'ci/runbot') repo.post_status(staging_head2.id, 'success', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'merged' ]).state == 'merged'
@ -470,7 +443,7 @@ class TestRetry:
prx.post_comment('hansen r+ delegate=other', "reviewer") prx.post_comment('hansen r+ delegate=other', "reviewer")
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
@ -479,7 +452,7 @@ class TestRetry:
repo.post_status(staging_head.id, 'failure', 'ci/runbot') repo.post_status(staging_head.id, 'failure', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])
assert pr.state == 'error' assert pr.state == 'error'
@ -508,22 +481,22 @@ class TestSquashing(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+', "reviewer") prx.post_comment('hansen r+', "reviewer")
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).squash ]).squash
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
staging = repo.commit('heads/staging.master') staging = repo.commit('heads/staging.master')
assert not git.is_ancestor(repo.objects, prx.head, of=staging.id),\ assert not repo.is_ancestor(prx.head, of=staging.id),\
"the pr head should not be an ancestor of the staging branch in a squash merge" "the pr head should not be an ancestor of the staging branch in a squash merge"
assert staging.parents == [m2],\ assert staging.parents == [m2],\
"the previous master's tip should be the sole parent of the staging commit" "the previous master's tip should be the sole parent of the staging commit"
assert git.read_object(repo.objects, staging.tree) == { assert repo.read_tree(staging) == {
'm': b'c1', 'm2': b'm2', 'm': b'c1', 'm2': b'm2',
}, "the tree should still be correctly merged" }, "the tree should still be correctly merged"
@ -531,7 +504,7 @@ class TestSquashing(object):
repo.post_status(staging.id, 'success', 'ci/runbot') repo.post_status(staging.id, 'success', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'merged' ]).state == 'merged'
assert prx.state == 'closed' assert prx.state == 'closed'
@ -548,7 +521,7 @@ class TestSquashing(object):
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'}) c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
prx = repo.make_pr('title', 'body', target='master', ctid=c1, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c1, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert pr.squash, "a PR with a single commit should be squashed" assert pr.squash, "a PR with a single commit should be squashed"
@ -569,7 +542,7 @@ class TestSquashing(object):
c2 = repo.make_commit(c1, 'second2', None, tree={'m': 'c2'}) c2 = repo.make_commit(c1, 'second2', None, tree={'m': 'c2'})
prx = repo.make_pr('title', 'body', target='master', ctid=c2, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c2, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert not pr.squash, "a PR with a single commit should not be squashed" assert not pr.squash, "a PR with a single commit should not be squashed"
@ -590,22 +563,22 @@ class TestSquashing(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+ squash+', "reviewer") prx.post_comment('hansen r+ squash+', "reviewer")
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).squash ]).squash
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
staging = repo.commit('heads/staging.master') staging = repo.commit('heads/staging.master')
assert not git.is_ancestor(repo.objects, prx.head, of=staging.id),\ assert not repo.is_ancestor(prx.head, of=staging.id),\
"the pr head should not be an ancestor of the staging branch in a squash merge" "the pr head should not be an ancestor of the staging branch in a squash merge"
assert staging.parents == [m2],\ assert staging.parents == [m2],\
"the previous master's tip should be the sole parent of the staging commit" "the previous master's tip should be the sole parent of the staging commit"
assert git.read_object(repo.objects, staging.tree) == { assert repo.read_tree(staging) == {
'm': b'c2', 'm2': b'm2', 'm': b'c2', 'm2': b'm2',
}, "the tree should still be correctly merged" }, "the tree should still be correctly merged"
@ -613,7 +586,7 @@ class TestSquashing(object):
repo.post_status(staging.id, 'success', 'ci/runbot') repo.post_status(staging.id, 'success', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'merged' ]).state == 'merged'
assert prx.state == 'closed' assert prx.state == 'closed'
@ -630,20 +603,20 @@ class TestSquashing(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+ squash-', "reviewer") prx.post_comment('hansen r+ squash-', "reviewer")
assert not env['runbot_merge.pull_requests'].search([ assert not env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).squash ]).squash
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).staging_id ]).staging_id
staging = repo.commit('heads/staging.master') staging = repo.commit('heads/staging.master')
assert git.is_ancestor(repo.objects, prx.head, of=staging.id) assert repo.is_ancestor(prx.head, of=staging.id)
assert staging.parents == [m2, c1] assert staging.parents == [m2, c1]
assert git.read_object(repo.objects, staging.tree) == { assert repo.read_tree(staging) == {
'm': b'c1', 'm2': b'm2', 'm': b'c1', 'm2': b'm2',
} }
@ -651,7 +624,7 @@ class TestSquashing(object):
repo.post_status(staging.id, 'success', 'ci/runbot') repo.post_status(staging.id, 'success', 'ci/runbot')
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'merged' ]).state == 'merged'
assert prx.state == 'closed' assert prx.state == 'closed'
@ -668,7 +641,7 @@ class TestPRUpdate(object):
c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'}) c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'})
prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert pr.head == c assert pr.head == c
@ -686,7 +659,7 @@ class TestPRUpdate(object):
c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'}) c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'})
prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
prx.close() prx.close()
@ -707,7 +680,7 @@ class TestPRUpdate(object):
c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'}) c = repo.make_commit(m, 'fist', None, tree={'m': 'c1'})
prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
prx.close() prx.close()
@ -732,7 +705,7 @@ class TestPRUpdate(object):
repo.post_status(prx.head, 'success', 'legal/cla') repo.post_status(prx.head, 'success', 'legal/cla')
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert pr.head == c assert pr.head == c
@ -751,7 +724,7 @@ class TestPRUpdate(object):
prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c, user='user')
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert pr.head == c assert pr.head == c
@ -774,7 +747,7 @@ class TestPRUpdate(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
assert pr.head == c assert pr.head == c
@ -797,7 +770,7 @@ class TestPRUpdate(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
@ -825,7 +798,7 @@ class TestPRUpdate(object):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
h = repo.commit('heads/staging.master').id h = repo.commit('heads/staging.master').id
@ -854,7 +827,7 @@ class TestPRUpdate(object):
repo.post_status(prx.head, 'success', 'ci/runbot') repo.post_status(prx.head, 'success', 'ci/runbot')
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number), ('number', '=', prx.number),
]) ])
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
@ -897,6 +870,7 @@ class TestBatching(object):
): ):
""" Helper creating a PR from a series of commits on a base """ Helper creating a PR from a series of commits on a base
:type repo: fake_github.Repo
:param prefix: a prefix used for commit messages, PR title & PR body :param prefix: a prefix used for commit messages, PR title & PR body
:param trees: a list of dicts symbolising the tree for the corresponding commit. :param trees: a list of dicts symbolising the tree for the corresponding commit.
each tree is an update on the "current state" of the tree each tree is an update on the "current state" of the tree
@ -907,7 +881,7 @@ class TestBatching(object):
:type statuses: List[(str, str)] :type statuses: List[(str, str)]
""" """
base = repo.commit('heads/{}'.format(target)) base = repo.commit('heads/{}'.format(target))
tree = dict(repo.objects[base.tree]) tree = repo.read_tree(base)
c = base.id c = base.id
for i, t in enumerate(trees): for i, t in enumerate(trees):
tree.update(t) tree.update(t)
@ -920,9 +894,9 @@ class TestBatching(object):
pr.post_comment('hansen r+', reviewer) pr.post_comment('hansen r+', reviewer)
return pr return pr
def _get(self, env, number): def _get(self, env, repo, number):
return env['runbot_merge.pull_requests'].search([ return env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', number), ('number', '=', number),
]) ])
@ -937,9 +911,9 @@ class TestBatching(object):
pr2 = self._pr(repo, 'PR2', [{'c': 'CCC'}, {'d': 'DDD'}]) pr2 = self._pr(repo, 'PR2', [{'c': 'CCC'}, {'d': 'DDD'}])
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
pr1 = self._get(env, pr1.number) pr1 = self._get(env, repo, pr1.number)
assert pr1.staging_id assert pr1.staging_id
pr2 = self._get(env, pr2.number) pr2 = self._get(env, repo, pr2.number)
assert pr1.staging_id assert pr1.staging_id
assert pr2.staging_id assert pr2.staging_id
assert pr1.staging_id == pr2.staging_id assert pr1.staging_id == pr2.staging_id
@ -958,7 +932,7 @@ class TestBatching(object):
pr11.post_comment('hansen priority=1', 'reviewer') pr11.post_comment('hansen priority=1', 'reviewer')
pr12.post_comment('hansen priority=1', 'reviewer') pr12.post_comment('hansen priority=1', 'reviewer')
pr21, pr22, pr11, pr12 = prs = [self._get(env, pr.number) for pr in [pr21, pr22, pr11, pr12]] pr21, pr22, pr11, pr12 = prs = [self._get(env, repo, pr.number) for pr in [pr21, pr22, pr11, pr12]]
assert pr21.priority == pr22.priority == 2 assert pr21.priority == pr22.priority == 2
assert pr11.priority == pr12.priority == 1 assert pr11.priority == pr12.priority == 1
@ -986,7 +960,7 @@ class TestBatching(object):
# stage PR1 # stage PR1
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
p_11, p_12, p_21, p_22 = \ p_11, p_12, p_21, p_22 = \
[self._get(env, pr.number) for pr in [pr11, pr12, pr21, pr22]] [self._get(env, repo, pr.number) for pr in [pr11, pr12, pr21, pr22]]
assert not p_21.staging_id or p_22.staging_id assert not p_21.staging_id or p_22.staging_id
assert p_11.staging_id and p_12.staging_id assert p_11.staging_id and p_12.staging_id
assert p_11.staging_id == p_12.staging_id assert p_11.staging_id == p_12.staging_id
@ -995,7 +969,7 @@ class TestBatching(object):
# no statuses run on PR0s # no statuses run on PR0s
pr01 = self._pr(repo, 'Urgent 1', [{'n': 'n'}, {'o': 'o'}], reviewer=None, statuses=[]) pr01 = self._pr(repo, 'Urgent 1', [{'n': 'n'}, {'o': 'o'}], reviewer=None, statuses=[])
pr01.post_comment('hansen priority=0', 'reviewer') pr01.post_comment('hansen priority=0', 'reviewer')
p_01 = self._get(env, pr01.number) p_01 = self._get(env, repo, pr01.number)
assert p_01.state == 'opened' assert p_01.state == 'opened'
assert p_01.priority == 0 assert p_01.priority == 0
@ -1014,9 +988,9 @@ class TestBatching(object):
repo.make_ref('heads/master', m) repo.make_ref('heads/master', m)
pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}]) pr1 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}])
p_1 = self._get(env, pr1.number) p_1 = self._get(env, repo, pr1.number)
pr2 = self._pr(repo, 'PR2', [{'a': 'some_content', 'c': 'CCC'}, {'d': 'DDD'}]) pr2 = self._pr(repo, 'PR2', [{'a': 'some_content', 'c': 'CCC'}, {'d': 'DDD'}])
p_2 = self._get(env, pr2.number) p_2 = self._get(env, repo, pr2.number)
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
st = env['runbot_merge.stagings'].search([]) st = env['runbot_merge.stagings'].search([])
@ -1038,7 +1012,7 @@ class TestBatching(object):
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
# TODO: maybe just deactivate stagings instead of deleting them when canceling? # TODO: maybe just deactivate stagings instead of deleting them when canceling?
assert not p_1.staging_id assert not p_1.staging_id
assert self._get(env, pr0.number).staging_id assert self._get(env, repo, pr0.number).staging_id
def test_urgent_failed(self, env, repo): def test_urgent_failed(self, env, repo):
""" Ensure pr[p=0,state=failed] don't get picked up """ Ensure pr[p=0,state=failed] don't get picked up
@ -1048,12 +1022,12 @@ class TestBatching(object):
pr21 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}]) pr21 = self._pr(repo, 'PR1', [{'a': 'AAA'}, {'b': 'BBB'}])
p_21 = self._get(env, pr21.number) p_21 = self._get(env, repo, pr21.number)
# no statuses run on PR0s # no statuses run on PR0s
pr01 = self._pr(repo, 'Urgent 1', [{'n': 'n'}, {'o': 'o'}], reviewer=None, statuses=[]) pr01 = self._pr(repo, 'Urgent 1', [{'n': 'n'}, {'o': 'o'}], reviewer=None, statuses=[])
pr01.post_comment('hansen priority=0', 'reviewer') pr01.post_comment('hansen priority=0', 'reviewer')
p_01 = self._get(env, pr01.number) p_01 = self._get(env, repo, pr01.number)
p_01.state = 'error' p_01.state = 'error'
env['runbot_merge.project']._check_progress() env['runbot_merge.project']._check_progress()
@ -1122,12 +1096,12 @@ class TestReviewing(object):
prx.post_comment('hansen r+', user='rando') prx.post_comment('hansen r+', user='rando')
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'validated' ]).state == 'validated'
prx.post_comment('hansen r+', user='reviewer') prx.post_comment('hansen r+', user='reviewer')
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'ready' ]).state == 'ready'
@ -1146,7 +1120,7 @@ class TestReviewing(object):
assert prx.user == 'reviewer' assert prx.user == 'reviewer'
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'validated' ]).state == 'validated'
@ -1165,7 +1139,7 @@ class TestReviewing(object):
assert prx.user == 'self-reviewer' assert prx.user == 'self-reviewer'
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'ready' ]).state == 'ready'
@ -1186,7 +1160,7 @@ class TestReviewing(object):
assert prx.user == 'user' assert prx.user == 'user'
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'ready' ]).state == 'ready'
@ -1207,13 +1181,13 @@ class TestReviewing(object):
assert prx.user == 'user' assert prx.user == 'user'
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'validated' ]).state == 'validated'
prx.post_comment('hansen r+', user='jimbob') prx.post_comment('hansen r+', user='jimbob')
assert env['runbot_merge.pull_requests'].search([ assert env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]).state == 'ready' ]).state == 'ready'
@ -1225,7 +1199,7 @@ class TestReviewing(object):
c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'}) c1 = repo.make_commit(m, 'first', None, tree={'m': 'c1'})
prx = repo.make_pr('title', 'body', target='master', ctid=c1, user='user') prx = repo.make_pr('title', 'body', target='master', ctid=c1, user='user')
pr = env['runbot_merge.pull_requests'].search([ pr = env['runbot_merge.pull_requests'].search([
('repository.name', '=', 'odoo/odoo'), ('repository.name', '=', repo.name),
('number', '=', prx.number) ('number', '=', prx.number)
]) ])

View File

@ -7,50 +7,19 @@ are staged concurrently in all repos
""" """
import json import json
import odoo
import pytest import pytest
@pytest.fixture @pytest.fixture
def project(env): def repo_a(make_repo):
env['res.partner'].create({ return make_repo('a')
'name': "Reviewer",
'github_login': 'reviewer',
'reviewer': True,
})
env['res.partner'].create({
'name': "Self Reviewer",
'github_login': 'self-reviewer',
'self_reviewer': True,
})
return env['runbot_merge.project'].create({
'name': 'odoo',
'github_token': 'okokok',
'github_prefix': 'hansen',
'branch_ids': [(0, 0, {'name': 'master'})],
'required_statuses': 'legal/cla,ci/runbot',
})
@pytest.fixture @pytest.fixture
def repo_a(gh, project): def repo_b(make_repo):
project.write({'repo_ids': [(0, 0, {'name': "odoo/a"})]}) return make_repo('b')
return gh.repo('odoo/a', hooks=[
((odoo.http.root, '/runbot_merge/hooks'), ['pull_request', 'issue_comment', 'status', 'pull_request_review'])
])
@pytest.fixture @pytest.fixture
def repo_b(gh, project): def repo_c(make_repo):
project.write({'repo_ids': [(0, 0, {'name': "odoo/b"})]}) return make_repo('c')
return gh.repo('odoo/b', hooks=[
((odoo.http.root, '/runbot_merge/hooks'), ['pull_request', 'issue_comment', 'status'])
])
@pytest.fixture
def repo_c(gh, project):
project.write({'repo_ids': [(0, 0, {'name': "odoo/c"})]})
return gh.repo('odoo/c', hooks=[
((odoo.http.root, '/runbot_merge/hooks'), ['pull_request', 'issue_comment', 'status'])
])
def make_pr(repo, prefix, trees, *, target='master', user='user', label=None, def make_pr(repo, prefix, trees, *, target='master', user='user', label=None,
statuses=(('ci/runbot', 'success'), ('legal/cla', 'success')), statuses=(('ci/runbot', 'success'), ('legal/cla', 'success')),
@ -67,7 +36,7 @@ def make_pr(repo, prefix, trees, *, target='master', user='user', label=None,
:rtype: fake_github.PR :rtype: fake_github.PR
""" """
base = repo.commit('heads/{}'.format(target)) base = repo.commit('heads/{}'.format(target))
tree = dict(repo.objects[base.tree]) tree = repo.read_tree(base)
c = base.id c = base.id
for i, t in enumerate(trees): for i, t in enumerate(trees):
tree.update(t) tree.update(t)
@ -172,9 +141,9 @@ def test_sub_match(env, project, repo_a, repo_b, repo_c):
"branch-matched PRs should be part of the same staging" "branch-matched PRs should be part of the same staging"
st = pr_b.staging_id st = pr_b.staging_id
assert json.loads(st.heads) == { assert json.loads(st.heads) == {
'odoo/a': repo_a.commit('heads/master').id, repo_a.name: repo_a.commit('heads/master').id,
'odoo/b': repo_b.commit('heads/staging.master').id, repo_b.name: repo_b.commit('heads/staging.master').id,
'odoo/c': repo_c.commit('heads/staging.master').id, repo_c.name: repo_c.commit('heads/staging.master').id,
} }
def test_merge_fail(env, project, repo_a, repo_b): def test_merge_fail(env, project, repo_a, repo_b):