Skip to content

Commit 298b071

Browse files
committed
Address PR comments
1 parent 99e401d commit 298b071

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

awscli/customizations/ecs/expressgateway/display_strategy.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class DisplayStrategy:
2727
Each strategy controls its own execution model, timing, and output format.
2828
"""
2929

30-
def execute(self, collector, start_time, timeout_minutes):
30+
def execute_monitoring(self, collector, start_time, timeout_minutes):
3131
"""Execute the monitoring loop.
3232
3333
Args:
@@ -47,10 +47,17 @@ class InteractiveDisplayStrategy(DisplayStrategy):
4747
"""
4848

4949
def __init__(self, display, use_color):
50+
"""Initialize the interactive display strategy.
51+
52+
Args:
53+
display: Display instance from prompt_toolkit_display module
54+
providing the interactive terminal interface
55+
use_color: Whether to use colored output
56+
"""
5057
self.display = display
5158
self.use_color = use_color
5259

53-
def execute(self, collector, start_time, timeout_minutes):
60+
def execute_monitoring(self, collector, start_time, timeout_minutes):
5461
"""Execute async monitoring with spinner and keyboard controls."""
5562
final_output, timed_out = asyncio.run(
5663
self._execute_async(collector, start_time, timeout_minutes)
@@ -143,6 +150,8 @@ async def update_spinner():
143150
pass
144151

145152
finally:
153+
# Ensure display app is properly shut down
154+
self.display.app.exit()
146155
spinner_task.cancel()
147156
if display_task is not None and not display_task.done():
148157
display_task.cancel()

tests/unit/customizations/ecs/expressgateway/test_display_strategy.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_base_strategy_not_implemented(self):
2828
"""Test base class raises NotImplementedError."""
2929
strategy = DisplayStrategy()
3030
with pytest.raises(NotImplementedError):
31-
strategy.execute(None, None, None)
31+
strategy.execute_monitoring(None, None, None)
3232

3333

3434
@pytest.fixture
@@ -71,7 +71,9 @@ def test_execute_with_mock_display(
7171
mock_sleep.side_effect = KeyboardInterrupt()
7272

7373
start_time = time.time()
74-
strategy.execute(mock_collector, start_time, timeout_minutes=1)
74+
strategy.execute_monitoring(
75+
mock_collector, start_time, timeout_minutes=1
76+
)
7577

7678
# Verify display was called
7779
assert mock_display.display.called
@@ -106,7 +108,9 @@ def test_completion_message_on_normal_exit(
106108
mock_sleep.side_effect = KeyboardInterrupt()
107109

108110
start_time = time.time()
109-
strategy.execute(mock_collector, start_time, timeout_minutes=1)
111+
strategy.execute_monitoring(
112+
mock_collector, start_time, timeout_minutes=1
113+
)
110114

111115
captured = capsys.readouterr()
112116
assert "Monitoring Complete!" in captured.out
@@ -128,7 +132,9 @@ def test_collector_output_is_displayed(
128132
mock_sleep.side_effect = KeyboardInterrupt()
129133

130134
start_time = time.time()
131-
strategy.execute(mock_collector, start_time, timeout_minutes=1)
135+
strategy.execute_monitoring(
136+
mock_collector, start_time, timeout_minutes=1
137+
)
132138

133139
captured = capsys.readouterr()
134140
assert unique_output in captured.out
@@ -157,7 +163,9 @@ def test_execute_handles_service_inactive(
157163
mock_sleep.side_effect = KeyboardInterrupt()
158164

159165
start_time = time.time()
160-
strategy.execute(mock_collector, start_time, timeout_minutes=1)
166+
strategy.execute_monitoring(
167+
mock_collector, start_time, timeout_minutes=1
168+
)
161169

162170
# Strategy should handle the error and set output to "Service is inactive"
163171
captured = capsys.readouterr()
@@ -190,8 +198,35 @@ def test_execute_other_client_errors_propagate(
190198

191199
# Other client errors should propagate
192200
with pytest.raises(ClientError) as exc_info:
193-
strategy.execute(mock_collector, start_time, timeout_minutes=1)
201+
strategy.execute_monitoring(
202+
mock_collector, start_time, timeout_minutes=1
203+
)
194204

195205
assert (
196206
exc_info.value.response['Error']['Code'] == 'AccessDeniedException'
197207
)
208+
209+
@patch('time.sleep')
210+
def test_display_cleanup_on_exception(
211+
self, mock_sleep, app_session, mock_display
212+
):
213+
"""Test display app is properly shut down when exception occurs."""
214+
mock_collector = Mock()
215+
error = ClientError(
216+
error_response={'Error': {'Code': 'ThrottlingException'}},
217+
operation_name='DescribeServiceRevisions',
218+
)
219+
mock_collector.get_current_view = Mock(side_effect=error)
220+
221+
strategy = InteractiveDisplayStrategy(
222+
display=mock_display, use_color=True
223+
)
224+
mock_sleep.side_effect = KeyboardInterrupt()
225+
226+
with pytest.raises(ClientError):
227+
strategy.execute_monitoring(
228+
mock_collector, time.time(), timeout_minutes=1
229+
)
230+
231+
# Verify app.exit() was called in finally block despite exception
232+
mock_display.app.exit.assert_called()

0 commit comments

Comments
 (0)