8 diagnostic checks: Python version (3.10+), package install, git, 14 core deps, 10 optional deps, API keys, MCP server, output dir. Each check reports pass/warn/fail with --verbose for extra detail. Exit code 0 if no critical failures, 1 otherwise. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
165 lines
5.0 KiB
Python
165 lines
5.0 KiB
Python
"""Tests for skill-seekers doctor command (#316)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from unittest.mock import patch
|
|
|
|
from skill_seekers.cli.doctor import (
|
|
CheckResult,
|
|
check_api_keys,
|
|
check_core_deps,
|
|
check_git,
|
|
check_mcp_server,
|
|
check_optional_deps,
|
|
check_output_directory,
|
|
check_package_installed,
|
|
check_python_version,
|
|
print_report,
|
|
run_all_checks,
|
|
)
|
|
|
|
|
|
class TestCheckPythonVersion:
|
|
def test_passes_on_current_python(self):
|
|
result = check_python_version()
|
|
assert result.status == "pass"
|
|
assert result.critical is True
|
|
|
|
def test_detail_contains_version(self):
|
|
result = check_python_version()
|
|
assert "." in result.detail # e.g. "3.14.3"
|
|
|
|
|
|
class TestCheckPackageInstalled:
|
|
def test_passes_when_installed(self):
|
|
result = check_package_installed()
|
|
assert result.status == "pass"
|
|
assert result.detail.startswith("v")
|
|
|
|
def test_fails_when_import_broken(self):
|
|
with (
|
|
patch.dict("sys.modules", {"skill_seekers._version": None}),
|
|
patch("builtins.__import__", side_effect=ImportError("mocked")),
|
|
):
|
|
result = check_package_installed()
|
|
assert result.status == "fail"
|
|
|
|
|
|
class TestCheckGit:
|
|
def test_passes_when_git_available(self):
|
|
result = check_git()
|
|
# Most CI/dev environments have git
|
|
assert result.status in ("pass", "warn")
|
|
|
|
def test_warns_when_git_missing(self):
|
|
with patch("skill_seekers.cli.doctor.shutil.which", return_value=None):
|
|
result = check_git()
|
|
assert result.status == "warn"
|
|
|
|
|
|
class TestCheckCoreDeps:
|
|
def test_passes_in_normal_environment(self):
|
|
result = check_core_deps()
|
|
assert result.status == "pass"
|
|
assert result.critical is True
|
|
|
|
def test_detail_shows_count(self):
|
|
result = check_core_deps()
|
|
assert "found" in result.detail.lower() or "missing" in result.detail.lower()
|
|
|
|
|
|
class TestCheckOptionalDeps:
|
|
def test_returns_result(self):
|
|
result = check_optional_deps()
|
|
assert result.status in ("pass", "warn")
|
|
assert "/" in result.detail # e.g. "7/10 installed"
|
|
|
|
|
|
class TestCheckApiKeys:
|
|
def test_warns_when_no_keys(self):
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
result = check_api_keys()
|
|
assert result.status == "warn"
|
|
|
|
def test_passes_when_all_set(self):
|
|
env = {
|
|
"ANTHROPIC_API_KEY": "sk-ant-test123456789",
|
|
"GITHUB_TOKEN": "ghp_test123456789",
|
|
"GOOGLE_API_KEY": "AIza_test123456789",
|
|
"OPENAI_API_KEY": "sk-test123456789",
|
|
}
|
|
with patch.dict(os.environ, env, clear=True):
|
|
result = check_api_keys()
|
|
assert result.status == "pass"
|
|
|
|
def test_partial_keys_warns(self):
|
|
env = {"ANTHROPIC_API_KEY": "sk-ant-test123456789"}
|
|
with patch.dict(os.environ, env, clear=True):
|
|
result = check_api_keys()
|
|
assert result.status == "warn"
|
|
assert "1 set" in result.detail
|
|
|
|
|
|
class TestCheckMcpServer:
|
|
def test_returns_result(self):
|
|
result = check_mcp_server()
|
|
assert result.status in ("pass", "warn")
|
|
|
|
|
|
class TestCheckOutputDirectory:
|
|
def test_passes_in_writable_dir(self):
|
|
result = check_output_directory()
|
|
assert result.status == "pass"
|
|
assert result.critical is True
|
|
|
|
|
|
class TestRunAllChecks:
|
|
def test_returns_8_results(self):
|
|
results = run_all_checks()
|
|
assert len(results) == 8
|
|
|
|
def test_all_have_name_and_status(self):
|
|
results = run_all_checks()
|
|
for r in results:
|
|
assert isinstance(r, CheckResult)
|
|
assert r.name
|
|
assert r.status in ("pass", "warn", "fail")
|
|
|
|
|
|
class TestPrintReport:
|
|
def test_returns_0_when_no_failures(self, capsys):
|
|
results = [
|
|
CheckResult("Test1", "pass", "ok", critical=True),
|
|
CheckResult("Test2", "warn", "meh"),
|
|
]
|
|
code = print_report(results)
|
|
assert code == 0
|
|
captured = capsys.readouterr()
|
|
assert "1 passed" in captured.out
|
|
assert "1 warnings" in captured.out
|
|
|
|
def test_returns_1_when_critical_failure(self, capsys):
|
|
results = [
|
|
CheckResult("Test1", "pass", "ok"),
|
|
CheckResult("Test2", "fail", "broken", critical=True),
|
|
]
|
|
code = print_report(results)
|
|
assert code == 1
|
|
|
|
def test_verbose_shows_detail(self, capsys):
|
|
results = [
|
|
CheckResult("Test1", "pass", "ok", verbose_detail=" extra: info"),
|
|
]
|
|
print_report(results, verbose=True)
|
|
captured = capsys.readouterr()
|
|
assert "extra: info" in captured.out
|
|
|
|
def test_no_verbose_hides_detail(self, capsys):
|
|
results = [
|
|
CheckResult("Test1", "pass", "ok", verbose_detail=" secret: hidden"),
|
|
]
|
|
print_report(results, verbose=False)
|
|
captured = capsys.readouterr()
|
|
assert "secret: hidden" not in captured.out
|