import importlib.util from pathlib import Path def _load_utils_module(): module_path = Path(__file__).resolve().parents[2] / "strix" / "interface" / "utils.py" spec = importlib.util.spec_from_file_location("strix_interface_utils_test", module_path) if spec is None or spec.loader is None: raise RuntimeError("Failed to load strix.interface.utils for tests") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module utils = _load_utils_module() def test_parse_name_status_uses_rename_destination_path() -> None: raw = ( b"R100\x00old/path.py\x00new/path.py\x00" b"R75\x00legacy/module.py\x00modern/module.py\x00" b"M\x00src/app.py\x00" b"A\x00src/new_file.py\x00" b"D\x00src/deleted.py\x00" ) entries = utils._parse_name_status_z(raw) classified = utils._classify_diff_entries(entries) assert "new/path.py" in classified["analyzable_files"] assert "old/path.py" not in classified["analyzable_files"] assert "modern/module.py" in classified["analyzable_files"] assert classified["renamed_files"][0]["old_path"] == "old/path.py" assert classified["renamed_files"][0]["new_path"] == "new/path.py" assert "src/deleted.py" in classified["deleted_files"] assert "src/deleted.py" not in classified["analyzable_files"] def test_build_diff_scope_instruction_includes_added_modified_and_deleted_guidance() -> None: scope = utils.RepoDiffScope( source_path="/tmp/repo", workspace_subdir="repo", base_ref="refs/remotes/origin/main", merge_base="abc123", added_files=["src/added.py"], modified_files=["src/changed.py"], renamed_files=[{"old_path": "src/old.py", "new_path": "src/new.py", "similarity": 90}], deleted_files=["src/deleted.py"], analyzable_files=["src/added.py", "src/changed.py", "src/new.py"], ) instruction = utils.build_diff_scope_instruction([scope]) assert "For Added files, review the entire file content." in instruction assert "For Modified files, focus primarily on the changed areas." in instruction assert "Note: These files were deleted" in instruction assert "src/deleted.py" in instruction assert "src/old.py -> src/new.py" in instruction def test_resolve_base_ref_prefers_github_base_ref(monkeypatch) -> None: calls: list[str] = [] def fake_ref_exists(_repo_path: Path, ref: str) -> bool: calls.append(ref) return ref == "refs/remotes/origin/release-2026" monkeypatch.setattr(utils, "_git_ref_exists", fake_ref_exists) monkeypatch.setattr(utils, "_extract_github_base_sha", lambda _env: None) monkeypatch.setattr(utils, "_resolve_origin_head_ref", lambda _repo_path: None) base_ref = utils._resolve_base_ref( Path("/tmp/repo"), diff_base=None, env={"GITHUB_BASE_REF": "release-2026"}, ) assert base_ref == "refs/remotes/origin/release-2026" assert calls[0] == "refs/remotes/origin/release-2026" def test_resolve_base_ref_falls_back_to_remote_main(monkeypatch) -> None: calls: list[str] = [] def fake_ref_exists(_repo_path: Path, ref: str) -> bool: calls.append(ref) return ref == "refs/remotes/origin/main" monkeypatch.setattr(utils, "_git_ref_exists", fake_ref_exists) monkeypatch.setattr(utils, "_extract_github_base_sha", lambda _env: None) monkeypatch.setattr(utils, "_resolve_origin_head_ref", lambda _repo_path: None) base_ref = utils._resolve_base_ref(Path("/tmp/repo"), diff_base=None, env={}) assert base_ref == "refs/remotes/origin/main" assert "refs/remotes/origin/main" in calls assert "origin/main" not in calls