example
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Tests for flat-layout dotfiles helpers and state format."""
|
||||
"""Tests for dotfiles link planning, root markers, and module sources."""
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -8,11 +9,25 @@ import pytest
|
||||
from flow.commands.dotfiles import (
|
||||
LinkSpec,
|
||||
_collect_home_specs,
|
||||
_collect_root_specs,
|
||||
_list_profiles,
|
||||
_load_link_specs_from_state,
|
||||
_pull_requires_ack,
|
||||
_resolved_package_source,
|
||||
_save_link_specs_to_state,
|
||||
_sync_modules,
|
||||
)
|
||||
from flow.core.config import AppConfig, FlowContext
|
||||
from flow.core.console import ConsoleLogger
|
||||
from flow.core.platform import PlatformInfo
|
||||
|
||||
|
||||
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 _make_flow_tree(tmp_path: Path) -> Path:
|
||||
@@ -25,12 +40,9 @@ def _make_flow_tree(tmp_path: Path) -> Path:
|
||||
|
||||
(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")
|
||||
(flow_root / "_shared" / "dnsmasq" / "_root" / "etc").mkdir(parents=True)
|
||||
(flow_root / "_shared" / "dnsmasq" / "_root" / "etc" / "hostname").write_text("devbox")
|
||||
|
||||
return flow_root
|
||||
|
||||
@@ -47,14 +59,33 @@ def test_collect_home_specs_conflict_fails(tmp_path):
|
||||
home.mkdir()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Conflicting dotfile targets"):
|
||||
_collect_home_specs(flow_root, home, "work", set(), None)
|
||||
_collect_home_specs(_ctx(), 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_collect_home_specs_maps_root_marker_to_absolute(tmp_path):
|
||||
flow_root = tmp_path
|
||||
(flow_root / "_shared" / "dnsmasq" / "_root" / "opt" / "homebrew" / "etc").mkdir(parents=True)
|
||||
src = flow_root / "_shared" / "dnsmasq" / "_root" / "opt" / "homebrew" / "etc" / "dnsmasq.conf"
|
||||
src.write_text("conf")
|
||||
|
||||
home = tmp_path / "home"
|
||||
home.mkdir()
|
||||
|
||||
specs = _collect_home_specs(_ctx(), flow_root, home, None, set(), None)
|
||||
assert Path("/opt/homebrew/etc/dnsmasq.conf") in specs
|
||||
assert specs[Path("/opt/homebrew/etc/dnsmasq.conf")].source == src
|
||||
|
||||
|
||||
def test_collect_home_specs_skip_root_marker(tmp_path):
|
||||
flow_root = tmp_path
|
||||
(flow_root / "_shared" / "dnsmasq" / "_root" / "etc").mkdir(parents=True)
|
||||
(flow_root / "_shared" / "dnsmasq" / "_root" / "etc" / "hostname").write_text("devbox")
|
||||
|
||||
home = tmp_path / "home"
|
||||
home.mkdir()
|
||||
|
||||
specs = _collect_home_specs(_ctx(), flow_root, home, None, {"_root"}, None)
|
||||
assert Path("/etc/hostname") not in specs
|
||||
|
||||
|
||||
def test_state_round_trip(tmp_path, monkeypatch):
|
||||
@@ -92,3 +123,153 @@ def test_state_old_format_rejected(tmp_path, monkeypatch):
|
||||
|
||||
with pytest.raises(RuntimeError, match="Unsupported linked state format"):
|
||||
_load_link_specs_from_state()
|
||||
|
||||
|
||||
def test_module_source_requires_sync(tmp_path):
|
||||
package_dir = tmp_path / "_shared" / "nvim"
|
||||
package_dir.mkdir(parents=True)
|
||||
(package_dir / "_module.yaml").write_text(
|
||||
"source: github:dummy/example\n"
|
||||
"ref:\n"
|
||||
" branch: main\n"
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="Run 'flow dotfiles sync' first"):
|
||||
_resolved_package_source(_ctx(), "_shared/nvim", package_dir)
|
||||
|
||||
|
||||
def test_sync_modules_populates_cache_and_resolves_source(tmp_path, monkeypatch):
|
||||
module_src = tmp_path / "module-src"
|
||||
module_src.mkdir()
|
||||
subprocess.run(["git", "init", "-b", "main", str(module_src)], check=True)
|
||||
(module_src / ".config" / "nvim").mkdir(parents=True)
|
||||
(module_src / ".config" / "nvim" / "init.lua").write_text("-- module")
|
||||
subprocess.run(["git", "-C", str(module_src), "add", "."], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(module_src),
|
||||
"-c",
|
||||
"user.name=Flow Test",
|
||||
"-c",
|
||||
"user.email=flow-test@example.com",
|
||||
"commit",
|
||||
"-m",
|
||||
"init module",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
dotfiles = tmp_path / "dotfiles"
|
||||
package_dir = dotfiles / "_shared" / "nvim"
|
||||
package_dir.mkdir(parents=True)
|
||||
(package_dir / "_module.yaml").write_text(
|
||||
f"source: {module_src}\n"
|
||||
"ref:\n"
|
||||
" branch: main\n"
|
||||
)
|
||||
(package_dir / "notes.txt").write_text("ignore me")
|
||||
|
||||
monkeypatch.setattr("flow.commands.dotfiles.DOTFILES_DIR", dotfiles)
|
||||
monkeypatch.setattr("flow.commands.dotfiles.MODULES_DIR", tmp_path / "modules")
|
||||
|
||||
_sync_modules(_ctx(), verbose=False)
|
||||
resolved = _resolved_package_source(_ctx(), "_shared/nvim", package_dir)
|
||||
|
||||
assert (resolved / ".config" / "nvim" / "init.lua").exists()
|
||||
|
||||
|
||||
def test_module_backed_link_specs_exclude_git_internals(tmp_path, monkeypatch):
|
||||
module_src = tmp_path / "module-src"
|
||||
module_src.mkdir()
|
||||
subprocess.run(["git", "init", "-b", "main", str(module_src)], check=True)
|
||||
(module_src / ".config" / "nvim").mkdir(parents=True)
|
||||
(module_src / ".config" / "nvim" / "init.lua").write_text("-- module")
|
||||
subprocess.run(["git", "-C", str(module_src), "add", "."], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(module_src),
|
||||
"-c",
|
||||
"user.name=Flow Test",
|
||||
"-c",
|
||||
"user.email=flow-test@example.com",
|
||||
"commit",
|
||||
"-m",
|
||||
"init module",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
dotfiles = tmp_path / "dotfiles"
|
||||
package_dir = dotfiles / "_shared" / "nvim"
|
||||
package_dir.mkdir(parents=True)
|
||||
(package_dir / "_module.yaml").write_text(
|
||||
f"source: {module_src}\n"
|
||||
"ref:\n"
|
||||
" branch: main\n"
|
||||
)
|
||||
|
||||
monkeypatch.setattr("flow.commands.dotfiles.DOTFILES_DIR", dotfiles)
|
||||
monkeypatch.setattr("flow.commands.dotfiles.MODULES_DIR", tmp_path / "modules")
|
||||
|
||||
_sync_modules(_ctx(), verbose=False)
|
||||
|
||||
home = tmp_path / "home"
|
||||
home.mkdir()
|
||||
specs = _collect_home_specs(_ctx(), dotfiles, home, None, set(), None)
|
||||
|
||||
assert home / ".config" / "nvim" / "init.lua" in specs
|
||||
assert not any(target.relative_to(home).parts[0] == ".git" for target in specs)
|
||||
|
||||
|
||||
def test_sync_modules_resolves_relative_source_independent_of_cwd(tmp_path, monkeypatch):
|
||||
module_src = tmp_path / "module-src"
|
||||
module_src.mkdir()
|
||||
subprocess.run(["git", "init", "-b", "main", str(module_src)], check=True)
|
||||
(module_src / ".config" / "nvim").mkdir(parents=True)
|
||||
(module_src / ".config" / "nvim" / "init.lua").write_text("-- module")
|
||||
subprocess.run(["git", "-C", str(module_src), "add", "."], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"-C",
|
||||
str(module_src),
|
||||
"-c",
|
||||
"user.name=Flow Test",
|
||||
"-c",
|
||||
"user.email=flow-test@example.com",
|
||||
"commit",
|
||||
"-m",
|
||||
"init module",
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
dotfiles = tmp_path / "dotfiles"
|
||||
package_dir = dotfiles / "_shared" / "nvim"
|
||||
package_dir.mkdir(parents=True)
|
||||
relative_source = Path("../../../module-src")
|
||||
(package_dir / "_module.yaml").write_text(
|
||||
f"source: {relative_source}\n"
|
||||
"ref:\n"
|
||||
" branch: main\n"
|
||||
)
|
||||
|
||||
unrelated_cwd = tmp_path / "unrelated-cwd"
|
||||
unrelated_cwd.mkdir()
|
||||
monkeypatch.chdir(unrelated_cwd)
|
||||
monkeypatch.setattr("flow.commands.dotfiles.DOTFILES_DIR", dotfiles)
|
||||
monkeypatch.setattr("flow.commands.dotfiles.MODULES_DIR", tmp_path / "modules")
|
||||
|
||||
_sync_modules(_ctx(), verbose=False)
|
||||
resolved = _resolved_package_source(_ctx(), "_shared/nvim", package_dir)
|
||||
|
||||
assert (resolved / ".config" / "nvim" / "init.lua").exists()
|
||||
|
||||
|
||||
def test_pull_requires_ack_only_on_real_updates():
|
||||
assert _pull_requires_ack("Already up to date.\n", "") is False
|
||||
assert _pull_requires_ack("Updating 123..456\n", "") is True
|
||||
|
||||
Reference in New Issue
Block a user