"""Tests for flow.commands.bootstrap — action planning.""" import pytest from flow.commands.bootstrap import ( _get_profiles, _plan_actions, _resolve_package_manager, _resolve_package_name, ) from flow.core.config import AppConfig, FlowContext from flow.core.console import ConsoleLogger from flow.core.platform import PlatformInfo @pytest.fixture 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", }, }, }, platform=PlatformInfo(os="linux", arch="arm64", platform="linux-arm64"), 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) def test_get_profiles_rejects_environments(ctx): ctx.manifest = {"environments": {"legacy": {"os": "linux"}}} with pytest.raises(RuntimeError, match="no longer supported"): _get_profiles(ctx) def test_resolve_package_manager_explicit_value(ctx): assert _resolve_package_manager(ctx, {"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_fedora(ctx): os_release = "ID=fedora\nID_LIKE=rhel" assert _resolve_package_manager(ctx, {}, os_release_text=os_release) == "dnf" def test_resolve_package_name_with_package_map(ctx): ctx.manifest["package-map"] = { "fd": { "apt": "fd-find", "dnf": "fd-find", "brew": "fd", } } 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" 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"}} resolved = _resolve_package_name(ctx, "python3-dev", "dnf", warn_missing=True) assert resolved == "python3-dev" assert warnings