"""Tests for BootstrapService.""" import pytest from flow.core.config import AppConfig, FlowContext from flow.core.console import Console from flow.core.errors import FlowError from flow.core.platform import PlatformInfo from flow.core.runtime import SystemRuntime from flow.app.bootstrap import BootstrapService def _make_ctx(manifest=None): return FlowContext( config=AppConfig(), manifest=manifest or {}, platform=PlatformInfo(), console=Console(color=False), runtime=SystemRuntime(), ) class TestBootstrapService: def test_show_profile(self, capsys): manifest = { "profiles": { "work": { "os": "linux", "hostname": "dev", "packages": ["fd"], }, }, "packages": [{"name": "fd", "type": "pkg"}], } ctx = _make_ctx(manifest) svc = BootstrapService(ctx) svc.show("work") output = capsys.readouterr().out assert "work" in output def test_unknown_profile_raises(self): ctx = _make_ctx({"profiles": {}}) svc = BootstrapService(ctx) with pytest.raises(FlowError, match="Unknown profile"): svc.run("missing") def test_list_profiles(self, capsys): manifest = { "profiles": { "work": {"os": "linux", "hostname": "dev"}, "personal": {"os": "linux"}, }, } ctx = _make_ctx(manifest) svc = BootstrapService(ctx) svc.list_profiles() output = capsys.readouterr().out assert "work" in output assert "personal" in output def test_list_profiles_empty(self, capsys): ctx = _make_ctx({}) svc = BootstrapService(ctx) svc.list_profiles() assert "No profiles" in capsys.readouterr().out def test_run_preserves_profile_package_overrides(self, monkeypatch): captured = {} class StubPackageService: def __init__(self, ctx): pass def install(self, packages, *, dry_run=False): captured["packages"] = packages monkeypatch.setattr("flow.app.packages.PackageService", StubPackageService) monkeypatch.setattr("flow.app.dotfiles.DotfilesService.link", lambda self, profile=None: None) manifest = { "profiles": { "linux-auto": { "os": "linux", "packages": [{ "name": "docker", "post-install": "sudo groupadd docker || true", }], }, }, "packages": [{"name": "docker", "type": "pkg", "sources": {"apt": "docker-ce"}}], } ctx = _make_ctx(manifest) BootstrapService(ctx).run("linux-auto") assert captured["packages"][0].post_install == "sudo groupadd docker || true" def test_unknown_phase_raises(self): from flow.domain.bootstrap.models import BootstrapAction, BootstrapPlan manifest = {"profiles": {"work": {"os": "linux"}}} ctx = _make_ctx(manifest) svc = BootstrapService(ctx) # Forge an action with a phase the dispatcher doesn't handle. # The trailing raise in _execute_action is the single source of # truth for unhandled phases — adding a phase to VALID_PHASES # without a handler should surface here. action = BootstrapAction.__new__(BootstrapAction) object.__setattr__(action, "phase", "no-such-phase") object.__setattr__(action, "description", "") object.__setattr__(action, "commands", ()) object.__setattr__(action, "needs_sudo", False) plan = BootstrapPlan(profile="work", actions=(), packages_to_install=()) with pytest.raises(FlowError, match="Unhandled bootstrap phase"): svc._execute_action(action, plan, "work") def test_run_uses_dotfiles_profile_override(self, monkeypatch): captured = {} monkeypatch.setattr("flow.app.packages.PackageService.install", lambda self, packages, dry_run=False: None) class StubDotfilesService: def __init__(self, ctx): pass def link(self, profile=None): captured["profile"] = profile monkeypatch.setattr("flow.app.dotfiles.DotfilesService", StubDotfilesService) manifest = { "profiles": { "linux-auto": { "os": "linux", "dotfiles-profile": "linux-work", }, }, } ctx = _make_ctx(manifest) BootstrapService(ctx).run("linux-auto") assert captured["profile"] == "linux-work"