testsAndMisc/python_pkg/repo_explorer/tests/test_discovery.py
Krzysztof kuhy Rudnicki ee27d10fef Reduce per-file-ignores by fixing lint violations across codebase
Fix ruff violations in ~15 source files and ~60+ test files to minimize
per-file-ignores in pyproject.toml. Remaining ignores are justified with
comments explaining why each suppression is necessary.

Source fixes: FBT003 (keyword args), S310 (URL validation), SLF001
(private access), T201 (print→logging), C901 (complexity), E501 (line
length), E402 (import order).

Test fixes: SIM117 (combined with), FBT (boolean args), PERF203 (try in
loop), S310/S607 (URLs/executables), E402/E501 (imports/lines), S108
(tmp paths), PLR0913 (too many args), ARG (unused args), ANN (type
annotations), RUF059 (unused unpacked vars), PT019 (fixture naming).

Remaining per-file-ignores (with justifications):
- Tests: ARG, D, PLC0415, PLR2004, S101, SLF001
- music_gen sources: PLC0415 (heavy ML lazy imports)
- moviepy_showcase: PLC0415 (circular dependency)
- generate_images: PLR0913 (matplotlib helpers need many params)
- praca_magisterska_video: E501, E402 (long paths, mpl.use)
2026-03-25 18:58:05 +01:00

285 lines
10 KiB
Python

