- Dotfiles: models, module resolution, path resolution, link planning - Packages: models, catalog parsing, resolution, install/remove planning - Remote: target parsing, SSH command building - Containers: image refs, mount resolution, container specs All domain code is pure functions + frozen dataclasses. 88 tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
"""Tests for dotfiles module resolution -- the core bug fix."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from flow.core.errors import ConfigError
|
|
from flow.domain.dotfiles.modules import (
|
|
compute_mount_path,
|
|
module_cache_dir,
|
|
normalize_source,
|
|
parse_module_ref,
|
|
)
|
|
|
|
|
|
class TestComputeMountPath:
|
|
def test_nested_module(self):
|
|
"""_shared/nvim/.config/nvim/_module.yaml -> .config/nvim"""
|
|
result = compute_mount_path(
|
|
module_yaml=Path("/dots/_shared/nvim/.config/nvim/_module.yaml"),
|
|
package_dir=Path("/dots/_shared/nvim"),
|
|
)
|
|
assert result == Path(".config/nvim")
|
|
|
|
def test_root_level_module(self):
|
|
"""_shared/nvim/_module.yaml -> Path('.')"""
|
|
result = compute_mount_path(
|
|
module_yaml=Path("/dots/_shared/nvim/_module.yaml"),
|
|
package_dir=Path("/dots/_shared/nvim"),
|
|
)
|
|
assert result == Path(".")
|
|
|
|
def test_deeply_nested(self):
|
|
result = compute_mount_path(
|
|
module_yaml=Path("/dots/_shared/pkg/.config/a/b/c/_module.yaml"),
|
|
package_dir=Path("/dots/_shared/pkg"),
|
|
)
|
|
assert result == Path(".config/a/b/c")
|
|
|
|
|
|
class TestModuleCacheDir:
|
|
def test_simple_name(self):
|
|
result = module_cache_dir("_shared/nvim", Path("/home/x/.local/share/flow/modules"))
|
|
assert result == Path("/home/x/.local/share/flow/modules/_shared--nvim")
|
|
|
|
def test_profile_name(self):
|
|
result = module_cache_dir("linux-work/nvim", Path("/m"))
|
|
assert result == Path("/m/linux-work--nvim")
|
|
|
|
|
|
class TestNormalizeSource:
|
|
def test_github_shorthand(self):
|
|
assert normalize_source("github:org/repo") == "https://github.com/org/repo.git"
|
|
|
|
def test_full_url_passthrough(self):
|
|
assert normalize_source("https://example.com/repo.git") == "https://example.com/repo.git"
|
|
|
|
def test_ssh_passthrough(self):
|
|
assert normalize_source("git@github.com:org/repo.git") == "git@github.com:org/repo.git"
|
|
|
|
|
|
class TestParseModuleRef:
|
|
def test_branch_ref(self):
|
|
raw = {"source": "github:org/nvim-config", "ref": {"branch": "main"}}
|
|
ref = parse_module_ref(
|
|
raw, package_id="_shared/nvim",
|
|
mount_path=Path(".config/nvim"),
|
|
modules_base=Path("/modules"),
|
|
)
|
|
assert ref.source == "https://github.com/org/nvim-config.git"
|
|
assert ref.ref_type == "branch"
|
|
assert ref.ref_value == "main"
|
|
assert ref.mount_path == Path(".config/nvim")
|
|
assert ref.cache_dir == Path("/modules/_shared--nvim")
|
|
|
|
def test_tag_ref(self):
|
|
raw = {"source": "github:org/repo", "ref": {"tag": "v1.0"}}
|
|
ref = parse_module_ref(raw, "p/x", Path("."), Path("/m"))
|
|
assert ref.ref_type == "tag"
|
|
assert ref.ref_value == "v1.0"
|
|
|
|
def test_missing_source_raises(self):
|
|
with pytest.raises(ConfigError):
|
|
parse_module_ref({}, "p/x", Path("."), Path("/m"))
|
|
|
|
def test_missing_ref_raises(self):
|
|
raw = {"source": "github:org/repo"}
|
|
with pytest.raises(ConfigError):
|
|
parse_module_ref(raw, "p/x", Path("."), Path("/m"))
|
|
|
|
def test_ref_not_dict_raises(self):
|
|
raw = {"source": "github:org/repo", "ref": "main"}
|
|
with pytest.raises(ConfigError):
|
|
parse_module_ref(raw, "p/x", Path("."), Path("/m"))
|
|
|
|
def test_ambiguous_ref_raises(self):
|
|
raw = {"source": "github:org/repo", "ref": {"branch": "main", "tag": "v1"}}
|
|
with pytest.raises(ConfigError):
|
|
parse_module_ref(raw, "p/x", Path("."), Path("/m"))
|