11"""
22Integration tests for CLI cluster stack deletion functionality.
33
4- Tests the complete user workflow for deleting cluster stacks via CLI commands.
5- Uses CLI commands as a user would, focusing on deletion process and error handling.
4+ Tests the basic happy path user workflow for deleting cluster stacks via CLI commands.
5+ Focuses on core functionality with minimal stack creation/deletion overhead.
6+
7+ Detailed error handling and edge cases are covered by unit tests.
68"""
79import time
810import pytest
911import boto3
10- from unittest .mock import patch , MagicMock
1112from click .testing import CliRunner
1213
1314from sagemaker .hyperpod .cli .commands .cluster_stack import delete_cluster_stack
1415from test .integration_tests .cluster_management .utils import (
1516 assert_command_succeeded ,
16- assert_command_failed_with_helpful_error ,
1717 assert_yes_no_prompt_displayed ,
1818 assert_success_message_displayed ,
1919)
@@ -30,13 +30,6 @@ def runner():
3030 return CliRunner ()
3131
3232
33- @pytest .fixture (scope = "module" )
34- def test_stack_name ():
35- """Generate a unique test stack name."""
36- import uuid
37- return f"{ TEST_STACK_PREFIX } -{ str (uuid .uuid4 ())[:8 ]} "
38-
39-
4033@pytest .fixture (scope = "module" )
4134def cfn_client ():
4235 """CloudFormation client for test infrastructure."""
@@ -96,69 +89,11 @@ def wait_for_stack_delete_complete(cfn_client, stack_name, timeout_minutes=10):
9689
9790# --------- CLI Delete Tests ---------
9891
99- @pytest .mark .dependency (name = "test_delete_nonexistent_stack" )
100- def test_delete_nonexistent_stack_error_handling (runner ):
101- """Test CLI error handling when trying to delete a non-existent stack."""
102- nonexistent_stack = "nonexistent-stack-12345"
103-
104- result = runner .invoke (delete_cluster_stack , [
105- nonexistent_stack ,
106- "--region" , REGION
107- ], input = 'y\n ' , catch_exceptions = False )
108-
109- # CLI shows user-friendly error message but doesn't fail with non-zero exit code
110- assert "not found" in result .output .lower ()
111- assert nonexistent_stack in result .output
112-
113-
114- @pytest .mark .dependency (name = "test_delete_with_confirmation_prompt" )
115- def test_delete_with_confirmation_prompt (runner , test_stack_name , cfn_client ):
116- """Test CLI deletion with user confirmation prompt."""
117- # Create test stack
118- create_test_stack (cfn_client , test_stack_name )
119-
120- # Test deletion with confirmation prompt (simulate 'n' response)
121- result = runner .invoke (delete_cluster_stack , [
122- test_stack_name ,
123- "--region" , REGION
124- ], input = 'n\n ' , catch_exceptions = False )
125-
126- # Should show confirmation prompt and abort on 'n'
127- assert_yes_no_prompt_displayed (result )
128- assert "Aborted" in result .output or "cancelled" in result .output .lower ()
129-
130- # Verify stack still exists
131- response = cfn_client .describe_stacks (StackName = test_stack_name )
132- assert len (response ['Stacks' ]) == 1
133- assert response ['Stacks' ][0 ]['StackStatus' ] != 'DELETE_COMPLETE'
134-
135-
136- @pytest .mark .dependency (name = "test_delete_with_user_confirmation_yes" , depends = ["test_delete_with_confirmation_prompt" ])
137- def test_delete_with_user_confirmation_yes (runner , test_stack_name , cfn_client ):
138- """Test CLI deletion with user confirmation (yes response)."""
139- result = runner .invoke (delete_cluster_stack , [
140- test_stack_name ,
141- "--region" , REGION
142- ], input = 'y\n ' , catch_exceptions = False )
143-
144- assert_command_succeeded (result )
145- assert_success_message_displayed (result , ["deletion" , "initiated" ])
146-
147- # Wait for deletion to complete
148- wait_for_stack_delete_complete (cfn_client , test_stack_name )
149-
150- # Verify stack is deleted
151- with pytest .raises (Exception ) as exc_info :
152- cfn_client .describe_stacks (StackName = test_stack_name )
153- assert "does not exist" in str (exc_info .value )
154-
155-
156- @pytest .mark .dependency (name = "test_delete_with_user_confirmation" )
15792def test_delete_with_user_confirmation (runner , cfn_client ):
158- """Test CLI deletion with user confirmation (yes response) ."""
159- # Create a new test stack for this test
93+ """Test CLI deletion happy path with user confirmation."""
94+ # Create a test stack for this test
16095 import uuid
161- stack_name = f"{ TEST_STACK_PREFIX } -confirm -{ str (uuid .uuid4 ())[:8 ]} "
96+ stack_name = f"{ TEST_STACK_PREFIX } -happy -{ str (uuid .uuid4 ())[:8 ]} "
16297 create_test_stack (cfn_client , stack_name )
16398
16499 try :
@@ -187,54 +122,3 @@ def test_delete_with_user_confirmation(runner, cfn_client):
187122 except :
188123 pass
189124 raise
190-
191-
192- @pytest .mark .dependency (name = "test_retain_resources_validation" )
193- def test_retain_resources_validation_error (runner , cfn_client ):
194- """Test CLI validation of retain-resources parameter on non-DELETE_FAILED stack."""
195- # Create a test stack in CREATE_COMPLETE state
196- import uuid
197- stack_name = f"{ TEST_STACK_PREFIX } -retain-{ str (uuid .uuid4 ())[:8 ]} "
198- create_test_stack (cfn_client , stack_name )
199-
200- try :
201- # Try to delete with retain-resources (should fail validation)
202- result = runner .invoke (delete_cluster_stack , [
203- stack_name ,
204- "--region" , REGION ,
205- "--retain-resources" , "TestBucket"
206- ], input = 'y\n ' , catch_exceptions = False )
207-
208- # Should fail with helpful error about retain-resources limitation
209- assert result .exit_code != 0
210- assert "retain" in result .output .lower ()
211- assert "only works on failed deletions" in result .output or "DELETE_FAILED" in result .output
212-
213- # Verify stack still exists (deletion should not have been attempted)
214- response = cfn_client .describe_stacks (StackName = stack_name )
215- assert len (response ['Stacks' ]) == 1
216- assert response ['Stacks' ][0 ]['StackStatus' ] == 'CREATE_COMPLETE'
217-
218- finally :
219- # Cleanup
220- try :
221- cfn_client .delete_stack (StackName = stack_name )
222- wait_for_stack_delete_complete (cfn_client , stack_name )
223- except :
224- pass
225-
226-
227- @pytest .mark .dependency (name = "test_region_parameter" )
228- def test_region_parameter_handling (runner ):
229- """Test CLI handling of region parameter."""
230- nonexistent_stack = "test-stack-region-param"
231-
232- # Test with explicit region
233- result = runner .invoke (delete_cluster_stack , [
234- nonexistent_stack ,
235- "--region" , "us-west-1"
236- ], input = 'y\n ' , catch_exceptions = False )
237-
238- # CLI shows user-friendly error message for non-existent stack
239- assert "not found" in result .output .lower () or "does not exist" in result .output .lower ()
240- assert nonexistent_stack in result .output
0 commit comments