Skip to content

Commit d6109b1

Browse files
committed
Add tests for commodore tool command group
1 parent d097ba5 commit d6109b1

8 files changed

Lines changed: 2646 additions & 0 deletions

tests/test_cli.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,18 @@ def test_package_sync_command():
107107
def test_version_command():
108108
exit_status = call("commodore version --help", shell=True)
109109
assert exit_status == 0
110+
111+
112+
def test_tool_list_command():
113+
exit_status = call("commodore tool list --help", shell=True)
114+
assert exit_status == 0
115+
116+
117+
def test_tool_install_command():
118+
exit_status = call("commodore tool install --help", shell=True)
119+
assert exit_status == 0
120+
121+
122+
def test_tool_upgrade_command():
123+
exit_status = call("commodore tool upgrade --help", shell=True)
124+
assert exit_status == 0

tests/test_tools.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import json
2+
3+
from datetime import datetime
4+
from pathlib import Path
5+
from typing import Optional
6+
from unittest.mock import patch
7+
8+
import pytest
9+
import responses
10+
11+
from commodore.config import Config
12+
13+
from commodore import tools
14+
15+
16+
DATA_DIR = Path(__file__).parent.absolute() / "testdata" / "github"
17+
18+
TOOL_VERSIONS = {
19+
"helm": "v3.18.3",
20+
"jb": "0.6.3",
21+
"kustomize": "v5.7.0",
22+
}
23+
24+
25+
class MockToolInfo:
26+
tool: str
27+
path: Optional[str]
28+
version: Optional[str]
29+
30+
def __init__(self, tool: str):
31+
self.tool = tool
32+
self.path = f"/path/to/{tool}"
33+
self.version = TOOL_VERSIONS[tool]
34+
35+
36+
def _parse_list_tools_output(out: str) -> dict[str, list[str]]:
37+
tool = None
38+
tool_lines = {}
39+
for line in out.splitlines():
40+
trimmed_line = " ".join(line.strip().split())
41+
if trimmed_line.startswith(tuple(TOOL_VERSIONS.keys())):
42+
tool = trimmed_line.split(" ")[0]
43+
tool_lines.setdefault(tool, []).append(trimmed_line)
44+
return tool_lines
45+
46+
47+
def _setup_tool_github_responses() -> dict[str, str]:
48+
latest_versions = {}
49+
for repo, tool in {
50+
"helm/helm": "helm",
51+
"projectsyn/jsonnet-bundler": "jb",
52+
"kubernetes-sigs/kustomize": "kustomize",
53+
}.items():
54+
repo_key = repo.replace("/", "-")
55+
with open(DATA_DIR / f"{repo_key}.json", "r", encoding="utf-8") as respf:
56+
resp = json.load(respf)
57+
responses.add(
58+
responses.GET,
59+
f"https://api.github.com:443/repos/{repo}",
60+
json=resp,
61+
status=200,
62+
)
63+
with open(
64+
DATA_DIR / f"{repo_key}-releases-latest.json", "r", encoding="utf-8"
65+
) as latestf:
66+
latest = json.load(latestf)
67+
responses.add(
68+
responses.GET,
69+
f"https://api.github.com:443/repos/{repo}/releases/latest",
70+
json=latest,
71+
status=200,
72+
)
73+
latest_versions[tool] = latest["tag_name"].removeprefix(f"{tool}/")
74+
return latest_versions
75+
76+
77+
def test_toolinfo_unknown():
78+
with pytest.raises(ValueError) as e:
79+
tools.ToolInfo("foo")
80+
assert "Unknown tool foo" in str(e)
81+
82+
83+
def test_load_state(fs):
84+
state = {
85+
"helm": "2025-07-09T15:20:00",
86+
"jb": "2025-07-08T19:20:00",
87+
"kustomize": "2025-07-09T15:22:00",
88+
}
89+
fs.create_file(tools.MANAGED_TOOLS_STATE)
90+
fs.create_file(tools.MANAGED_TOOLS_PATH / "helm")
91+
fs.create_file(tools.MANAGED_TOOLS_PATH / "jb")
92+
with open(tools.MANAGED_TOOLS_STATE, "w", encoding="utf-8") as statef:
93+
json.dump(state, statef)
94+
95+
loaded_state = tools.load_state()
96+
assert loaded_state == state
97+
98+
99+
@patch.object(tools, "ToolInfo")
100+
def test_list_tools_no_version_check(mock_tinfo, config: Config, capsys):
101+
mock_tinfo.side_effect = MockToolInfo
102+
tools.list_tools(config, False)
103+
104+
out, _ = capsys.readouterr()
105+
tool_lines = _parse_list_tools_output(out)
106+
107+
for tool, version in TOOL_VERSIONS.items():
108+
assert tool_lines[tool][0] == f"{tool} {version}"
109+
assert tool_lines[tool][1] == f"Location: /path/to/{tool}"
110+
assert tool_lines[tool][2] == f"Managed: {False}"
111+
assert tool_lines[tool][3] == "Latest version: N/A (Version check skipped)"
112+
assert tool_lines[tool][4] == "Updated: UNKNOWN"
113+
114+
115+
@patch.object(tools, "ToolInfo")
116+
@responses.activate
117+
def test_list_tool_version_check(mock_tinfo, config: Config, capsys):
118+
mock_tinfo.side_effect = MockToolInfo
119+
latest_versions = _setup_tool_github_responses()
120+
121+
tools.list_tools(config, True)
122+
123+
out, _ = capsys.readouterr()
124+
tool_lines = _parse_list_tools_output(out)
125+
126+
for tool, version in TOOL_VERSIONS.items():
127+
assert tool_lines[tool][0] == f"{tool} {version}"
128+
assert tool_lines[tool][1] == f"Location: /path/to/{tool}"
129+
assert tool_lines[tool][2] == f"Managed: {False}"
130+
if latest_versions[tool].removeprefix("v") == TOOL_VERSIONS[tool].removeprefix(
131+
"v"
132+
):
133+
# Latest version is always GH tag name even for tools that report
134+
# their own version without a v prefix.
135+
assert (
136+
tool_lines[tool][3]
137+
== f"Latest version: v{TOOL_VERSIONS[tool].removeprefix('v')} (No upgrade available)"
138+
)
139+
else:
140+
assert (
141+
tool_lines[tool][3]
142+
== f"Latest version: {latest_versions[tool]} (Upgrade available!)"
143+
)
144+
assert tool_lines[tool][4] == "Updated: UNKNOWN"
145+
146+
147+
@patch.object(tools, "ToolInfo")
148+
def test_list_tool_missing(mock_tinfo, config: Config, capsys):
149+
class MockToolInfo2(MockToolInfo):
150+
def __init__(self, tool):
151+
if tool != "jb":
152+
super().__init__(tool)
153+
else:
154+
self.tool = tool
155+
self.path = None
156+
self.version = None
157+
158+
mock_tinfo.side_effect = MockToolInfo2
159+
160+
tools.list_tools(config, False)
161+
162+
out, _ = capsys.readouterr()
163+
tool_lines = _parse_list_tools_output(out)
164+
for tool, version in TOOL_VERSIONS.items():
165+
if tool == "jb":
166+
assert len(tool_lines[tool]) == 1
167+
assert tool_lines[tool][0] == f"{tool} missing!"
168+
else:
169+
assert len(tool_lines[tool]) == 5
170+
assert tool_lines[tool][0] == f"{tool} {version}"

0 commit comments

Comments
 (0)