"""Tests for python_pkg.repo_explorer._discovery."""
from __future__ import annotations
from pathlib import Path, PurePosixPath
from unittest.mock import MagicMock, patch
from python_pkg.repo_explorer._discovery import (
IGNORED_DIRS,
_desc_from_run_sh,
_find_terminal,
_is_ignored,
_strip_ansi,
find_projects,
get_description,
)
# ── _strip_ansi ──────────────────────────────────────────────────────
class TestStripAnsi:
def test_removes_colour_codes(self) -> None:
assert _strip_ansi("\x1b[31mred\x1b[0m") == "red"
def test_no_ansi(self) -> None:
assert _strip_ansi("plain text") == "plain text"
def test_empty_string(self) -> None:
assert _strip_ansi("") == ""
def test_complex_ansi(self) -> None:
assert _strip_ansi("\x1b[1;32mgreen\x1b[0m rest") == "green rest"
# ── _find_terminal ───────────────────────────────────────────────────
class TestFindTerminal:
@patch("python_pkg.repo_explorer._discovery.shutil.which")
def test_first_candidate_found(self, mock_which: MagicMock) -> None:
mock_which.return_value = "/usr/bin/kitty"
result = _find_terminal()
assert result == ["kitty", "--"]
@patch("python_pkg.repo_explorer._discovery.shutil.which")
def test_later_candidate_found(self, mock_which: MagicMock) -> None:
def side_effect(exe: str) -> str | None:
return "/usr/bin/xterm" if exe == "xterm" else None
mock_which.side_effect = side_effect
result = _find_terminal()
assert result == ["xterm", "-e"]
@patch("python_pkg.repo_explorer._discovery.shutil.which")
def test_none_found(self, mock_which: MagicMock) -> None:
mock_which.return_value = None
result = _find_terminal()
assert result == []
# ── _is_ignored ──────────────────────────────────────────────────────
class TestIsIgnored:
def test_ignored_dir(self) -> None:
assert _is_ignored(Path("project/.git/config"))
def test_not_ignored(self) -> None:
assert not _is_ignored(Path("project/src/main.py"))
def test_ignored_pycache(self) -> None:
assert _is_ignored(Path("a/__pycache__/b.pyc"))
def test_all_ignored_dirs_recognized(self) -> None:
for d in IGNORED_DIRS:
assert _is_ignored(Path(d) / "file.txt")
# ── find_projects ────────────────────────────────────────────────────
class TestFindProjects:
@patch("python_pkg.repo_explorer._discovery._is_ignored")
def test_finds_run_sh(self, mock_ignored: MagicMock) -> None:
mock_ignored.return_value = False
root = MagicMock(spec=Path)
run1 = MagicMock(spec=Path)
proj1 = MagicMock(spec=Path)
run1.parent = proj1
proj1.name = "proj1"
proj1.relative_to.return_value = PurePosixPath("sub/proj1")
root.rglob.return_value = [run1]
result = find_projects(root)
assert len(result) == 1
assert result[0]["path"] is proj1
assert result[0]["name"] == "proj1"
@patch("python_pkg.repo_explorer._discovery._is_ignored")
def test_filters_ignored(self, mock_ignored: MagicMock) -> None:
mock_ignored.return_value = True
root = MagicMock(spec=Path)
run1 = MagicMock(spec=Path)
root.rglob.return_value = [run1]
result = find_projects(root)
assert result == []
def test_empty_root(self) -> None:
root = MagicMock(spec=Path)
root.rglob.return_value = []
result = find_projects(root)
assert result == []
# ── _desc_from_run_sh ────────────────────────────────────────────────
class TestDescFromRunSh:
def test_with_shebang_and_comments(self) -> None:
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = (
"#!/bin/bash\n# First line\n# Second line\necho hi"
)
result = _desc_from_run_sh(run_sh)
assert result == "First line Second line"
def test_only_shebang(self) -> None:
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = "#!/bin/bash\necho hi"
result = _desc_from_run_sh(run_sh)
assert result == ""
def test_comments_only(self) -> None:
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = "# Just a comment\n# Another one"
result = _desc_from_run_sh(run_sh)
assert result == "Just a comment Another one"
def test_empty_file(self) -> None:
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = ""
result = _desc_from_run_sh(run_sh)
assert result == ""
def test_truncates_at_300(self) -> None:
run_sh = MagicMock(spec=Path)
long_comment = "# " + "x" * 400
run_sh.read_text.return_value = long_comment
result = _desc_from_run_sh(run_sh)
assert len(result) == 300
def test_non_comment_line_without_prior_comments(self) -> None:
"""Non-comment before comments: comments still collected."""
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = "echo hello\n# comment after code"
result = _desc_from_run_sh(run_sh)
assert result == "comment after code"
def test_break_on_non_comment_after_comments(self) -> None:
run_sh = MagicMock(spec=Path)
run_sh.read_text.return_value = "# first\ncode\n# ignored"
result = _desc_from_run_sh(run_sh)
assert result == "first"
# ── get_description ──────────────────────────────────────────────────
class TestGetDescription:
def test_readme_md_with_heading(self) -> None:
mock_path = MagicMock(spec=Path)
readme = MagicMock(spec=Path)
readme.exists.return_value = True
readme.read_text.return_value = "# My Project\nDetails here"
def truediv(_self: object, name: str) -> MagicMock:
if name == "README.md":
return readme
m = MagicMock(spec=Path)
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "My Project"
def test_readme_txt(self) -> None:
mock_path = MagicMock(spec=Path)
def truediv(_self: object, name: str) -> MagicMock:
m = MagicMock(spec=Path)
if name == "README.txt":
m.exists.return_value = True
m.read_text.return_value = "Text readme content"
else:
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "Text readme content"
def test_readme_lower(self) -> None:
mock_path = MagicMock(spec=Path)
def truediv(_self: object, name: str) -> MagicMock:
m = MagicMock(spec=Path)
if name == "readme.md":
m.exists.return_value = True
m.read_text.return_value = "## Lower readme"
else:
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "Lower readme"
def test_readme_all_empty_lines(self) -> None:
"""README exists but all lines strip to empty."""
mock_path = MagicMock(spec=Path)
def truediv(_self: object, name: str) -> MagicMock:
m = MagicMock(spec=Path)
if name == "README.md":
m.exists.return_value = True
m.read_text.return_value = "###\n \n"
else:
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
# README.md has only empty/whitespace lines → falls through
# README.txt and readme.md don't exist → falls to run.sh
result = get_description(mock_path)
# run.sh also doesn't exist so "(no description)"
assert result == "(no description)"
@patch("python_pkg.repo_explorer._discovery._desc_from_run_sh")
def test_no_readme_run_sh_with_desc(self, mock_desc: MagicMock) -> None:
mock_desc.return_value = "From run.sh"
mock_path = MagicMock(spec=Path)
run_sh = MagicMock(spec=Path)
run_sh.exists.return_value = True
def truediv(_self: object, name: str) -> MagicMock:
if name == "run.sh":
return run_sh
m = MagicMock(spec=Path)
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "From run.sh"
@patch("python_pkg.repo_explorer._discovery._desc_from_run_sh")
def test_no_readme_run_sh_empty_desc(self, mock_desc: MagicMock) -> None:
mock_desc.return_value = ""
mock_path = MagicMock(spec=Path)
run_sh = MagicMock(spec=Path)
run_sh.exists.return_value = True
def truediv(_self: object, name: str) -> MagicMock:
if name == "run.sh":
return run_sh
m = MagicMock(spec=Path)
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "(no description)"
def test_no_readme_no_run_sh(self) -> None:
mock_path = MagicMock(spec=Path)
def truediv(_self: object, _name: str) -> MagicMock:
m = MagicMock(spec=Path)
m.exists.return_value = False
return m
mock_path.__truediv__ = truediv
result = get_description(mock_path)
assert result == "(no description)"