working version
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
"""Tests for flow.commands.bootstrap — action planning."""
|
||||
"""Tests for flow.commands.bootstrap helpers and schema behavior."""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from flow.commands.bootstrap import (
|
||||
_ensure_required_variables,
|
||||
_get_profiles,
|
||||
_plan_actions,
|
||||
_normalize_profile_package_entry,
|
||||
_resolve_package_manager,
|
||||
_resolve_package_name,
|
||||
_resolve_package_spec,
|
||||
_resolve_pkg_source_name,
|
||||
)
|
||||
from flow.core.config import AppConfig, FlowContext
|
||||
from flow.core.console import ConsoleLogger
|
||||
@@ -18,127 +22,28 @@ def ctx():
|
||||
return FlowContext(
|
||||
config=AppConfig(),
|
||||
manifest={
|
||||
"binaries": {
|
||||
"neovim": {
|
||||
"version": "0.10.4",
|
||||
"source": "github:neovim/neovim",
|
||||
"asset-pattern": "nvim-{{os}}-{{arch}}.tar.gz",
|
||||
"platform-map": {"linux-arm64": {"os": "linux", "arch": "arm64"}},
|
||||
"install-script": "echo install",
|
||||
"packages": [
|
||||
{
|
||||
"name": "fd",
|
||||
"type": "pkg",
|
||||
"sources": {"apt": "fd-find", "dnf": "fd-find", "brew": "fd"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "neovim",
|
||||
"type": "binary",
|
||||
"source": "github:neovim/neovim",
|
||||
"version": "0.10.4",
|
||||
"asset-pattern": "nvim-{{os}}-{{arch}}.tar.gz",
|
||||
"platform-map": {"linux-x64": {"os": "linux", "arch": "x64"}},
|
||||
"install": {"bin": ["bin/nvim"]},
|
||||
},
|
||||
]
|
||||
},
|
||||
platform=PlatformInfo(os="linux", arch="arm64", platform="linux-arm64"),
|
||||
platform=PlatformInfo(os="linux", arch="x64", platform="linux-x64"),
|
||||
console=ConsoleLogger(),
|
||||
)
|
||||
|
||||
|
||||
def test_plan_empty_profile(ctx):
|
||||
actions = _plan_actions(ctx, "test", {}, {})
|
||||
assert actions == []
|
||||
|
||||
|
||||
def test_plan_hostname(ctx):
|
||||
actions = _plan_actions(ctx, "test", {"hostname": "myhost"}, {})
|
||||
types = [a.type for a in actions]
|
||||
assert "set-hostname" in types
|
||||
|
||||
|
||||
def test_plan_locale_and_shell(ctx):
|
||||
actions = _plan_actions(ctx, "test", {"locale": "en_US.UTF-8", "shell": "zsh"}, {})
|
||||
types = [a.type for a in actions]
|
||||
assert "set-locale" in types
|
||||
assert "set-shell" in types
|
||||
|
||||
|
||||
def test_plan_packages(ctx):
|
||||
env_config = {
|
||||
"packages": {
|
||||
"standard": ["git", "zsh", "tmux"],
|
||||
"binary": ["neovim"],
|
||||
},
|
||||
}
|
||||
actions = _plan_actions(ctx, "test", env_config, {})
|
||||
types = [a.type for a in actions]
|
||||
assert "pm-update" in types
|
||||
assert "install-packages" in types
|
||||
assert "install-binary" in types
|
||||
|
||||
|
||||
def test_plan_packages_uses_package_map(ctx):
|
||||
ctx.manifest["package-map"] = {
|
||||
"fd": {"apt": "fd-find"},
|
||||
}
|
||||
env_config = {
|
||||
"package-manager": "apt",
|
||||
"packages": {
|
||||
"standard": ["fd"],
|
||||
},
|
||||
}
|
||||
|
||||
actions = _plan_actions(ctx, "test", env_config, {})
|
||||
install = [a for a in actions if a.type == "install-packages"][0]
|
||||
assert install.data["packages"] == ["fd-find"]
|
||||
|
||||
|
||||
def test_plan_ssh_keygen(ctx):
|
||||
env_config = {
|
||||
"ssh_keygen": [
|
||||
{"type": "ed25519", "comment": "test@host", "filename": "id_ed25519"},
|
||||
],
|
||||
}
|
||||
actions = _plan_actions(ctx, "test", env_config, {})
|
||||
types = [a.type for a in actions]
|
||||
assert "generate-ssh-key" in types
|
||||
|
||||
|
||||
def test_plan_runcmd(ctx):
|
||||
env_config = {"runcmd": ["echo hello", "mkdir -p ~/tmp"]}
|
||||
actions = _plan_actions(ctx, "test", env_config, {})
|
||||
run_cmds = [a for a in actions if a.type == "run-command"]
|
||||
assert len(run_cmds) == 2
|
||||
|
||||
|
||||
def test_plan_requires(ctx):
|
||||
env_config = {"requires": ["VAR1", "VAR2"]}
|
||||
actions = _plan_actions(ctx, "test", env_config, {})
|
||||
checks = [a for a in actions if a.type == "check-variable"]
|
||||
assert len(checks) == 2
|
||||
assert all(not a.skip_on_error for a in checks)
|
||||
|
||||
|
||||
def test_plan_full_profile(ctx):
|
||||
"""Test planning with a realistic linux-vm profile."""
|
||||
env_config = {
|
||||
"requires": ["TARGET_HOSTNAME"],
|
||||
"os": "linux",
|
||||
"hostname": "$TARGET_HOSTNAME",
|
||||
"shell": "zsh",
|
||||
"locale": "en_US.UTF-8",
|
||||
"packages": {
|
||||
"standard": ["zsh", "tmux", "git"],
|
||||
"binary": ["neovim"],
|
||||
},
|
||||
"ssh_keygen": [{"type": "ed25519", "comment": "test"}],
|
||||
"configs": ["bin"],
|
||||
"runcmd": ["mkdir -p ~/projects"],
|
||||
}
|
||||
actions = _plan_actions(ctx, "linux-vm", env_config, {"TARGET_HOSTNAME": "myvm"})
|
||||
assert len(actions) >= 8
|
||||
|
||||
types = [a.type for a in actions]
|
||||
assert "check-variable" in types
|
||||
assert "set-hostname" in types
|
||||
assert "set-locale" in types
|
||||
assert "set-shell" in types
|
||||
assert "pm-update" in types
|
||||
assert "install-packages" in types
|
||||
assert "install-binary" in types
|
||||
assert "generate-ssh-key" in types
|
||||
assert "link-config" in types
|
||||
assert "run-command" in types
|
||||
|
||||
|
||||
def test_get_profiles_from_manifest(ctx):
|
||||
ctx.manifest = {"profiles": {"linux": {"os": "linux"}}}
|
||||
assert "linux" in _get_profiles(ctx)
|
||||
@@ -151,38 +56,88 @@ def test_get_profiles_rejects_environments(ctx):
|
||||
|
||||
|
||||
def test_resolve_package_manager_explicit_value(ctx):
|
||||
assert _resolve_package_manager(ctx, {"package-manager": "dnf"}) == "dnf"
|
||||
assert _resolve_package_manager(ctx, {"os": "linux", "package-manager": "dnf"}) == "dnf"
|
||||
|
||||
|
||||
def test_resolve_package_manager_linux_ubuntu(ctx):
|
||||
os_release = "ID=ubuntu\nID_LIKE=debian"
|
||||
assert _resolve_package_manager(ctx, {}, os_release_text=os_release) == "apt"
|
||||
def test_resolve_package_manager_linux_auto_apt(monkeypatch, ctx):
|
||||
monkeypatch.setattr("flow.commands.bootstrap.shutil.which", lambda name: "/usr/bin/apt" if name == "apt" else None)
|
||||
assert _resolve_package_manager(ctx, {"os": "linux"}) == "apt"
|
||||
|
||||
|
||||
def test_resolve_package_manager_linux_fedora(ctx):
|
||||
os_release = "ID=fedora\nID_LIKE=rhel"
|
||||
assert _resolve_package_manager(ctx, {}, os_release_text=os_release) == "dnf"
|
||||
def test_resolve_package_manager_linux_auto_dnf(monkeypatch, ctx):
|
||||
monkeypatch.setattr("flow.commands.bootstrap.shutil.which", lambda name: "/usr/bin/dnf" if name == "dnf" else None)
|
||||
assert _resolve_package_manager(ctx, {"os": "linux"}) == "dnf"
|
||||
|
||||
|
||||
def test_resolve_package_name_with_package_map(ctx):
|
||||
ctx.manifest["package-map"] = {
|
||||
def test_resolve_package_manager_requires_os(ctx):
|
||||
with pytest.raises(RuntimeError, match="must be set"):
|
||||
_resolve_package_manager(ctx, {})
|
||||
|
||||
|
||||
def test_normalize_package_entry_string():
|
||||
assert _normalize_profile_package_entry("git") == {"name": "git"}
|
||||
|
||||
|
||||
def test_normalize_package_entry_type_prefix():
|
||||
assert _normalize_profile_package_entry("cask/wezterm") == {"name": "wezterm", "type": "cask"}
|
||||
|
||||
|
||||
def test_normalize_package_entry_object():
|
||||
out = _normalize_profile_package_entry({"name": "docker", "allow_sudo": True})
|
||||
assert out["name"] == "docker"
|
||||
assert out["allow_sudo"] is True
|
||||
|
||||
|
||||
def test_resolve_package_spec_uses_catalog_type(ctx):
|
||||
catalog = {
|
||||
"fd": {
|
||||
"apt": "fd-find",
|
||||
"dnf": "fd-find",
|
||||
"brew": "fd",
|
||||
"name": "fd",
|
||||
"type": "pkg",
|
||||
"sources": {"apt": "fd-find"},
|
||||
}
|
||||
}
|
||||
assert _resolve_package_name(ctx, "fd", "apt") == "fd-find"
|
||||
assert _resolve_package_name(ctx, "fd", "dnf") == "fd-find"
|
||||
assert _resolve_package_name(ctx, "fd", "brew") == "fd"
|
||||
resolved = _resolve_package_spec(catalog, {"name": "fd"})
|
||||
assert resolved["type"] == "pkg"
|
||||
assert resolved["sources"]["apt"] == "fd-find"
|
||||
|
||||
|
||||
def test_resolve_package_name_falls_back_with_warning(ctx):
|
||||
warnings = []
|
||||
ctx.console.warn = warnings.append
|
||||
ctx.manifest["package-map"] = {"python3-dev": {"apt": "python3-dev"}}
|
||||
def test_resolve_package_spec_defaults_to_pkg(ctx):
|
||||
resolved = _resolve_package_spec({}, {"name": "git"})
|
||||
assert resolved["type"] == "pkg"
|
||||
|
||||
resolved = _resolve_package_name(ctx, "python3-dev", "dnf", warn_missing=True)
|
||||
|
||||
assert resolved == "python3-dev"
|
||||
assert warnings
|
||||
def test_resolve_package_spec_profile_override(ctx):
|
||||
catalog = {
|
||||
"neovim": {
|
||||
"name": "neovim",
|
||||
"type": "binary",
|
||||
"version": "0.10.4",
|
||||
}
|
||||
}
|
||||
resolved = _resolve_package_spec(catalog, {"name": "neovim", "post-install": "echo ok"})
|
||||
assert resolved["type"] == "binary"
|
||||
assert resolved["post-install"] == "echo ok"
|
||||
|
||||
|
||||
def test_resolve_pkg_source_name_with_mapping(ctx):
|
||||
spec = {"name": "fd", "sources": {"apt": "fd-find", "dnf": "fd-find", "brew": "fd"}}
|
||||
assert _resolve_pkg_source_name(spec, "apt") == "fd-find"
|
||||
assert _resolve_pkg_source_name(spec, "dnf") == "fd-find"
|
||||
assert _resolve_pkg_source_name(spec, "brew") == "fd"
|
||||
|
||||
|
||||
def test_resolve_pkg_source_name_fallback_to_name(ctx):
|
||||
spec = {"name": "ripgrep", "sources": {"apt": "ripgrep"}}
|
||||
assert _resolve_pkg_source_name(spec, "dnf") == "ripgrep"
|
||||
|
||||
|
||||
def test_ensure_required_variables_missing_raises():
|
||||
with pytest.raises(RuntimeError, match="Missing required environment variables"):
|
||||
_ensure_required_variables({"requires": ["USER_EMAIL", "TARGET_HOSTNAME"]}, {"USER_EMAIL": "a@b"})
|
||||
|
||||
|
||||
def test_ensure_required_variables_accepts_vars(monkeypatch):
|
||||
env = dict(os.environ)
|
||||
env["USER_EMAIL"] = "a@b"
|
||||
env["TARGET_HOSTNAME"] = "devbox"
|
||||
_ensure_required_variables({"requires": ["USER_EMAIL", "TARGET_HOSTNAME"]}, env)
|
||||
|
||||
Reference in New Issue
Block a user