testsAndMisc/python_pkg/code_tutor/tests/test_cli.py
Krzysztof kuhy Rudnicki e3214c813c
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Clear the seven over-cap code_tutor test modules
Partitioned at the section banners each file already used, into ten new
modules named for what they cover. python_pkg/code_tutor is now entirely
under the cap, from thirteen violations.

The splitter copies the whole original header into each new file and
lets ruff's F401 pass prune it - 59 imports removed correctly, versus a
hand-written import block failing on every earlier test split today.
That does not cover module-level helpers: _make_verifier sat between two
banners and was left behind by its callers, so it moved to conftest.py,
taking test_verifier.py's shadowing copy of _make_live_mock with it.

No patch string changed. Pass count is 217 before and after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 18:06:38 +02:00

205 lines
6.2 KiB
Python

"""Tests for python_pkg.code_tutor.cli."""
from __future__ import annotations
from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch
import requests
from typer.testing import CliRunner
from python_pkg.code_tutor._cli_checks import (
_ensure_fresh_plan,
_ensure_ollama_running,
)
if TYPE_CHECKING:
from pathlib import Path
runner = CliRunner()
def _make_plan(codebase: Path, fingerprint: str = "abc") -> dict:
return {
"codebase_path": str(codebase),
"created_at": "2026-01-01T00:00:00+00:00",
"total_items": 2,
"source_fingerprint": fingerprint,
"sessions": [
{
"id": 1,
"title": "mod",
"items": [
{
"id": "mod.fn",
"file": "mod.py",
"type": "function",
"name": "fn",
"start_line": 1,
"end_line": 3,
"class_name": "",
"depends_on": [],
}
],
}
],
}
# ---------------------------------------------------------------------------
# _ensure_ollama_running
# ---------------------------------------------------------------------------
def test_ensure_ollama_running_already_up() -> None:
mock_console = MagicMock()
mock_resp = MagicMock()
with patch(
"python_pkg.code_tutor._cli_checks.requests.get", return_value=mock_resp
):
result = _ensure_ollama_running(mock_console)
assert result is True
def test_ensure_ollama_running_no_systemctl() -> None:
"""A machine without systemctl should say so, not raise."""
mock_console = MagicMock()
with (
patch(
"python_pkg.code_tutor._cli_checks.requests.get",
side_effect=requests.exceptions.ConnectionError(),
),
patch("python_pkg.code_tutor._cli_checks.shutil.which", return_value=None),
):
result = _ensure_ollama_running(mock_console)
assert result is False
def test_ensure_ollama_running_systemctl_fails() -> None:
import subprocess
mock_console = MagicMock()
with (
patch(
"python_pkg.code_tutor._cli_checks.requests.get",
side_effect=requests.exceptions.ConnectionError(),
),
patch(
"python_pkg.code_tutor._cli_checks.subprocess.run",
side_effect=subprocess.CalledProcessError(1, "systemctl", stderr=b"failed"),
),
):
result = _ensure_ollama_running(mock_console)
assert result is False
def test_ensure_ollama_running_starts_then_up() -> None:
mock_console = MagicMock()
mock_subprocess = MagicMock()
request_calls = [0]
def fake_get(*args: object, **kwargs: object) -> MagicMock:
request_calls[0] += 1
if request_calls[0] == 1:
raise requests.exceptions.ConnectionError
return MagicMock()
with (
patch("python_pkg.code_tutor._cli_checks.requests.get", side_effect=fake_get),
patch(
"python_pkg.code_tutor._cli_checks.subprocess.run",
return_value=mock_subprocess,
),
patch("python_pkg.code_tutor._cli_checks.time.sleep"),
):
result = _ensure_ollama_running(mock_console)
assert result is True
def test_ensure_ollama_running_times_out() -> None:
mock_console = MagicMock()
mock_subprocess = MagicMock()
time_values = iter(
[0, 0, 100]
) # deadline=30, first while check→0 (enter), next→100 (exit)
def fake_get(*args: object, **kwargs: object) -> MagicMock:
raise requests.exceptions.ConnectionError
with (
patch("python_pkg.code_tutor._cli_checks.requests.get", side_effect=fake_get),
patch(
"python_pkg.code_tutor._cli_checks.subprocess.run",
return_value=mock_subprocess,
),
patch(
"python_pkg.code_tutor._cli_checks.time.monotonic",
side_effect=time_values,
),
patch("python_pkg.code_tutor._cli_checks.time.sleep"),
):
result = _ensure_ollama_running(mock_console)
assert result is False
# ---------------------------------------------------------------------------
# _ensure_fresh_plan
# ---------------------------------------------------------------------------
def test_ensure_fresh_plan_no_fingerprint(tmp_path: Path) -> None:
plan = {
"codebase_path": str(tmp_path),
"created_at": "t",
"total_items": 0,
"source_fingerprint": "",
"sessions": [],
}
mock_console = MagicMock()
result = _ensure_fresh_plan(tmp_path, plan, mock_console)
assert result is plan
def test_ensure_fresh_plan_up_to_date(tmp_path: Path) -> None:
plan = _make_plan(tmp_path, fingerprint="same")
mock_console = MagicMock()
with patch(
"python_pkg.code_tutor._cli_checks.codebase_fingerprint", return_value="same"
):
result = _ensure_fresh_plan(tmp_path, plan, mock_console)
assert result is plan
def test_ensure_fresh_plan_stale_no_items(tmp_path: Path) -> None:
plan = _make_plan(tmp_path, fingerprint="old")
mock_console = MagicMock()
with (
patch(
"python_pkg.code_tutor._cli_checks.codebase_fingerprint", return_value="new"
),
patch("python_pkg.code_tutor._cli_checks.extract_items", return_value=[]),
):
result = _ensure_fresh_plan(tmp_path, plan, mock_console)
assert result is plan
def test_ensure_fresh_plan_stale_rebuilds(tmp_path: Path) -> None:
plan = _make_plan(tmp_path, fingerprint="old")
new_plan = _make_plan(tmp_path, fingerprint="new")
mock_console = MagicMock()
mock_items = [MagicMock()]
with (
patch(
"python_pkg.code_tutor._cli_checks.codebase_fingerprint", return_value="new"
),
patch(
"python_pkg.code_tutor._cli_checks.extract_items", return_value=mock_items
),
patch("python_pkg.code_tutor._cli_checks.build_plan", return_value=new_plan),
patch("python_pkg.code_tutor._cli_checks.save_plan"),
):
result = _ensure_fresh_plan(tmp_path, plan, mock_console)
assert result["source_fingerprint"] == "new"