Merge branch 'kurtmckee/prevent-source-deletio...'
[dotbot.git] / tests / test_cli.py
blob9bd478100dc031f234b0d7ff298751b937cb43de
1 import os
2 import shutil
3 from typing import Callable
5 import pytest
7 from tests.conftest import Dotfiles
10 def test_except_create(
11 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
12 ) -> None:
13 """Verify that `--except` works as intended."""
15 dotfiles.write_config(
17 {"create": ["~/a"]},
19 "shell": [
20 {"command": "echo success", "stdout": True},
25 run_dotbot("--except", "create")
27 assert not os.path.exists(os.path.join(home, "a"))
28 stdout = capfd.readouterr().out.splitlines()
29 assert any(line.startswith("success") for line in stdout)
32 def test_except_shell(
33 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
34 ) -> None:
35 """Verify that `--except` works as intended."""
37 dotfiles.write_config(
39 {"create": ["~/a"]},
41 "shell": [
42 {"command": "echo failure", "stdout": True},
47 run_dotbot("--except", "shell")
49 assert os.path.exists(os.path.join(home, "a"))
50 stdout = capfd.readouterr().out.splitlines()
51 assert not any(line.startswith("failure") for line in stdout)
54 def test_except_multiples(
55 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
56 ) -> None:
57 """Verify that `--except` works with multiple exceptions."""
59 dotfiles.write_config(
61 {"create": ["~/a"]},
63 "shell": [
64 {"command": "echo failure", "stdout": True},
69 run_dotbot("--except", "create", "shell")
71 assert not os.path.exists(os.path.join(home, "a"))
72 stdout = capfd.readouterr().out.splitlines()
73 assert not any(line.startswith("failure") for line in stdout)
76 def test_exit_on_failure(home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]) -> None:
77 """Verify that processing can halt immediately on failures."""
79 dotfiles.write_config(
81 {"create": ["~/a"]},
82 {"shell": ["this_is_not_a_command"]},
83 {"create": ["~/b"]},
86 with pytest.raises(SystemExit):
87 run_dotbot("-x")
89 assert os.path.isdir(os.path.join(home, "a"))
90 assert not os.path.isdir(os.path.join(home, "b"))
93 def test_only(
94 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
95 ) -> None:
96 """Verify that `--only` works as intended."""
98 dotfiles.write_config(
100 {"create": ["~/a"]},
101 {"shell": [{"command": "echo success", "stdout": True}]},
104 run_dotbot("--only", "shell")
106 assert not os.path.exists(os.path.join(home, "a"))
107 stdout = capfd.readouterr().out.splitlines()
108 assert any(line.startswith("success") for line in stdout)
111 def test_only_with_defaults(
112 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
113 ) -> None:
114 """Verify that `--only` does not suppress defaults."""
116 dotfiles.write_config(
118 {"defaults": {"shell": {"stdout": True}}},
119 {"create": ["~/a"]},
120 {"shell": [{"command": "echo success"}]},
123 run_dotbot("--only", "shell")
125 assert not os.path.exists(os.path.join(home, "a"))
126 stdout = capfd.readouterr().out.splitlines()
127 assert any(line.startswith("success") for line in stdout)
130 def test_only_with_multiples(
131 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
132 ) -> None:
133 """Verify that `--only` works as intended."""
135 dotfiles.write_config(
137 {"create": ["~/a"]},
138 {"shell": [{"command": "echo success", "stdout": True}]},
139 {"link": ["~/.f"]},
142 run_dotbot("--only", "create", "shell")
144 assert os.path.isdir(os.path.join(home, "a"))
145 stdout = capfd.readouterr().out.splitlines()
146 assert any(line.startswith("success") for line in stdout)
147 assert not os.path.exists(os.path.join(home, ".f"))
150 def test_plugin_loading_file(home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]) -> None:
151 """Verify that plugins can be loaded by file."""
153 plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_file.py")
154 shutil.copy(plugin_file, os.path.join(dotfiles.directory, "file.py"))
155 dotfiles.write_config([{"plugin_file": "~"}])
156 run_dotbot("--plugin", os.path.join(dotfiles.directory, "file.py"))
158 with open(os.path.join(home, "flag")) as file:
159 assert file.read() == "file plugin loading works"
162 def test_plugin_loading_directory(home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]) -> None:
163 """Verify that plugins can be loaded from a directory."""
165 dotfiles.makedirs("plugins")
166 plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_directory.py")
167 shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugins", "directory.py"))
168 dotfiles.write_config([{"plugin_directory": "~"}])
169 run_dotbot("--plugin-dir", os.path.join(dotfiles.directory, "plugins"))
171 with open(os.path.join(home, "flag")) as file:
172 assert file.read() == "directory plugin loading works"
175 def test_issue_357(
176 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
177 ) -> None:
178 """Verify that built-in plugins are only executed once, when
179 using a plugin that imports from dotbot.plugins."""
181 _ = home
182 plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_issue_357.py")
183 dotfiles.write_config([{"shell": [{"command": "echo apple", "stdout": True}]}])
185 run_dotbot("--plugin", plugin_file)
187 assert len([line for line in capfd.readouterr().out.splitlines() if line.strip() == "apple"]) == 1
190 def test_disable_builtin_plugins(home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]) -> None:
191 """Verify that builtin plugins can be disabled."""
193 dotfiles.write("f", "apple")
194 dotfiles.write_config([{"link": {"~/.f": "f"}}])
196 # The link directive will be unhandled so dotbot will raise SystemExit.
197 with pytest.raises(SystemExit):
198 run_dotbot("--disable-built-in-plugins")
200 assert not os.path.exists(os.path.join(home, ".f"))
203 def test_plugin_context_plugin(
204 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
205 ) -> None:
206 """Verify that the plugin context is available to plugins."""
208 _ = home
209 plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_context_plugin.py")
210 shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugin.py"))
211 dotfiles.write_config([{"dispatch": [{"shell": [{"command": "echo apple", "stdout": True}]}]}])
212 run_dotbot("--plugin", os.path.join(dotfiles.directory, "plugin.py"))
214 stdout = capfd.readouterr().out.splitlines()
215 assert any(line.startswith("apple") for line in stdout)
218 def test_plugin_dispatcher_no_plugins(
219 capfd: pytest.CaptureFixture[str], home: str, dotfiles: Dotfiles, run_dotbot: Callable[..., None]
220 ) -> None:
221 """Verify that plugins instantiating Dispatcher without plugins work."""
223 _ = home
224 plugin_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "dotbot_plugin_dispatcher_no_plugins.py")
225 shutil.copy(plugin_file, os.path.join(dotfiles.directory, "plugin.py"))
226 dotfiles.write_config([{"dispatch": [{"shell": [{"command": "echo apple", "stdout": True}]}]}])
227 run_dotbot("--plugin", os.path.join(dotfiles.directory, "plugin.py"))
229 stdout = capfd.readouterr().out.splitlines()
230 assert any(line.startswith("apple") for line in stdout)