"""Tests for muse/core/shallow.py — shallow graft management. A shallow repo is one whose local history is intentionally incomplete. `.muse/shallow` lists the boundary commits: their snapshots and objects are expected locally, but their parents are not. Coverage: Unit: read/write/add/remove/is_shallow/shallow_path Edge cases: empty file, nonexistent file, duplicate IDs, bare hex IDs, sha256:-prefixed IDs, invalid lines silently skipped Integration: BFS walk stops at shallow boundary (tested via run_verify) """ from __future__ import annotations import json import pathlib import pytest from muse.core.shallow import ( add_shallow, is_shallow, read_shallow, remove_shallow, shallow_path, write_shallow, ) from muse.core.types import long_id from muse.core.paths import muse_dir # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _init_repo(path: pathlib.Path) -> pathlib.Path: muse = muse_dir(path) for d in ("commits", "snapshots", "objects", "refs/heads"): (muse / d).mkdir(parents=True, exist_ok=True) (muse / "HEAD").write_text("ref: refs/heads/main") (muse / "repo.json").write_text(json.dumps({"repo_id": "shallow-test", "domain": "code"})) return path _CID_A = long_id("a" * 64) _CID_B = long_id("b" * 64) _CID_C = long_id("c" * 64) # --------------------------------------------------------------------------- # shallow_path # --------------------------------------------------------------------------- class TestShallowPath: def test_returns_path_under_muse(self, tmp_path: pathlib.Path) -> None: p = shallow_path(tmp_path) assert p == muse_dir(tmp_path) / "shallow" def test_does_not_create_file(self, tmp_path: pathlib.Path) -> None: shallow_path(tmp_path) assert not (muse_dir(tmp_path) / "shallow").exists() # --------------------------------------------------------------------------- # read_shallow # --------------------------------------------------------------------------- class TestReadShallow: def test_returns_empty_frozenset_when_no_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) result = read_shallow(repo) assert result == frozenset() def test_returns_empty_frozenset_for_empty_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text("") assert read_shallow(repo) == frozenset() def test_reads_single_commit(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(_CID_A + "\n") assert read_shallow(repo) == frozenset({_CID_A}) def test_reads_multiple_commits(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n{_CID_B}\n{_CID_C}\n") assert read_shallow(repo) == frozenset({_CID_A, _CID_B, _CID_C}) def test_ignores_blank_lines(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n\n{_CID_B}\n") assert read_shallow(repo) == frozenset({_CID_A, _CID_B}) def test_strips_whitespace(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(f" {_CID_A} \n") assert _CID_A in read_shallow(repo) def test_ignores_comment_lines(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(f"# generated by muse\n{_CID_A}\n") assert read_shallow(repo) == frozenset({_CID_A}) def test_returns_frozenset_not_set(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(_CID_A + "\n") result = read_shallow(repo) assert isinstance(result, frozenset) def test_deduplicates(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text(f"{_CID_A}\n{_CID_A}\n") assert read_shallow(repo) == frozenset({_CID_A}) # --------------------------------------------------------------------------- # write_shallow # --------------------------------------------------------------------------- class TestWriteShallow: def test_writes_empty_set_creates_empty_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, set()) p = muse_dir(repo) / "shallow" assert p.exists() assert p.read_text().strip() == "" def test_writes_single_commit(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) result = read_shallow(repo) assert result == frozenset({_CID_A}) def test_writes_multiple_commits_sorted(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_C, _CID_A, _CID_B}) lines = [l for l in (muse_dir(repo) / "shallow").read_text().splitlines() if l.strip()] assert lines == sorted(lines) def test_round_trips(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) original = {_CID_A, _CID_B, _CID_C} write_shallow(repo, original) assert read_shallow(repo) == frozenset(original) def test_overwrites_existing(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A, _CID_B}) write_shallow(repo, {_CID_C}) assert read_shallow(repo) == frozenset({_CID_C}) def test_creates_muse_dir_if_absent(self, tmp_path: pathlib.Path) -> None: # .muse dir may not exist yet write_shallow(tmp_path, {_CID_A}) assert (muse_dir(tmp_path) / "shallow").exists() # --------------------------------------------------------------------------- # add_shallow # --------------------------------------------------------------------------- class TestAddShallow: def test_adds_to_empty(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) add_shallow(repo, _CID_A) assert read_shallow(repo) == frozenset({_CID_A}) def test_adds_to_existing(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) add_shallow(repo, _CID_B) assert read_shallow(repo) == frozenset({_CID_A, _CID_B}) def test_add_is_idempotent(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) add_shallow(repo, _CID_A) add_shallow(repo, _CID_A) assert read_shallow(repo) == frozenset({_CID_A}) def test_add_when_no_file_exists(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) assert not (muse_dir(repo) / "shallow").exists() add_shallow(repo, _CID_A) assert _CID_A in read_shallow(repo) # --------------------------------------------------------------------------- # remove_shallow # --------------------------------------------------------------------------- class TestRemoveShallow: def test_removes_existing_entry(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A, _CID_B}) remove_shallow(repo, _CID_A) assert read_shallow(repo) == frozenset({_CID_B}) def test_remove_nonexistent_is_noop(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) remove_shallow(repo, _CID_C) # not present assert read_shallow(repo) == frozenset({_CID_A}) def test_remove_from_empty_is_noop(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) remove_shallow(repo, _CID_A) # no file assert read_shallow(repo) == frozenset() def test_remove_last_entry_leaves_empty(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) remove_shallow(repo, _CID_A) assert read_shallow(repo) == frozenset() # --------------------------------------------------------------------------- # is_shallow # --------------------------------------------------------------------------- class TestIsShallow: def test_false_when_no_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) assert is_shallow(repo) is False def test_false_when_empty_file(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) (muse_dir(repo) / "shallow").write_text("") assert is_shallow(repo) is False def test_true_when_has_entry(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) assert is_shallow(repo) is True def test_true_with_multiple_entries(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A, _CID_B}) assert is_shallow(repo) is True def test_false_after_removing_all(self, tmp_path: pathlib.Path) -> None: repo = _init_repo(tmp_path) write_shallow(repo, {_CID_A}) remove_shallow(repo, _CID_A) assert is_shallow(repo) is False