-
Yoann Schneider authoredYoann Schneider authored
test_git.py 14.59 KiB
# -*- coding: utf-8 -*-
from pathlib import Path
import pytest
from gitlab import GitlabCreateError, GitlabError
from requests import ConnectionError
from responses import matchers
from arkindex_worker.git import GitlabHelper
PROJECT_ID = 21259233
MERGE_REQUEST_ID = 7
SOURCE_BRANCH = "new_branch"
TARGET_BRANCH = "master"
MR_TITLE = "merge request title"
CREATE_MR_RESPONSE_JSON = {
"id": 107,
"iid": MERGE_REQUEST_ID,
"project_id": PROJECT_ID,
"title": MR_TITLE,
"target_branch": TARGET_BRANCH,
"source_branch": SOURCE_BRANCH,
# several fields omitted
}
@pytest.fixture
def fake_responses(responses):
responses.add(
responses.GET,
"https://gitlab.com/api/v4/projects/balsac_exporter%2Fbalsac-exported-xmls-testing",
json={
"id": PROJECT_ID,
# several fields omitted
},
)
return responses
def test_clone_done(fake_git_helper):
assert not fake_git_helper.is_clone_finished
fake_git_helper._clone_done(None, None, None)
assert fake_git_helper.is_clone_finished
def test_clone(fake_git_helper):
command = fake_git_helper.run_clone_in_background()
cmd_str = " ".join(list(map(str, command.cmd)))
assert "git" in cmd_str
assert "clone" in cmd_str
def _get_fn_name_from_call(call):
# call.add(2, 3) => "add"
return str(call)[len("call.") :].split("(")[0]
def test_save_files(fake_git_helper, mocker):
mocker.patch("sh.wc", return_value=2)
fake_git_helper._git = mocker.MagicMock()
fake_git_helper.is_clone_finished = True
fake_git_helper.success = True
fake_git_helper.save_files(Path("/tmp/test_1234/tmp/"))
expected_calls = ["checkout", "add", "commit", "show", "push"]
actual_calls = list(map(_get_fn_name_from_call, fake_git_helper._git.mock_calls))
assert actual_calls == expected_calls
assert fake_git_helper.gitlab_helper.merge.call_count == 1
def test_save_files__fail_with_failed_clone(fake_git_helper, mocker):
mocker.patch("sh.wc", return_value=2)
fake_git_helper._git = mocker.MagicMock()
fake_git_helper.is_clone_finished = True
with pytest.raises(Exception) as execinfo:
fake_git_helper.save_files(Path("/tmp/test_1234/tmp/"))
assert execinfo.value.args[0] == "Clone was not a success"
def test_merge(mocker):
api = mocker.MagicMock()
project = mocker.MagicMock()
api.projects.get.return_value = project
merqe_request = mocker.MagicMock()
project.mergerequests.create.return_value = merqe_request
mocker.patch("gitlab.Gitlab", return_value=api)
gitlab_helper = GitlabHelper("project_id", "url", "token", "branch")
gitlab_helper._wait_for_rebase_to_finish = mocker.MagicMock()
gitlab_helper._wait_for_rebase_to_finish.return_value = True
success = gitlab_helper.merge("source", "merge title")
assert success
assert project.mergerequests.create.call_count == 1
assert merqe_request.merge.call_count == 1
def test_merge__rebase_failed(mocker):
api = mocker.MagicMock()
project = mocker.MagicMock()
api.projects.get.return_value = project
merqe_request = mocker.MagicMock()
project.mergerequests.create.return_value = merqe_request
mocker.patch("gitlab.Gitlab", return_value=api)
gitlab_helper = GitlabHelper("project_id", "url", "token", "branch")
gitlab_helper._wait_for_rebase_to_finish = mocker.MagicMock()
gitlab_helper._wait_for_rebase_to_finish.return_value = False
success = gitlab_helper.merge("source", "merge title")
assert not success
assert project.mergerequests.create.call_count == 1
assert merqe_request.merge.call_count == 0
def test_wait_for_rebase_to_finish(fake_responses, fake_gitlab_helper_factory):
get_mr_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}?include_rebase_in_progress=True"
fake_responses.add(
fake_responses.GET,
get_mr_url,
json={
"rebase_in_progress": True,
"merge_error": None,
},
)
fake_responses.add(
fake_responses.GET,
get_mr_url,
json={
"rebase_in_progress": True,
"merge_error": None,
},
)
fake_responses.add(
fake_responses.GET,
get_mr_url,
json={
"rebase_in_progress": False,
"merge_error": None,
},
)
gitlab_helper = fake_gitlab_helper_factory()
success = gitlab_helper._wait_for_rebase_to_finish(MERGE_REQUEST_ID)
assert success
assert len(fake_responses.calls) == 4
assert gitlab_helper.is_rebase_finished
def test_wait_for_rebase_to_finish__fail_connection_error(
fake_responses, fake_gitlab_helper_factory
):
get_mr_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}?include_rebase_in_progress=True"
fake_responses.add(
fake_responses.GET,
get_mr_url,
body=ConnectionError(),
)
gitlab_helper = fake_gitlab_helper_factory()
with pytest.raises(ConnectionError):
gitlab_helper._wait_for_rebase_to_finish(MERGE_REQUEST_ID)
assert len(fake_responses.calls) == 2
assert not gitlab_helper.is_rebase_finished
def test_wait_for_rebase_to_finish__fail_server_error(
fake_responses, fake_gitlab_helper_factory
):
get_mr_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}?include_rebase_in_progress=True"
fake_responses.add(
fake_responses.GET,
get_mr_url,
body="Service Unavailable",
status=503,
)
gitlab_helper = fake_gitlab_helper_factory()
with pytest.raises(GitlabError):
gitlab_helper._wait_for_rebase_to_finish(MERGE_REQUEST_ID)
assert len(fake_responses.calls) == 2
assert not gitlab_helper.is_rebase_finished
def test_merge_request(fake_responses, fake_gitlab_helper_factory, mocker):
fake_responses.add(
fake_responses.POST,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests",
json=CREATE_MR_RESPONSE_JSON,
)
fake_responses.add(
fake_responses.PUT,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/rebase",
json={},
)
fake_responses.add(
fake_responses.PUT,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/merge",
json={
"iid": MERGE_REQUEST_ID,
"state": "merged",
# several fields omitted
},
match=[matchers.json_params_matcher({"should_remove_source_branch": True})],
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
gitlab_helper._wait_for_rebase_to_finish = mocker.MagicMock()
gitlab_helper._wait_for_rebase_to_finish.return_value = True
success = gitlab_helper.merge(SOURCE_BRANCH, MR_TITLE)
assert success
assert len(fake_responses.calls) == 4
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls
def test_merge_request_fail(fake_responses, fake_gitlab_helper_factory, mocker):
fake_responses.add(
fake_responses.POST,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests",
json=CREATE_MR_RESPONSE_JSON,
)
fake_responses.add(
fake_responses.PUT,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/rebase",
json={},
)
fake_responses.add(
fake_responses.PUT,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/merge",
json={"error": "Method not allowed"},
status=405,
match=[matchers.json_params_matcher({"should_remove_source_branch": True})],
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
gitlab_helper._wait_for_rebase_to_finish = mocker.MagicMock()
gitlab_helper._wait_for_rebase_to_finish.return_value = True
success = gitlab_helper.merge(SOURCE_BRANCH, MR_TITLE)
assert not success
assert len(fake_responses.calls) == 4
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls
def test_merge_request__success_after_errors(
fake_responses, fake_gitlab_helper_factory
):
fake_responses.add(
fake_responses.POST,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests",
json=CREATE_MR_RESPONSE_JSON,
)
rebase_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/rebase"
fake_responses.add(
fake_responses.PUT,
rebase_url,
json={"rebase_in_progress": True},
)
get_mr_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}?include_rebase_in_progress=True"
fake_responses.add(
fake_responses.GET,
get_mr_url,
body="Service Unavailable",
status=503,
)
fake_responses.add(
fake_responses.PUT,
rebase_url,
json={"rebase_in_progress": True},
)
fake_responses.add(
fake_responses.GET,
get_mr_url,
body=ConnectionError(),
)
fake_responses.add(
fake_responses.PUT,
rebase_url,
json={"rebase_in_progress": True},
)
fake_responses.add(
fake_responses.GET,
get_mr_url,
json={
"rebase_in_progress": True,
"merge_error": None,
},
)
fake_responses.add(
fake_responses.GET,
get_mr_url,
json={
"rebase_in_progress": False,
"merge_error": None,
},
)
fake_responses.add(
fake_responses.PUT,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/merge",
json={
"iid": MERGE_REQUEST_ID,
"state": "merged",
# several fields omitted
},
match=[matchers.json_params_matcher({"should_remove_source_branch": True})],
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
success = gitlab_helper.merge(SOURCE_BRANCH, MR_TITLE)
assert success
assert len(fake_responses.calls) == 10
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls
def test_merge_request__fail_bad_request(fake_responses, fake_gitlab_helper_factory):
fake_responses.add(
fake_responses.POST,
f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests",
json=CREATE_MR_RESPONSE_JSON,
)
rebase_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}/rebase"
fake_responses.add(
fake_responses.PUT,
rebase_url,
json={"rebase_in_progress": True},
)
get_mr_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests/{MERGE_REQUEST_ID}?include_rebase_in_progress=True"
fake_responses.add(
fake_responses.GET,
get_mr_url,
body="Bad Request",
status=400,
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
with pytest.raises(GitlabError):
gitlab_helper.merge(SOURCE_BRANCH, MR_TITLE)
assert len(fake_responses.calls) == 4
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls
def test_create_merge_request__no_retry_5xx_error(
fake_responses, fake_gitlab_helper_factory
):
request_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests"
fake_responses.add(
fake_responses.POST,
request_url,
body="Service Unavailable",
status=503,
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
with pytest.raises(GitlabCreateError):
gitlab_helper.project.mergerequests.create(
{
"source_branch": "branch",
"target_branch": gitlab_helper.branch,
"title": "MR title",
}
)
assert len(fake_responses.calls) == 2
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls
def test_create_merge_request__retry_5xx_error(
fake_responses, fake_gitlab_helper_factory
):
request_url = f"https://gitlab.com/api/v4/projects/{PROJECT_ID}/merge_requests"
fake_responses.add(
fake_responses.POST,
request_url,
body="Service Unavailable",
status=503,
)
fake_responses.add(
fake_responses.POST,
request_url,
body="Service Unavailable",
status=503,
)
fake_responses.add(
fake_responses.POST,
request_url,
json=CREATE_MR_RESPONSE_JSON,
)
# the fake_responses are defined in the same order as they are expected to be called
expected_http_methods = [r.method for r in fake_responses.registered()]
expected_urls = [r.url for r in fake_responses.registered()]
gitlab_helper = fake_gitlab_helper_factory()
gitlab_helper.project.mergerequests.create(
{
"source_branch": "branch",
"target_branch": gitlab_helper.branch,
"title": "MR title",
},
retry_transient_errors=True,
)
assert len(fake_responses.calls) == 4
assert [c.request.method for c in fake_responses.calls] == expected_http_methods
assert [c.request.url for c in fake_responses.calls] == expected_urls