1
0
Fork 0

Complete test coverage

This commit is contained in:
Maurizio Porrato 2023-08-18 16:45:28 +01:00
parent 0b4da7c5cf
commit f46348d7a0
6 changed files with 212 additions and 15 deletions

View File

@ -40,6 +40,8 @@ class CheckResult:
return self.severity
def __eq__(self, other: Any) -> bool:
if not isinstance(other, self.__class__):
return False
return (self.kind, self.reason, self.check, self.origin) == (
other.kind,
other.reason,

View File

@ -442,12 +442,11 @@ class Operator:
)
if update_strategy == "semver-mode":
return {x: {y} for x, y in zip(all_bundles, all_bundles[1:])}
if update_strategy == "semver-skippatch":
# TODO: implement semver-skippatch
raise NotImplementedError("%s: semver-skippatch is not implemented yet")
if update_strategy == "replaces-mode":
return self._replaces_graph(channel, all_bundles)
raise ValueError(f"{self}: unknown updateGraph value: {update_strategy}")
raise NotImplementedError(
f"{self}: unsupported updateGraph value: {update_strategy}"
)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, self.__class__):

View File

@ -83,26 +83,21 @@ def action_list(repo: Repo, *what: str, recursive: bool = False) -> None:
show(target, recursive)
def _walk(
target: Union[Repo, Operator, Bundle]
) -> Iterator[Union[Repo, Operator, Bundle]]:
def _walk(target: Union[Operator, Bundle]) -> Iterator[Union[Operator, Bundle]]:
yield target
if isinstance(target, Repo):
for operator in target:
yield from _walk(operator)
elif isinstance(target, Operator):
if isinstance(target, Operator):
yield from target.all_bundles()
def action_check(repo: Repo, suite: str, *what: str, recursive: bool = False) -> None:
if what:
targets: Iterator[Union[Repo, Operator, Bundle]] = (
targets: Iterator[Union[Operator, Bundle]] = (
parse_target(repo, x) for x in what
)
else:
targets = repo.all_operators()
if recursive:
all_targets: Iterator[Union[Repo, Operator, Bundle]] = chain.from_iterable(
all_targets: Iterator[Union[Operator, Bundle]] = chain.from_iterable(
_walk(x) for x in targets
)
else:
@ -119,7 +114,7 @@ def action_check_list(suite: str) -> None:
print(f" - {display_name}: {check.__doc__}")
def _get_repo(path: Optional[Path]) -> Repo:
def _get_repo(path: Optional[Path] = None) -> Repo:
if not path:
path = Path.cwd()
try:
@ -201,5 +196,5 @@ def main() -> None:
main_parser.print_help()
if __name__ == "__main__":
if __name__ == "__main__": # pragma: no cover
main()

View File

@ -15,3 +15,20 @@ def mock_bundle(tmp_path: Path) -> Bundle:
create_files(tmp_path, bundle_files("hello", "0.0.1"))
repo = Repo(tmp_path)
return repo.operator("hello").bundle("0.0.1")
@pytest.fixture
def mock_repo(tmp_path: Path) -> Repo:
"""
Create a dummy file structure for an operator repo with two operators
and a total of four bundles and return the corresponding Repo object
"""
create_files(
tmp_path,
bundle_files("hello", "0.0.1"),
bundle_files("hello", "0.0.2"),
bundle_files("world", "0.0.1"),
bundle_files("world", "0.0.2"),
)
repo = Repo(tmp_path)
return repo

122
tests/test_cli.py Normal file
View File

@ -0,0 +1,122 @@
from pathlib import Path
from typing import Iterator
from unittest.mock import MagicMock, patch
import pytest
from _pytest.capture import CaptureFixture
from operator_repo import Bundle, Operator, Repo
from operator_repo.checks import CheckResult
from operator_repo.cli import _get_repo, main, parse_target
def test_cli_parse_target(mock_repo: Repo) -> None:
assert parse_target(mock_repo, "hello") == mock_repo.operator("hello")
assert parse_target(mock_repo, "world/0.0.1") == mock_repo.operator("world").bundle(
"0.0.1"
)
def test_cli_list(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool", "-r", str(mock_repo.root), "list", "hello"]):
main()
captured = capsys.readouterr()
assert "hello/0.0.1" in captured.out
assert "hello/0.0.2" in captured.out
def test_cli_list_repo(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool", "-r", str(mock_repo.root), "list"]):
main()
captured = capsys.readouterr()
assert "hello" in captured.out
assert "world" in captured.out
def test_cli_list_bundle(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch(
"sys.argv", ["optool", "-r", str(mock_repo.root), "list", "hello/0.0.1"]
):
main()
captured = capsys.readouterr()
assert "hello/0.0.1" in captured.out
assert "hello/0.0.2" not in captured.out
def test_cli_list_recursive(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool", "-r", str(mock_repo.root), "list", "-R"]):
main()
captured = capsys.readouterr()
assert "hello/0.0.1" in captured.out
assert "hello/0.0.2" in captured.out
assert "world/0.0.1" in captured.out
assert "world/0.0.2" in captured.out
def test_cli_check(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool", "-r", str(mock_repo.root), "check", "hello"]):
main()
captured = capsys.readouterr()
assert "Channel beta has dangling bundles" in captured.out
def test_cli_check_recursive(mock_repo: Repo, capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool", "-r", str(mock_repo.root), "check", "-R"]):
main()
captured = capsys.readouterr()
assert "Channel beta has dangling bundles" in captured.out
assert "CSV doesn't define .metadata.annotations.containerImage" in captured.out
def test_cli_check_operator_recursive(
mock_repo: Repo, capsys: CaptureFixture[str]
) -> None:
with patch(
"sys.argv", ["optool", "-r", str(mock_repo.root), "check", "-R", "hello"]
):
main()
captured = capsys.readouterr()
assert "Channel beta has dangling bundles" in captured.out
assert "CSV doesn't define .metadata.annotations.containerImage" in captured.out
@patch("operator_repo.cli.get_checks")
def test_cli_check_list(mock_checks: MagicMock, capsys: CaptureFixture[str]) -> None:
def check_foo(_: Operator) -> Iterator[CheckResult]:
"""This is the check_foo description"""
raise StopIteration
def check_bar(_: Bundle) -> Iterator[CheckResult]:
"""This is the check_bar description"""
raise StopIteration
mock_checks.return_value = {"operator": [check_foo], "bundle": [check_bar]}
with patch("sys.argv", ["optool", "check", "--list"]):
main()
captured = capsys.readouterr()
assert "This is the check_foo description" in captured.out
assert "This is the check_bar description" in captured.out
@patch("operator_repo.cli.Path.cwd")
def test_cli_get_repo(mock_cwd: MagicMock, mock_repo: Repo) -> None:
mock_cwd.return_value = mock_repo.root
assert _get_repo() == mock_repo
@patch("operator_repo.cli.Path.cwd")
def test_cli_get_repo_invalid(
mock_cwd: MagicMock, tmp_path: Path, capsys: CaptureFixture[str]
) -> None:
mock_cwd.return_value = tmp_path
with pytest.raises(SystemExit):
_ = _get_repo()
captured = capsys.readouterr()
assert "is not a valid operator repository" in captured.out
def test_cli_help(capsys: CaptureFixture[str]) -> None:
with patch("sys.argv", ["optool"]):
main()
captured = capsys.readouterr()
assert "usage: optool" in captured.out

View File

@ -103,3 +103,65 @@ def test_update_graph(tmp_path: Path) -> None:
assert update[bundle1] == {bundle2}
assert update[bundle2] == {bundle3, bundle4}
assert update[bundle3] == {bundle4}
def test_update_graph_invalid_replaces(tmp_path: Path) -> None:
create_files(
tmp_path,
bundle_files("hello", "0.0.1"),
bundle_files("hello", "0.0.2", csv={"spec": {"replaces": "other.v0.0.1"}}),
)
repo = Repo(tmp_path)
operator = repo.operator("hello")
with pytest.raises(ValueError, match="replaces a bundle from a different operator"):
_ = operator.update_graph("beta")
def test_update_graph_replaces_missing_bundle(tmp_path: Path) -> None:
create_files(
tmp_path,
bundle_files("hello", "0.0.2", csv={"spec": {"replaces": "hello.v0.0.1"}}),
)
repo = Repo(tmp_path)
operator = repo.operator("hello")
_ = operator.update_graph("beta")
def test_update_graph_invalid_operator_name(tmp_path: Path) -> None:
create_files(
tmp_path,
bundle_files("hello", "0.0.1", csv={"metadata": {"name": "other.v0.0.1"}}),
bundle_files("hello", "0.0.2", csv={"spec": {"replaces": "hello.v0.0.1"}}),
)
repo = Repo(tmp_path)
operator = repo.operator("hello")
with pytest.raises(ValueError, match="has bundles with different operator names"):
_ = operator.update_graph("beta")
def test_update_graph_semver(tmp_path: Path) -> None:
create_files(
tmp_path,
bundle_files("hello", "0.0.1"),
bundle_files("hello", "0.0.2"),
{"operators/hello/ci.yaml": {"updateGraph": "semver-mode"}},
)
repo = Repo(tmp_path)
operator = repo.operator("hello")
bundle1 = operator.bundle("0.0.1")
bundle2 = operator.bundle("0.0.2")
update = operator.update_graph("beta")
assert update[bundle1] == {bundle2}
def test_update_graph_unsupported(tmp_path: Path) -> None:
create_files(
tmp_path,
bundle_files("hello", "0.0.1"),
bundle_files("hello", "0.0.2"),
{"operators/hello/ci.yaml": {"updateGraph": "semver-skippatch"}},
)
repo = Repo(tmp_path)
operator = repo.operator("hello")
with pytest.raises(NotImplementedError, match="unsupported updateGraph value"):
_ = operator.update_graph("beta")