110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
"""Tests for bootstrap planning."""
|
|
|
|
import pytest
|
|
|
|
from flow.core.errors import ConfigError
|
|
from flow.domain.bootstrap.models import BootstrapAction, 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_defaults(self):
|
|
profile = parse_profile("minimal", {})
|
|
assert profile.os == "linux"
|
|
assert profile.hostname is None
|
|
assert profile.packages == ()
|
|
|
|
def test_ssh_keys(self):
|
|
raw = {"ssh-keys": [{"path": "~/.ssh/id_ed25519", "type": "ed25519"}]}
|
|
profile = parse_profile("test", raw)
|
|
assert len(profile.ssh_keys) == 1
|
|
|
|
def test_ssh_keygen_alias(self):
|
|
raw = {"ssh-keygen": [{"filename": "id_work", "type": "ed25519"}]}
|
|
profile = parse_profile("test", raw)
|
|
assert profile.ssh_keys[0]["path"] == "~/.ssh/id_work"
|
|
|
|
def test_requires_alias(self):
|
|
profile = parse_profile("test", {"requires": ["USER_EMAIL"]})
|
|
assert profile.env_required == ("USER_EMAIL",)
|
|
|
|
def test_post_link_and_dotfiles_profile(self):
|
|
profile = parse_profile("test", {"dotfiles-profile": "linux-work", "post-link": "echo done"})
|
|
assert profile.dotfiles_profile == "linux-work"
|
|
assert profile.post_link == "echo done"
|
|
|
|
|
|
class TestPlanBootstrap:
|
|
def test_basic_plan(self):
|
|
profile = Profile(
|
|
name="test", os="linux", arch=None,
|
|
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)
|
|
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, monkeypatch):
|
|
monkeypatch.delenv("REQUIRED_VAR", raising=False)
|
|
profile = Profile(
|
|
name="test", os="linux", arch=None,
|
|
hostname=None, locale=None, shell=None,
|
|
ssh_keys=[], runcmd=[], packages=[],
|
|
env_required=["REQUIRED_VAR"],
|
|
)
|
|
with pytest.raises(ConfigError, match="REQUIRED_VAR"):
|
|
plan_bootstrap(profile, {})
|
|
|
|
def test_runcmd_produces_action(self):
|
|
profile = Profile(
|
|
name="test", os="linux", arch=None,
|
|
hostname=None, locale=None, shell=None,
|
|
ssh_keys=[], runcmd=["echo hello", "echo world"],
|
|
packages=[], env_required=[],
|
|
)
|
|
plan = plan_bootstrap(profile, {})
|
|
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", arch=None,
|
|
hostname=None, locale=None, shell=None,
|
|
ssh_keys=[], runcmd=[], packages=[], env_required=[],
|
|
post_link="echo done",
|
|
)
|
|
plan = plan_bootstrap(profile, {})
|
|
assert any(action.phase == "post-link" for action in plan.actions)
|
|
|
|
def test_ssh_keys_action(self):
|
|
profile = Profile(
|
|
name="test", os="linux", arch=None,
|
|
hostname=None, locale=None, shell=None,
|
|
ssh_keys=[{"path": "~/.ssh/id", "type": "ed25519"}],
|
|
runcmd=[], packages=[], env_required=[],
|
|
)
|
|
plan = plan_bootstrap(profile, {})
|
|
ssh_actions = [a for a in plan.actions if "SSH" in a.description]
|
|
assert len(ssh_actions) == 1
|