diff --git a/requirements-swh.txt b/requirements-swh.txt --- a/requirements-swh.txt +++ b/requirements-swh.txt @@ -1,4 +1,4 @@ swh.core >= 0.0.75 swh.model >= 0.3.0 swh.scheduler -swh.storage >= 0.3.0 +swh.storage >= 0.6.0 diff --git a/swh/loader/tests/common.py b/swh/loader/tests/common.py new file mode 100644 --- /dev/null +++ b/swh/loader/tests/common.py @@ -0,0 +1,53 @@ +# Copyright (C) 2020 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 + +from typing import Optional + +from swh.model.model import OriginVisitStatus +from swh.storage.algos.origin import origin_get_latest_visit_status + + +def assert_last_visit_matches( + storage, + url: str, + status: str, + type: Optional[str] = None, + snapshot: Optional[bytes] = None, +) -> OriginVisitStatus: + """This retrieves the last visit and visit_status which are expected to exist. + + This also checks that the {visit|visit_status} have their respective properties + correctly set. + + This returns the last visit_status for that given origin. + + Args: + url: Origin url + status: Check that the visit status has the given status + type: Check that the returned visit has the given type + snapshot: Check that the visit status points to the given snapshot + + Raises: + AssertionError in case visit or visit status is not found, or any of the type, + status and snapshot mismatch + + Returns: + the visit status for further check during the remaining part of the test. + + """ + visit_and_status = origin_get_latest_visit_status(storage, url) + assert visit_and_status is not None, f"Origin {url} has no visits" + visit, visit_status = visit_and_status + if type: + assert visit.type == type, f"Visit has type {visit.type} instead of {type}" + assert ( + visit_status.status == status + ), f"Visit_status has status {visit_status.status} instead of {status}" + if snapshot: + assert visit_status.snapshot == snapshot, ( + "Visit_status points to snapshot {visit_status.snapshot!r} " + f"instead of {snapshot!r}" + ) + return visit_status diff --git a/swh/loader/tests/test_common.py b/swh/loader/tests/test_common.py new file mode 100644 --- /dev/null +++ b/swh/loader/tests/test_common.py @@ -0,0 +1,121 @@ +# Copyright (C) 2019-2020 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 + +import datetime +import pytest + +from swh.loader.tests.common import assert_last_visit_matches +from swh.model.model import OriginVisit, OriginVisitStatus +from swh.model.hashutil import hash_to_bytes + + +ORIGIN_VISIT = OriginVisit( + origin="some-url", + visit=1, + date=datetime.datetime.now(tz=datetime.timezone.utc), + type="archive", + status="full", + snapshot=hash_to_bytes("d81cc0710eb6cf9efd5b920a8453e1e07157b6cd"), + metadata=None, +) + + +ORIGIN_VISIT_STATUS = OriginVisitStatus( + origin="some-url", + visit=1, + date=datetime.datetime.now(tz=datetime.timezone.utc), + status="full", + snapshot=hash_to_bytes("d81cc0710eb6cf9efd5b920a8453e1e07157b6cd"), + metadata=None, +) + + +@pytest.fixture +def mock_storage(mocker): + mock_storage = mocker.patch( + "swh.loader.tests.common.origin_get_latest_visit_status" + ) + mock_storage.return_value = ORIGIN_VISIT, ORIGIN_VISIT_STATUS + return mock_storage + + +def test_assert_last_visit_matches_raise(mock_storage, mocker): + """Not finding origin visit_and_statu should raise + + """ + # overwrite so we raise because we do not find the right visit + mock_storage.return_value = None + + with pytest.raises(AssertionError, match="Origin url has no visits"): + assert_last_visit_matches(mock_storage, "url", status="full") + + assert mock_storage.called is True + + +def test_assert_last_visit_matches_wrong_status(mock_storage, mocker): + """Wrong visit detected should raise AssertionError + + """ + expected_status = "partial" + assert ORIGIN_VISIT_STATUS.status != expected_status + with pytest.raises(AssertionError, match="Visit_status has status"): + assert_last_visit_matches(mock_storage, "url", status=expected_status) + + assert mock_storage.called is True + + +def test_assert_last_visit_matches_wrong_type(mock_storage, mocker): + """Wrong visit detected should raise AssertionError + + """ + expected_type = "git" + assert ORIGIN_VISIT.type != expected_type + with pytest.raises(AssertionError, match="Visit has type"): + assert_last_visit_matches( + mock_storage, + "url", + status=ORIGIN_VISIT_STATUS.status, + type=expected_type, # mismatched type will raise + ) + + assert mock_storage.called is True + + +def test_assert_last_visit_matches_wrong_snapshot(mock_storage, mocker): + """Wrong visit detected should raise AssertionError + + """ + expected_snapshot_id = hash_to_bytes("e92cc0710eb6cf9efd5b920a8453e1e07157b6cd") + assert ORIGIN_VISIT_STATUS.snapshot != expected_snapshot_id + + with pytest.raises(AssertionError, match="Visit_status points to snapshot"): + assert_last_visit_matches( + mock_storage, + "url", + status=ORIGIN_VISIT_STATUS.status, + snapshot=expected_snapshot_id, # mismatched snapshot will raise + ) + + assert mock_storage.called is True + + +def test_assert_last_visit_matches(mock_storage, mocker): + """Correct visit detected should return the visit_status + + """ + visit_type = ORIGIN_VISIT.type + visit_status = ORIGIN_VISIT_STATUS.status + visit_snapshot = ORIGIN_VISIT_STATUS.snapshot + + actual_visit_status = assert_last_visit_matches( + mock_storage, + "url", + type=visit_type, + status=visit_status, + snapshot=visit_snapshot, + ) + + assert actual_visit_status == ORIGIN_VISIT_STATUS + assert mock_storage.called is True