mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:47:12 +01:00
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
205 lines
6.2 KiB
Python
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"
|