"""Tests for dotfiles link planning.""" from pathlib import Path from typing import Optional from flow.domain.dotfiles.models import ( LinkOp, LinkTarget, LinkedState, ) from flow.domain.dotfiles.planning import plan_link, plan_unlink def _lt(target, source="/a", pkg="_shared/zsh", module=False, sudo=False): return LinkTarget( source=Path(source), target=Path(target), package=pkg, from_module=module, needs_sudo=sudo, ) def _fs_check_none(path: Path) -> Optional[str]: """Fake filesystem_check: nothing exists.""" return None def _fs_check_file(path: Path) -> Optional[str]: """Fake: everything is a file.""" return "file" class TestPlanLink: def test_new_target_creates_link(self): desired = [_lt("/home/x/.zshrc")] plan = plan_link(desired, LinkedState(), _fs_check_none) assert len(plan.operations) == 1 assert plan.operations[0].type == "create_link" assert plan.summary.added == 1 def test_existing_correct_link_unchanged(self): lt = _lt("/home/x/.zshrc") current = LinkedState(links={Path("/home/x/.zshrc"): lt}) plan = plan_link([lt], current, _fs_check_none) assert len(plan.operations) == 0 assert plan.summary.unchanged == 1 def test_stale_link_removed(self): old = _lt("/home/x/.old") current = LinkedState(links={Path("/home/x/.old"): old}) plan = plan_link([], current, _fs_check_none) assert len(plan.operations) == 1 assert plan.operations[0].type == "remove_link" assert plan.summary.removed == 1 def test_changed_source_produces_remove_then_create(self): old = _lt("/home/x/.zshrc", source="/old") new = _lt("/home/x/.zshrc", source="/new") current = LinkedState(links={Path("/home/x/.zshrc"): old}) plan = plan_link([new], current, _fs_check_none) types = [op.type for op in plan.operations] assert types == ["remove_link", "create_link"] def test_unmanaged_file_at_target_is_conflict(self): desired = [_lt("/home/x/.zshrc")] plan = plan_link(desired, LinkedState(), _fs_check_file) assert len(plan.conflicts) == 1 assert ".zshrc" in plan.conflicts[0] def test_module_targets_counted(self): desired = [_lt("/home/x/.config/nvim/init.lua", module=True)] plan = plan_link(desired, LinkedState(), _fs_check_none) assert plan.summary.from_modules == 1 class TestPlanUnlink: def test_unlink_all(self): lt = _lt("/home/x/.zshrc") current = LinkedState(links={Path("/home/x/.zshrc"): lt}) plan = plan_unlink(current, packages=None) assert len(plan.operations) == 1 assert plan.operations[0].type == "remove_link" def test_unlink_specific_package(self): zsh = _lt("/home/x/.zshrc", pkg="_shared/zsh") git = _lt("/home/x/.gitconfig", pkg="_shared/git") current = LinkedState(links={ Path("/home/x/.zshrc"): zsh, Path("/home/x/.gitconfig"): git, }) plan = plan_unlink(current, packages=["_shared/zsh"]) assert len(plan.operations) == 1 assert plan.operations[0].target == Path("/home/x/.zshrc") def test_unlink_by_basename(self): zsh = _lt("/home/x/.zshrc", pkg="_shared/zsh") current = LinkedState(links={Path("/home/x/.zshrc"): zsh}) plan = plan_unlink(current, packages=["zsh"]) assert len(plan.operations) == 1