Complete test coverage
This commit is contained in:
parent
0b4da7c5cf
commit
f46348d7a0
|
@ -40,6 +40,8 @@ class CheckResult:
|
||||||
return self.severity
|
return self.severity
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return False
|
||||||
return (self.kind, self.reason, self.check, self.origin) == (
|
return (self.kind, self.reason, self.check, self.origin) == (
|
||||||
other.kind,
|
other.kind,
|
||||||
other.reason,
|
other.reason,
|
||||||
|
|
|
@ -442,12 +442,11 @@ class Operator:
|
||||||
)
|
)
|
||||||
if update_strategy == "semver-mode":
|
if update_strategy == "semver-mode":
|
||||||
return {x: {y} for x, y in zip(all_bundles, all_bundles[1:])}
|
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":
|
if update_strategy == "replaces-mode":
|
||||||
return self._replaces_graph(channel, all_bundles)
|
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:
|
def __eq__(self, other: Any) -> bool:
|
||||||
if not isinstance(other, self.__class__):
|
if not isinstance(other, self.__class__):
|
||||||
|
|
|
@ -83,26 +83,21 @@ def action_list(repo: Repo, *what: str, recursive: bool = False) -> None:
|
||||||
show(target, recursive)
|
show(target, recursive)
|
||||||
|
|
||||||
|
|
||||||
def _walk(
|
def _walk(target: Union[Operator, Bundle]) -> Iterator[Union[Operator, Bundle]]:
|
||||||
target: Union[Repo, Operator, Bundle]
|
|
||||||
) -> Iterator[Union[Repo, Operator, Bundle]]:
|
|
||||||
yield target
|
yield target
|
||||||
if isinstance(target, Repo):
|
if isinstance(target, Operator):
|
||||||
for operator in target:
|
|
||||||
yield from _walk(operator)
|
|
||||||
elif isinstance(target, Operator):
|
|
||||||
yield from target.all_bundles()
|
yield from target.all_bundles()
|
||||||
|
|
||||||
|
|
||||||
def action_check(repo: Repo, suite: str, *what: str, recursive: bool = False) -> None:
|
def action_check(repo: Repo, suite: str, *what: str, recursive: bool = False) -> None:
|
||||||
if what:
|
if what:
|
||||||
targets: Iterator[Union[Repo, Operator, Bundle]] = (
|
targets: Iterator[Union[Operator, Bundle]] = (
|
||||||
parse_target(repo, x) for x in what
|
parse_target(repo, x) for x in what
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
targets = repo.all_operators()
|
targets = repo.all_operators()
|
||||||
if recursive:
|
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
|
_walk(x) for x in targets
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -119,7 +114,7 @@ def action_check_list(suite: str) -> None:
|
||||||
print(f" - {display_name}: {check.__doc__}")
|
print(f" - {display_name}: {check.__doc__}")
|
||||||
|
|
||||||
|
|
||||||
def _get_repo(path: Optional[Path]) -> Repo:
|
def _get_repo(path: Optional[Path] = None) -> Repo:
|
||||||
if not path:
|
if not path:
|
||||||
path = Path.cwd()
|
path = Path.cwd()
|
||||||
try:
|
try:
|
||||||
|
@ -201,5 +196,5 @@ def main() -> None:
|
||||||
main_parser.print_help()
|
main_parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__": # pragma: no cover
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -15,3 +15,20 @@ def mock_bundle(tmp_path: Path) -> Bundle:
|
||||||
create_files(tmp_path, bundle_files("hello", "0.0.1"))
|
create_files(tmp_path, bundle_files("hello", "0.0.1"))
|
||||||
repo = Repo(tmp_path)
|
repo = Repo(tmp_path)
|
||||||
return repo.operator("hello").bundle("0.0.1")
|
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
|
||||||
|
|
|
@ -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
|
|
@ -103,3 +103,65 @@ def test_update_graph(tmp_path: Path) -> None:
|
||||||
assert update[bundle1] == {bundle2}
|
assert update[bundle1] == {bundle2}
|
||||||
assert update[bundle2] == {bundle3, bundle4}
|
assert update[bundle2] == {bundle3, bundle4}
|
||||||
assert update[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")
|
||||||
|
|
Loading…
Reference in New Issue