"""Tests for bootstrap planning.""" import inspect import pytest from flow.core.errors import ConfigError from flow.domain.bootstrap.models import Profile from flow.domain.bootstrap.planning import parse_profile, plan_bootstrap class TestParseProfile: def test_basic(self): raw = { "os": "linux", "hostname": "dev-box", "shell": "zsh", "packages": ["fd", "ripgrep"], } profile = parse_profile("work", raw) assert profile.name == "work" assert profile.os == "linux" assert profile.hostname == "dev-box" assert profile.shell == "zsh" assert len(profile.packages) == 2 def test_missing_os_raises(self): with pytest.raises(ConfigError, match=r"Profile 'minimal': required field 'os' is missing"): parse_profile("minimal", {}) def test_optional_fields_default(self): profile = parse_profile("minimal", {"os": "linux"}) assert profile.os == "linux" assert profile.hostname is None assert profile.packages == () def test_ssh_keys(self): raw = {"os": "linux", "ssh-keys": [{"path": "~/.ssh/id_ed25519", "type": "ed25519"}]} profile = parse_profile("test", raw) assert len(profile.ssh_keys) == 1 def test_ssh_keys_with_filename(self): raw = {"os": "linux", "ssh-keys": [{"filename": "id_work", "type": "ed25519"}]} profile = parse_profile("test", raw) assert profile.ssh_keys[0]["path"] == "~/.ssh/id_work" def test_env_required(self): profile = parse_profile("test", {"os": "linux", "env-required": ["USER_EMAIL"]}) assert profile.env_required == ("USER_EMAIL",) def test_post_link_and_dotfiles_profile(self): profile = parse_profile( "test", {"os": "linux", "dotfiles-profile": "linux-work", "post-link": "echo done"}, ) assert profile.dotfiles_profile == "linux-work" assert profile.post_link == "echo done" class TestPlanBootstrap: def test_env_is_required_keyword(self): sig = inspect.signature(plan_bootstrap) param = sig.parameters["env"] assert param.default is inspect.Parameter.empty def test_basic_plan(self): profile = Profile( name="test", os="linux", hostname="my-host", locale="en_US.UTF-8", shell="zsh", ssh_keys=[], runcmd=[], packages=["fd"], env_required=[], ) manifest = {"packages": [{"name": "fd", "type": "pkg"}]} plan = plan_bootstrap(profile, manifest, env={}) assert plan.profile == "test" assert plan.total_steps > 0 phases = [a.phase for a in plan.actions] assert "setup" in phases assert "packages" in phases assert "dotfiles" in phases def test_missing_env_raises(self): profile = Profile( name="test", os="linux", hostname=None, locale=None, shell=None, ssh_keys=[], runcmd=[], packages=[], env_required=["REQUIRED_VAR"], ) with pytest.raises(ConfigError, match="REQUIRED_VAR"): plan_bootstrap(profile, {}, env={}) def test_runcmd_produces_action(self): profile = Profile( name="test", os="linux", hostname=None, locale=None, shell=None, ssh_keys=[], runcmd=["echo hello", "echo world"], packages=[], env_required=[], ) plan = plan_bootstrap(profile, {}, env={}) runcmd_actions = [a for a in plan.actions if "custom command" in a.description.lower()] assert len(runcmd_actions) == 1 def test_post_link_produces_action(self): profile = Profile( name="test", os="linux", hostname=None, locale=None, shell=None, ssh_keys=[], runcmd=[], packages=[], env_required=[], post_link="echo done", ) plan = plan_bootstrap(profile, {}, env={}) assert any(action.phase == "post-link" for action in plan.actions) def test_ssh_keys_action(self): profile = Profile( name="test", os="linux", hostname=None, locale=None, shell=None, ssh_keys=[{"path": "~/.ssh/id", "type": "ed25519"}], runcmd=[], packages=[], env_required=[], ) plan = plan_bootstrap(profile, {}, env={}) ssh_actions = [a for a in plan.actions if "SSH" in a.description] assert len(ssh_actions) == 1