"""Unit tests for porcelain parsing and ChangeType mapping (Section 6).""" from __future__ import annotations import pytest from git_auto_tracker.changes.models import ChangeType from git_auto_tracker.errors import GitError from git_auto_tracker.git.status import RawStatusEntry, parse_porcelain_z def _build(entries: list[tuple[str, str, str | None]]) -> bytes: """Serialize porcelain -z records: 'XY path' + optional origin path.""" parts: list[bytes] = [] for xy, path, previous in entries: parts.append(f"{xy} {path}".encode()) if previous: parts.append(previous.encode()) return b"\x00".join(parts) + b"\x00" class TestParsePorcelainZ: def test_modified(self) -> None: raw = _build([(" M", "config.xml", None)]) (entry,) = parse_porcelain_z(raw) assert entry.path == "config.xml" assert entry.change_type is ChangeType.MODIFIED def test_untracked_is_added(self) -> None: raw = _build([("??", "new.xml", None)]) (entry,) = parse_porcelain_z(raw) assert entry.change_type is ChangeType.ADDED def test_deleted(self) -> None: raw = _build([(" D", "gone.xml", None)]) (entry,) = parse_porcelain_z(raw) assert entry.change_type is ChangeType.DELETED def test_staged_deletion(self) -> None: raw = _build([("D ", "gone.xml", None)]) (entry,) = parse_porcelain_z(raw) assert entry.change_type is ChangeType.DELETED def test_rename_with_previous(self) -> None: raw = _build([("R ", "new.xml", "old.xml")]) (entry,) = parse_porcelain_z(raw) assert entry.change_type is ChangeType.RENAMED assert entry.previous_path == "old.xml" def test_filenames_with_spaces_and_unicode(self) -> None: raw = _build([(" M", "Station PS101/räumliche Datei.xml", None)]) (entry,) = parse_porcelain_z(raw) assert entry.path == "Station PS101/räumliche Datei.xml" def test_trailing_empty_chunks_ignored(self) -> None: raw = b"\x00\x00 M a.xml\x00\x00" entries = parse_porcelain_z(raw) assert len(entries) == 1 def test_invalid_xy_rejected(self) -> None: with pytest.raises(GitError): parse_porcelain_z(b"XX path.xml\x00") class TestChangeTypeMapping: def test_staged_addition(self) -> None: entry = RawStatusEntry.from_zy_pair("A ", "a.xml", None) assert entry.change_type is ChangeType.ADDED def test_staged_rename(self) -> None: entry = RawStatusEntry.from_zy_pair("R ", "a.xml", "b.xml") assert entry.change_type is ChangeType.RENAMED