Changeset View
Changeset View
Standalone View
Standalone View
swh/icinga_plugins/tests/test_save_code_now.py
- This file was added.
# Copyright (C) 2021 The Software Heritage developers | |||||
# See the AUTHORS file at the top-level directory of this distribution | |||||
# License: GNU General Public License version 3, or any later version | |||||
# See top-level LICENSE file for more information | |||||
ardumont: /me will play fill in the blank | |||||
from datetime import datetime, timezone | |||||
import random | |||||
from typing import Dict, Optional, Tuple | |||||
import pytest | |||||
from swh.icinga_plugins.save_code_now import ( | |||||
REPORT_MSG, | |||||
WAITING_STATUSES, | |||||
SaveCodeNowCheck, | |||||
) | |||||
from .utils import invoke | |||||
from .web_scenario import WebScenario | |||||
def fake_response( | |||||
origin: str, | |||||
visit_type: str, | |||||
sor_status: str = "pending", | |||||
task_status: Optional[str] = None, | |||||
) -> Dict: | |||||
"""Fake a save code now request api response""" | |||||
visit_date = None | |||||
if task_status in ("failed", "succeeded"): | |||||
visit_date = str(datetime.now(tz=timezone.utc)) | |||||
return { | |||||
"visit_type": visit_type, | |||||
"origin_url": origin, | |||||
"save_request_date": "to-replace", | |||||
"save_request_status": sor_status, | |||||
"save_task_status": task_status, | |||||
"visit_date": visit_date, | |||||
} | |||||
@pytest.fixture | |||||
def origin_info() -> Tuple[str, str]: | |||||
"""Build an origin info to request save code now | |||||
""" | |||||
origin_name = random.choice(range(10)) | |||||
return random.choice(["git", "svn", "hg"]), f"mock://fake-origin-url/{origin_name}" | |||||
Not Done Inline ActionsThe return of the random ;) vsellier: The return of the random ;) | |||||
def test_save_code_now_success(requests_mock, mocker, mocked_time, origin_info): | |||||
"""Successful ingestion scenario below threshold""" | |||||
scenario = WebScenario() | |||||
visit_type, origin = origin_info | |||||
root_api_url = "mock://swh-web.example.org" | |||||
api_url = SaveCodeNowCheck.api_url_scn(root_api_url, origin, visit_type) | |||||
# creation request | |||||
scenario.add_step( | |||||
"post", | |||||
api_url, | |||||
fake_response(origin, visit_type, "accepted", "not yet scheduled"), | |||||
) | |||||
response_scheduled = fake_response(origin, visit_type, "accepted", "scheduled") | |||||
# status polling requests | |||||
scenario.add_step("get", api_url, [response_scheduled]) | |||||
# sometimes we can have multiple response so we fake that here | |||||
scenario.add_step("get", api_url, [response_scheduled, response_scheduled]) | |||||
scenario.add_step( | |||||
"get", api_url, [fake_response(origin, visit_type, "accepted", "succeeded")] | |||||
) | |||||
scenario.install_mock(requests_mock) | |||||
# fmt: off | |||||
result = invoke( | |||||
[ | |||||
"check-savecodenow", "--swh-web-url", root_api_url, | |||||
"origin", origin, | |||||
"--visit-type", visit_type, | |||||
] | |||||
) | |||||
# fmt: on | |||||
assert result.output == ( | |||||
f"{SaveCodeNowCheck.TYPE} OK - {REPORT_MSG} {origin_info} took " | |||||
f"30.00s and succeeded.\n" | |||||
f"| 'total_time' = 30.00s\n" | |||||
) | |||||
assert result.exit_code == 0, f"Unexpected result: {result.output}" | |||||
def test_save_code_now_failure(requests_mock, mocker, mocked_time, origin_info): | |||||
"""Failed ingestion scenario should be reported""" | |||||
scenario = WebScenario() | |||||
visit_type, origin = origin_info | |||||
root_api_url = "mock://swh-web.example.org" | |||||
api_url = SaveCodeNowCheck.api_url_scn(root_api_url, origin, visit_type) | |||||
# creation request | |||||
scenario.add_step( | |||||
"post", | |||||
api_url, | |||||
fake_response(origin, visit_type, "accepted", "not yet scheduled"), | |||||
) | |||||
# status polling requests | |||||
scenario.add_step( | |||||
"get", api_url, [fake_response(origin, visit_type, "accepted", "scheduled")] | |||||
) | |||||
scenario.add_step( | |||||
"get", api_url, [fake_response(origin, visit_type, "accepted", "failed")] | |||||
) | |||||
scenario.install_mock(requests_mock) | |||||
# fmt: off | |||||
result = invoke( | |||||
[ | |||||
"check-savecodenow", "--swh-web-url", root_api_url, | |||||
"origin", origin, | |||||
"--visit-type", visit_type, | |||||
], | |||||
catch_exceptions=True, | |||||
) | |||||
# fmt: on | |||||
assert result.output == ( | |||||
f"{SaveCodeNowCheck.TYPE} CRITICAL - {REPORT_MSG} {origin_info} took " | |||||
f"20.00s and failed.\n" | |||||
f"| 'total_time' = 20.00s\n" | |||||
) | |||||
assert result.exit_code == 2, f"Unexpected result: {result.output}" | |||||
def test_save_code_now_pending_state_unsupported( | |||||
requests_mock, mocker, mocked_time, origin_info | |||||
): | |||||
"""Pending save requests are not supported in the test so they should fail early | |||||
Pending requests are requests that need a moderator to accept the repository into | |||||
the save code now flow. | |||||
Do not actually use such origin to trigger the checks. | |||||
""" | |||||
scenario = WebScenario() | |||||
visit_type, origin = origin_info | |||||
root_api_url = "mock://swh-web2.example.org" | |||||
api_url = SaveCodeNowCheck.api_url_scn(root_api_url, origin, visit_type) | |||||
# creation request | |||||
scenario.add_step( | |||||
"post", api_url, fake_response(origin, visit_type, "pending", "not created"), | |||||
) | |||||
scenario.install_mock(requests_mock) | |||||
# fmt: off | |||||
result = invoke( | |||||
[ | |||||
"check-savecodenow", "--swh-web-url", root_api_url, | |||||
"origin", origin, | |||||
"--visit-type", visit_type, | |||||
], | |||||
catch_exceptions=True, | |||||
) | |||||
# fmt: on | |||||
assert result.output == ( | |||||
f"{SaveCodeNowCheck.TYPE} CRITICAL - {REPORT_MSG} {origin_info} took " | |||||
f"0.00s and resulted in unsupported status: pending ; not created.\n" | |||||
f"| 'total_time' = 0.00s\n" | |||||
) | |||||
assert result.exit_code == 2, f"Unexpected output: {result.output}" | |||||
def test_save_code_now_threshold_exceeded( | |||||
requests_mock, mocker, mocked_time, origin_info | |||||
): | |||||
"""Saving requests exceeding threshold should mention warning in output | |||||
""" | |||||
scenario = WebScenario() | |||||
visit_type, origin = origin_info | |||||
root_api_url = "mock://swh-web2.example.org" | |||||
api_url = SaveCodeNowCheck.api_url_scn(root_api_url, origin, visit_type) | |||||
# creation request | |||||
scenario.add_step( | |||||
"post", | |||||
api_url, | |||||
fake_response(origin, visit_type, "accepted", "not yet scheduled"), | |||||
) | |||||
# we'll make the response being in the awaiting status | |||||
# beyond 13, this will exceed the threshold | |||||
for i in range(13): | |||||
waiting_status = random.choice(WAITING_STATUSES) | |||||
Not Done Inline Actionsrandom guy :) vsellier: random guy :) | |||||
response_scheduled = fake_response( | |||||
origin, visit_type, "accepted", waiting_status | |||||
) | |||||
scenario.add_step("get", api_url, [response_scheduled]) | |||||
scenario.install_mock(requests_mock) | |||||
# fmt: off | |||||
result = invoke( | |||||
[ | |||||
"check-savecodenow", "--swh-web-url", root_api_url, | |||||
"origin", origin, | |||||
"--visit-type", visit_type, | |||||
], | |||||
catch_exceptions=True, | |||||
) | |||||
# fmt: on | |||||
assert result.output == ( | |||||
f"{SaveCodeNowCheck.TYPE} CRITICAL - {REPORT_MSG} {origin_info} took " | |||||
f"more than 130.00s and has status: {waiting_status}.\n" | |||||
f"| 'total_time' = 130.00s\n" | |||||
) | |||||
assert result.exit_code == 2, f"Unexpected output: {result.output}" | |||||
def test_save_code_now_unexpected_failure( | |||||
requests_mock, mocker, mocked_time, origin_info | |||||
): | |||||
"""Unexpected failure if the webapi refuses to answer for example""" | |||||
scenario = WebScenario() | |||||
visit_type, origin = origin_info | |||||
root_api_url = "mock://swh-web.example.org" | |||||
api_url = SaveCodeNowCheck.api_url_scn(root_api_url, origin, visit_type) | |||||
# creation request | |||||
scenario.add_step( | |||||
"post", | |||||
api_url, | |||||
fake_response(origin, visit_type, "accepted", "not yet scheduled"), | |||||
) | |||||
# status polling requests | |||||
scenario.add_step( | |||||
"get", api_url, [fake_response(origin, visit_type, "accepted", "scheduled")] | |||||
) | |||||
# unexpected issue when communicating with the api | |||||
scenario.add_step("get", api_url, {}, status_code=500) | |||||
scenario.install_mock(requests_mock) | |||||
with pytest.raises(AssertionError): | |||||
# fmt: off | |||||
invoke( | |||||
[ | |||||
"check-savecodenow", "--swh-web-url", root_api_url, | |||||
"origin", origin, | |||||
"--visit-type", visit_type, | |||||
], | |||||
) | |||||
# fmt: on |
/me will play fill in the blank