105 lines
3.2 KiB
Python
105 lines
3.2 KiB
Python
"""Tests for flow.commands.dotfiles discovery and path resolution."""
|
|
|
|
import pytest
|
|
|
|
from flow.commands.dotfiles import _collect_home_specs, _discover_packages, _resolve_edit_target, _walk_package
|
|
from flow.core.config import AppConfig, FlowContext
|
|
from flow.core.console import ConsoleLogger
|
|
from flow.core.platform import PlatformInfo
|
|
|
|
|
|
def _make_tree(tmp_path):
|
|
flow_root = tmp_path
|
|
shared = flow_root / "_shared"
|
|
(shared / "zsh").mkdir(parents=True)
|
|
(shared / "zsh" / ".zshrc").write_text("# zsh")
|
|
(shared / "tmux").mkdir(parents=True)
|
|
(shared / "tmux" / ".tmux.conf").write_text("# tmux")
|
|
|
|
profile = flow_root / "work"
|
|
(profile / "git").mkdir(parents=True)
|
|
(profile / "git" / ".gitconfig").write_text("[user]\nname = Work")
|
|
|
|
return tmp_path
|
|
|
|
|
|
def _ctx() -> FlowContext:
|
|
return FlowContext(
|
|
config=AppConfig(),
|
|
manifest={"profiles": {"work": {"os": "linux", "configs": {"skip": []}}}},
|
|
platform=PlatformInfo(os="linux", arch="x64", platform="linux-x64"),
|
|
console=ConsoleLogger(),
|
|
)
|
|
|
|
|
|
def test_discover_packages_shared_only(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
packages = _discover_packages(tree)
|
|
assert "zsh" in packages
|
|
assert "tmux" in packages
|
|
assert "git" not in packages
|
|
|
|
|
|
def test_discover_packages_with_profile(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
packages = _discover_packages(tree, profile="work")
|
|
assert "zsh" in packages
|
|
assert "tmux" in packages
|
|
assert "git" in packages
|
|
|
|
|
|
def test_discover_packages_profile_overrides_shared(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
profile_zsh = tree / "work" / "zsh"
|
|
profile_zsh.mkdir(parents=True)
|
|
(profile_zsh / ".zshrc").write_text("# work zsh")
|
|
|
|
with pytest.raises(RuntimeError, match="Conflicting dotfile targets"):
|
|
_collect_home_specs(_ctx(), tree, tmp_path / "home", "work", set(), None)
|
|
|
|
|
|
def test_walk_package_returns_relative_paths(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
source = tree / "_shared" / "zsh"
|
|
|
|
pairs = list(_walk_package(source))
|
|
assert len(pairs) == 1
|
|
src, rel = pairs[0]
|
|
assert src.name == ".zshrc"
|
|
assert str(rel) == ".zshrc"
|
|
|
|
|
|
def test_resolve_edit_target_package(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
target = _resolve_edit_target("zsh", dotfiles_dir=tree)
|
|
assert target == tree / "_shared" / "zsh"
|
|
|
|
|
|
def test_resolve_edit_target_repo_path(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
target = _resolve_edit_target("_shared/zsh/.zshrc", dotfiles_dir=tree)
|
|
assert target == tree / "_shared" / "zsh" / ".zshrc"
|
|
|
|
|
|
def test_resolve_edit_target_rejects_parent_traversal(tmp_path):
|
|
tree = _make_tree(tmp_path / "repo")
|
|
outside = tmp_path / "outside.txt"
|
|
outside.write_text("secret")
|
|
|
|
target = _resolve_edit_target("../outside.txt", dotfiles_dir=tree)
|
|
assert target is None
|
|
|
|
|
|
def test_resolve_edit_target_rejects_nested_repo_escape(tmp_path):
|
|
tree = _make_tree(tmp_path / "repo")
|
|
outside = tmp_path / "escape.txt"
|
|
outside.write_text("secret")
|
|
|
|
target = _resolve_edit_target("_shared/../../escape.txt", dotfiles_dir=tree)
|
|
assert target is None
|
|
|
|
|
|
def test_resolve_edit_target_missing_returns_none(tmp_path):
|
|
tree = _make_tree(tmp_path)
|
|
assert _resolve_edit_target("does-not-exist", dotfiles_dir=tree) is None
|