"""Tests for muse/core/hooks.py::run_hook_point — musehub#192 Phase 3 core layer. Covers command execution semantics only. CLI-level `muse commit` integration (--no-verify, warnings, exit codes) is covered in tests/test_cmd_commit_hooks.py. """ import pathlib from muse.core.hooks import install_hooks, run_hook_point def _write_hooks(tmp_path: pathlib.Path, content: str) -> None: (tmp_path / ".musehooks.toml").write_text(content, encoding="utf-8") class TestRunHookPointNotInstalled: def test_not_installed_does_not_execute_anything(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["touch should-not-exist.txt"]\n', ) result = run_hook_point(tmp_path, "pre-commit") assert result.executed is False assert result.passed is True assert not (tmp_path / "should-not-exist.txt").exists() def test_no_hooks_defined_at_all_is_a_pass(self, tmp_path: pathlib.Path) -> None: install_hooks(tmp_path) # installed, but nothing declared result = run_hook_point(tmp_path, "pre-commit") assert result.executed is False assert result.passed is True class TestRunHookPointInstalled: def test_passing_command_executes_and_passes(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["touch ran.txt"]\n', ) install_hooks(tmp_path) result = run_hook_point(tmp_path, "pre-commit") assert result.executed is True assert result.passed is True assert (tmp_path / "ran.txt").exists() def test_failing_command_stops_and_reports_it(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["exit 1"]\n', ) install_hooks(tmp_path) result = run_hook_point(tmp_path, "pre-commit") assert result.executed is True assert result.passed is False assert result.failed_command == "exit 1" def test_first_failure_stops_subsequent_commands(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["exit 1", "touch should-not-run.txt"]\n', ) install_hooks(tmp_path) result = run_hook_point(tmp_path, "pre-commit") assert result.passed is False assert not (tmp_path / "should-not-run.txt").exists() def test_multiple_passing_commands_all_run_in_order(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["touch first.txt", "touch second.txt"]\n', ) install_hooks(tmp_path) result = run_hook_point(tmp_path, "pre-commit") assert result.passed is True assert (tmp_path / "first.txt").exists() assert (tmp_path / "second.txt").exists() def test_stderr_captured_on_failure(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["echo boom-message 1>&2; exit 1"]\n', ) install_hooks(tmp_path) result = run_hook_point(tmp_path, "pre-commit") assert result.passed is False assert "boom-message" in result.output def test_runs_relative_to_repo_root(self, tmp_path: pathlib.Path) -> None: _write_hooks( tmp_path, '[pre-commit]\ncommands = ["touch here.txt"]\n', ) install_hooks(tmp_path) # Run from a subdirectory-equivalent scenario by passing tmp_path as # root explicitly — command must execute with cwd=root regardless of # the caller's actual process cwd. run_hook_point(tmp_path, "pre-commit") assert (tmp_path / "here.txt").exists()