"""Tests for flat-layout dotfiles helpers and state format.""" import json from pathlib import Path import pytest from flow.commands.dotfiles import ( LinkSpec, _collect_home_specs, _collect_root_specs, _list_profiles, _load_link_specs_from_state, _save_link_specs_to_state, ) def _make_flow_tree(tmp_path: Path) -> Path: flow_root = tmp_path (flow_root / "_shared" / "git").mkdir(parents=True) (flow_root / "_shared" / "git" / ".gitconfig").write_text("shared") (flow_root / "_shared" / "tmux").mkdir(parents=True) (flow_root / "_shared" / "tmux" / ".tmux.conf").write_text("tmux") (flow_root / "work" / "git").mkdir(parents=True) (flow_root / "work" / "git" / ".gitconfig").write_text("profile") (flow_root / "work" / "nvim").mkdir(parents=True) (flow_root / "work" / "nvim" / ".config" / "nvim").mkdir(parents=True) (flow_root / "work" / "nvim" / ".config" / "nvim" / "init.lua").write_text("-- init") (flow_root / "_root" / "general" / "etc").mkdir(parents=True) (flow_root / "_root" / "general" / "etc" / "hostname").write_text("devbox") return flow_root def test_list_profiles_ignores_reserved_dirs(tmp_path): flow_root = _make_flow_tree(tmp_path) profiles = _list_profiles(flow_root) assert profiles == ["work"] def test_collect_home_specs_conflict_fails(tmp_path): flow_root = _make_flow_tree(tmp_path) home = tmp_path / "home" home.mkdir() with pytest.raises(RuntimeError, match="Conflicting dotfile targets"): _collect_home_specs(flow_root, home, "work", set(), None) def test_collect_root_specs_maps_to_absolute_paths(tmp_path): flow_root = _make_flow_tree(tmp_path) specs = _collect_root_specs(flow_root, set(), include_root=True) assert Path("/etc/hostname") in specs assert specs[Path("/etc/hostname")].package == "_root/general" def test_state_round_trip(tmp_path, monkeypatch): state_file = tmp_path / "linked.json" monkeypatch.setattr("flow.commands.dotfiles.LINKED_STATE", state_file) specs = { Path("/home/user/.gitconfig"): LinkSpec( source=Path("/repo/_shared/git/.gitconfig"), target=Path("/home/user/.gitconfig"), package="_shared/git", ) } _save_link_specs_to_state(specs) loaded = _load_link_specs_from_state() assert Path("/home/user/.gitconfig") in loaded assert loaded[Path("/home/user/.gitconfig")].package == "_shared/git" def test_state_old_format_rejected(tmp_path, monkeypatch): state_file = tmp_path / "linked.json" monkeypatch.setattr("flow.commands.dotfiles.LINKED_STATE", state_file) state_file.write_text( json.dumps( { "links": { "zsh": { "/home/user/.zshrc": "/repo/.zshrc", } } } ) ) with pytest.raises(RuntimeError, match="Unsupported linked state format"): _load_link_specs_from_state()