Coverage for tests / unit / cli_utils / command_chainer / test_execute.py: 100%
68 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Tests for CommandChainer execute_chain and _execute_single_command methods."""
3from __future__ import annotations
5from unittest.mock import patch
7import click
8import pytest
9from assertpy import assert_that
11from lintro.cli_utils.command_chainer import CommandChainer
14def test_execute_chain_all_success(mock_group: click.Group) -> None:
15 """Test execution of chain where all commands succeed.
17 Args:
18 mock_group: Mocked Click group fixture.
19 """
20 chainer = CommandChainer(mock_group)
21 ctx = click.Context(mock_group)
23 with patch.object(
24 chainer,
25 "_execute_single_command",
26 return_value=0,
27 ) as mock_exec:
28 result = chainer.execute_chain(ctx, [["fmt", "."], ["chk", "."]])
30 assert_that(result).is_equal_to(0)
31 assert_that(mock_exec.call_count).is_equal_to(2)
34def test_execute_chain_returns_max_exit_code(mock_group: click.Group) -> None:
35 """Test that execute_chain returns the maximum exit code.
37 Args:
38 mock_group: Mocked Click group fixture.
39 """
40 chainer = CommandChainer(mock_group)
41 ctx = click.Context(mock_group)
43 with patch.object(
44 chainer,
45 "_execute_single_command",
46 side_effect=[0, 2, 1],
47 ):
48 result = chainer.execute_chain(
49 ctx,
50 [["fmt", "."], ["chk", "."], ["tst"]],
51 )
53 assert_that(result).is_equal_to(2)
56def test_execute_chain_empty_groups(mock_group: click.Group) -> None:
57 """Test execution with empty command groups.
59 Args:
60 mock_group: Mocked Click group fixture.
61 """
62 chainer = CommandChainer(mock_group)
63 ctx = click.Context(mock_group)
65 result = chainer.execute_chain(ctx, [])
67 assert_that(result).is_equal_to(0)
70def test_execute_chain_skips_empty_groups(mock_group: click.Group) -> None:
71 """Test that empty groups within the list are skipped.
73 Args:
74 mock_group: Mocked Click group fixture.
75 """
76 chainer = CommandChainer(mock_group)
77 ctx = click.Context(mock_group)
79 with patch.object(
80 chainer,
81 "_execute_single_command",
82 return_value=0,
83 ) as mock_exec:
84 result = chainer.execute_chain(ctx, [["fmt"], [], ["chk"]])
86 assert_that(result).is_equal_to(0)
87 assert_that(mock_exec.call_count).is_equal_to(2)
90def test_execute_single_command_success(mock_group: click.Group) -> None:
91 """Test successful single command execution.
93 Args:
94 mock_group: Mocked Click group fixture.
95 """
96 chainer = CommandChainer(mock_group)
97 parent_ctx = click.Context(mock_group, info_name="lintro")
99 result = chainer._execute_single_command(parent_ctx, ["fmt", "."])
101 assert_that(result).is_equal_to(0)
104def test_execute_single_command_handles_system_exit(mock_group: click.Group) -> None:
105 """Test handling of SystemExit from command.
107 Args:
108 mock_group: Mocked Click group fixture.
109 """
110 chainer = CommandChainer(mock_group)
111 parent_ctx = click.Context(mock_group, info_name="lintro")
113 with patch.object(
114 mock_group,
115 "invoke",
116 side_effect=SystemExit(2),
117 ):
118 result = chainer._execute_single_command(parent_ctx, ["fmt"])
120 assert_that(result).is_equal_to(2)
123def test_execute_single_command_handles_system_exit_none(
124 mock_group: click.Group,
125) -> None:
126 """Test handling of SystemExit with None code.
128 Args:
129 mock_group: Mocked Click group fixture.
130 """
131 chainer = CommandChainer(mock_group)
132 parent_ctx = click.Context(mock_group, info_name="lintro")
134 with patch.object(
135 mock_group,
136 "invoke",
137 side_effect=SystemExit(None),
138 ):
139 result = chainer._execute_single_command(parent_ctx, ["fmt"])
141 assert_that(result).is_equal_to(0)
144def test_execute_single_command_reraises_keyboard_interrupt(
145 mock_group: click.Group,
146) -> None:
147 """Test that KeyboardInterrupt is re-raised.
149 Args:
150 mock_group: Mocked Click group fixture.
151 """
152 chainer = CommandChainer(mock_group)
153 parent_ctx = click.Context(mock_group, info_name="lintro")
155 with patch.object(
156 mock_group,
157 "invoke",
158 side_effect=KeyboardInterrupt(),
159 ):
160 with pytest.raises(KeyboardInterrupt):
161 chainer._execute_single_command(parent_ctx, ["fmt"])
164def test_execute_single_command_handles_exception(mock_group: click.Group) -> None:
165 """Test handling of generic exceptions.
167 Args:
168 mock_group: Mocked Click group fixture.
169 """
170 chainer = CommandChainer(mock_group)
171 parent_ctx = click.Context(mock_group, info_name="lintro")
173 with patch.object(
174 mock_group,
175 "make_context",
176 side_effect=RuntimeError("test error"),
177 ):
178 result = chainer._execute_single_command(parent_ctx, ["fmt"])
180 assert_that(result).is_equal_to(1)
183def test_execute_single_command_uses_exit_code_attribute(
184 mock_group: click.Group,
185) -> None:
186 """Test that exit_code attribute from exception is used.
188 Args:
189 mock_group: Mocked Click group fixture.
190 """
191 chainer = CommandChainer(mock_group)
192 parent_ctx = click.Context(mock_group, info_name="lintro")
194 class CustomError(Exception):
195 """Custom error with exit_code attribute."""
197 exit_code = 42
199 with patch.object(
200 mock_group,
201 "make_context",
202 side_effect=CustomError("custom error"),
203 ):
204 result = chainer._execute_single_command(parent_ctx, ["fmt"])
206 assert_that(result).is_equal_to(42)