cgcardona / muse public
test_cli_skeleton.py python
98 lines 2.9 KB
12901c5a Initial extraction from tellurstori/maestro cgcardona <gabriel@tellurstori.com> 4d ago
1 """Tests for the Muse CLI skeleton — subcommand stubs and exit-code contract."""
2 from __future__ import annotations
3
4 import os
5 import pathlib
6 import tempfile
7
8 import pytest
9 from typer.testing import CliRunner
10
11 from maestro.muse_cli.app import cli
12 from maestro.muse_cli.errors import ExitCode
13
14 runner = CliRunner()
15
16 ALL_SUBCOMMANDS = [
17 "init",
18 "status",
19 "commit",
20 "log",
21 "checkout",
22 "merge",
23 "remote",
24 "push",
25 "pull",
26 ]
27
28 # Commands that are not yet fully implemented — they print "not yet implemented"
29 # when invoked inside a repo with a bare .muse/ directory.
30 # ``init`` is excluded: fully implemented (issue #31).
31 # ``commit`` is excluded: fully implemented (issue #32).
32 # ``log`` is excluded: fully implemented (issue #33).
33 # ``checkout`` is excluded: fully implemented (issue #34).
34 STUB_COMMANDS = [
35 "merge",
36 "remote",
37 "push",
38 "pull",
39 ]
40
41 # Repo-dependent commands that exit 2 outside a .muse/ repo.
42 # ``commit`` requires -m so its no-repo exit-2 test lives in test_commit.py.
43 # ``log`` no-repo exit-2 test lives in test_log.py.
44 # ``checkout`` requires a branch arg so its no-repo exit-2 test lives in test_checkout.py.
45 REPO_DEPENDENT_COMMANDS = [
46 "status",
47 "merge",
48 "remote",
49 "push",
50 "pull",
51 ]
52
53
54 def test_cli_help_exits_zero() -> None:
55 """``muse --help`` exits 0 and lists all subcommand names."""
56 result = runner.invoke(cli, ["--help"])
57 assert result.exit_code == 0
58 for cmd in ALL_SUBCOMMANDS:
59 assert cmd in result.output
60
61
62 @pytest.mark.parametrize("cmd", STUB_COMMANDS)
63 def test_cli_subcommand_stub_exits_zero(cmd: str, tmp_path: pathlib.Path) -> None:
64 """Each not-yet-implemented stub exits 0 when run inside a Muse repository."""
65 muse_dir = tmp_path / ".muse"
66 muse_dir.mkdir()
67 prev = os.getcwd()
68 try:
69 os.chdir(tmp_path)
70 result = runner.invoke(cli, [cmd])
71 assert result.exit_code == 0, f"{cmd} failed: {result.output}"
72 assert "not yet implemented" in result.output
73 finally:
74 os.chdir(prev)
75
76
77 @pytest.mark.parametrize("cmd", REPO_DEPENDENT_COMMANDS)
78 def test_cli_no_repo_exits_2(cmd: str) -> None:
79 """Repo-dependent commands exit 2 when no ``.muse/`` directory exists."""
80 with tempfile.TemporaryDirectory() as d:
81 prev = os.getcwd()
82 try:
83 os.chdir(d)
84 result = runner.invoke(cli, [cmd])
85 assert result.exit_code == int(ExitCode.REPO_NOT_FOUND), (
86 f"{cmd} should exit {ExitCode.REPO_NOT_FOUND}, got {result.exit_code}: {result.output}"
87 )
88 assert "Not a Muse repository" in result.output
89 finally:
90 os.chdir(prev)
91
92
93 def test_exit_code_enum_values() -> None:
94 """Exit code enum values match the specification (0/1/2/3)."""
95 assert int(ExitCode.SUCCESS) == 0
96 assert int(ExitCode.USER_ERROR) == 1
97 assert int(ExitCode.REPO_NOT_FOUND) == 2
98 assert int(ExitCode.INTERNAL_ERROR) == 3