From cd68109381477cac2fe084e0e0d8e9c22d5a5b25 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 12 Jun 2026 10:07:31 +0000 Subject: [PATCH 1/2] test(diff_tool): add case-aware install and path normalization regressions Co-authored-by: PuritanWizard --- .../PyKotor/tests/diff_tool/test_cli_utils.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Libraries/PyKotor/tests/diff_tool/test_cli_utils.py diff --git a/Libraries/PyKotor/tests/diff_tool/test_cli_utils.py b/Libraries/PyKotor/tests/diff_tool/test_cli_utils.py new file mode 100644 index 000000000..681417569 --- /dev/null +++ b/Libraries/PyKotor/tests/diff_tool/test_cli_utils.py @@ -0,0 +1,65 @@ +"""Regression tests for diff_tool CLI path helpers (case-aware install detection).""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +from pykotor.diff_tool.cli_utils import is_kotor_install_dir, normalize_path_arg + + +@pytest.mark.parametrize( + ("raw", "expected"), + [ + (None, None), + ("", None), + (" ", None), + ('"C:/Games/KOTOR"', "C:/Games/KOTOR"), + ("'C:/Games/KOTOR'", "C:/Games/KOTOR"), + ( + 'C:\\Program Files\\Steam\\steamapps\\common\\swkotor" C:\\other', + "C:\\Program Files\\Steam\\steamapps\\common\\swkotor", + ), + ('C:\\Games\\KOTOR\\', "C:\\Games\\KOTOR"), + ], +) +def test_normalize_path_arg_strips_quotes_and_mangled_powershell_paths( + raw: str | None, + expected: str | None, +) -> None: + assert normalize_path_arg(raw) == expected + + +def test_is_kotor_install_dir_false_without_chitin_key(tmp_path: Path) -> None: + install_dir = tmp_path / "kotor" + install_dir.mkdir() + assert is_kotor_install_dir(install_dir) is False + + +def test_is_kotor_install_dir_true_with_chitin_key(tmp_path: Path) -> None: + install_dir = tmp_path / "kotor" + install_dir.mkdir() + (install_dir / "chitin.key").write_bytes(b"key") + assert is_kotor_install_dir(install_dir) is True + + +@pytest.mark.skipif(sys.platform == "win32", reason="Case mismatch semantics differ on Windows filesystems.") +def test_is_kotor_install_dir_case_mismatched_install_path(tmp_path: Path) -> None: + install_dir = tmp_path / "KOTOR" + install_dir.mkdir() + (install_dir / "chitin.key").write_bytes(b"key") + + mismatched = tmp_path / "kotor" + assert is_kotor_install_dir(mismatched) is True + + +def test_tslpatcher_engine_is_kotor_install_dir_matches_cli_utils(tmp_path: Path) -> None: + from pykotor.tslpatcher.diff.engine import is_kotor_install_dir as engine_is_install + + install_dir = tmp_path / "install" + install_dir.mkdir() + (install_dir / "chitin.key").write_bytes(b"key") + + assert engine_is_install(install_dir) == is_kotor_install_dir(install_dir) From 1cc830bc937305a4cbe3c0d01bb8ee4d74daaf88 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 12 Jun 2026 10:07:40 +0000 Subject: [PATCH 2/2] test(cli): add resource json ci progress reporting regressions Co-authored-by: PuritanWizard --- .../tests/cli/test_resource_json_progress.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Libraries/PyKotor/tests/cli/test_resource_json_progress.py diff --git a/Libraries/PyKotor/tests/cli/test_resource_json_progress.py b/Libraries/PyKotor/tests/cli/test_resource_json_progress.py new file mode 100644 index 000000000..79ec47652 --- /dev/null +++ b/Libraries/PyKotor/tests/cli/test_resource_json_progress.py @@ -0,0 +1,82 @@ +"""Regression tests for resource JSON export progress reporting.""" + +from __future__ import annotations + +import io +import logging +from typing import TextIO + +import pytest + +from pykotor.tools.resource_json import ( + _ExportProgressReporter, + _format_progress_bar, + _supports_live_progress, +) + + +class _FakeTTY(io.StringIO): + def isatty(self) -> bool: # noqa: D102 + return True + + +class _FakeNonTTY(io.StringIO): + def isatty(self) -> bool: # noqa: D102 + return False + + +@pytest.mark.parametrize("percent", [0.0, 50.0, 100.0]) +def test_format_progress_bar_clamps_and_fills(percent: float) -> None: + bar = _format_progress_bar(percent) + assert len(bar) == 24 + assert set(bar) <= {"#", "-"} + + +@pytest.mark.parametrize("stream", [_FakeTTY(), _FakeNonTTY()]) +def test_supports_live_progress_disabled_when_ci_env_set( + stream: TextIO, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("CI", "true") + assert _supports_live_progress(stream) is False + + +@pytest.mark.parametrize("stream", [_FakeTTY(), _FakeNonTTY()]) +def test_supports_live_progress_disabled_when_github_actions_set( + stream: TextIO, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("GITHUB_ACTIONS", "1") + assert _supports_live_progress(stream) is False + + +def test_supports_live_progress_true_for_tty_without_ci(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("CI", raising=False) + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + assert _supports_live_progress(_FakeTTY()) is True + + +def test_supports_live_progress_false_for_non_tty_without_ci(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("CI", raising=False) + monkeypatch.delenv("GITHUB_ACTIONS", raising=False) + assert _supports_live_progress(_FakeNonTTY()) is False + + +def test_export_progress_reporter_logs_to_logger_in_ci( + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("CI", "true") + logger = logging.getLogger("test_resource_json_progress") + reporter = _ExportProgressReporter( + logger=logger, + total_resources=2, + stream=_FakeTTY(), + ) + assert reporter.live_updates is False + + with caplog.at_level(logging.INFO, logger="test_resource_json_progress"): + reporter.update(1, "test.2da") + reporter.finish() + + assert any("Writing test.2da" in record.message for record in caplog.records)