@@ -995,41 +995,6 @@ def test_passes_session_and_region_to_get_iceberg_properties(self, mock_get_clie
995995 mock_get_iceberg .assert_called_once_with (session = mock_session , region = "us-east-1" )
996996
997997
998- class TestHasLakeFormationConfig :
999- """Tests for _has_lake_formation_config method."""
1000-
1001- def test_returns_false_when_attribute_missing (self ):
1002- """Test returns False when lake_formation_config attribute does not exist."""
1003- fg = MagicMock (spec = FeatureGroupManager )
1004- fg ._has_lake_formation_config = FeatureGroupManager ._has_lake_formation_config .__get__ (fg )
1005- # MagicMock(spec=...) won't have lake_formation_config since it's not on the class
1006- del fg .lake_formation_config
1007- assert fg ._has_lake_formation_config () is False
1008-
1009- def test_returns_false_when_none (self ):
1010- """Test returns False when lake_formation_config is None."""
1011- fg = MagicMock (spec = FeatureGroupManager )
1012- fg ._has_lake_formation_config = FeatureGroupManager ._has_lake_formation_config .__get__ (fg )
1013- fg .lake_formation_config = None
1014- assert fg ._has_lake_formation_config () is False
1015-
1016- def test_returns_false_when_unassigned (self ):
1017- """Test returns False when lake_formation_config is Unassigned()."""
1018- from sagemaker .core .shapes import Unassigned
1019-
1020- fg = MagicMock (spec = FeatureGroupManager )
1021- fg ._has_lake_formation_config = FeatureGroupManager ._has_lake_formation_config .__get__ (fg )
1022- fg .lake_formation_config = Unassigned ()
1023- assert fg ._has_lake_formation_config () is False
1024-
1025- def test_returns_true_when_config_present (self ):
1026- """Test returns True when lake_formation_config has a real value."""
1027- fg = MagicMock (spec = FeatureGroupManager )
1028- fg ._has_lake_formation_config = FeatureGroupManager ._has_lake_formation_config .__get__ (fg )
1029- fg .lake_formation_config = MagicMock ()
1030- assert fg ._has_lake_formation_config () is True
1031-
1032-
1033998class TestGetIcebergPropertiesAccessDenied :
1034999 """Tests for AccessDeniedException handling in _get_iceberg_properties."""
10351000
@@ -1052,26 +1017,16 @@ def _make_client_error(self, code):
10521017 return ClientError ({"Error" : {"Code" : code , "Message" : "denied" }}, "GetTable" )
10531018
10541019 @patch ("sagemaker.mlops.feature_store.feature_group_manager.load_catalog" )
1055- def test_access_denied_with_lake_formation_raises_permission_error (self , mock_load_catalog ):
1056- """Test PermissionError with LF message when AccessDenied and LF config present."""
1057- mock_catalog = MagicMock ()
1058- mock_catalog .load_table .side_effect = self ._make_client_error ("AccessDeniedException" )
1059- mock_load_catalog .return_value = mock_catalog
1060- self .fg ._has_lake_formation_config .return_value = True
1061-
1062- with pytest .raises (PermissionError , match = "Lake Formation governance" ):
1063- self .fg ._get_iceberg_properties (session = MagicMock ())
1064-
1065- @patch ("sagemaker.mlops.feature_store.feature_group_manager.load_catalog" )
1066- def test_access_denied_without_lake_formation_raises_permission_error (self , mock_load_catalog ):
1067- """Test PermissionError with IAM message when AccessDenied and no LF config."""
1020+ def test_access_denied_raises_permission_error_with_combined_message (self , mock_load_catalog ):
1021+ """Test PermissionError with combined LF/IAM message on AccessDenied."""
10681022 mock_catalog = MagicMock ()
10691023 mock_catalog .load_table .side_effect = self ._make_client_error ("AccessDeniedException" )
10701024 mock_load_catalog .return_value = mock_catalog
1071- self .fg ._has_lake_formation_config .return_value = False
10721025
1073- with pytest .raises (PermissionError , match = "glue:GetTable" ) :
1026+ with pytest .raises (PermissionError , match = "Lake Formation governance" ) as exc_info :
10741027 self .fg ._get_iceberg_properties (session = MagicMock ())
1028+ assert "glue:GetTable" in str (exc_info .value )
1029+ assert "glue:UpdateTable" in str (exc_info .value )
10751030
10761031 @patch ("sagemaker.mlops.feature_store.feature_group_manager.load_catalog" )
10771032 def test_non_access_denied_client_error_raises_runtime_error (self , mock_load_catalog ):
@@ -1090,7 +1045,6 @@ def test_access_denied_permission_error_chains_original(self, mock_load_catalog)
10901045 mock_catalog = MagicMock ()
10911046 mock_catalog .load_table .side_effect = original
10921047 mock_load_catalog .return_value = mock_catalog
1093- self .fg ._has_lake_formation_config .return_value = False
10941048
10951049 with pytest .raises (PermissionError ) as exc_info :
10961050 self .fg ._get_iceberg_properties (session = MagicMock ())
@@ -1121,31 +1075,22 @@ def _setup_get_result(self):
11211075 }
11221076 return mock_table
11231077
1124- def test_access_denied_with_lake_formation_raises_permission_error (self ):
1125- """Test PermissionError with LF message when AccessDenied and LF config present ."""
1078+ def test_access_denied_raises_permission_error_with_combined_message (self ):
1079+ """Test PermissionError with combined LF/IAM message on AccessDenied."""
11261080 mock_table = self ._setup_get_result ()
11271081 mock_table .transaction ().__enter__ ().set_properties .side_effect = self ._make_client_error ("AccessDeniedException" )
1128- self .fg ._has_lake_formation_config .return_value = True
11291082
11301083 props = IcebergProperties (properties = {"write.target-file-size-bytes" : "536870912" })
1131- with pytest .raises (PermissionError , match = "Lake Formation governance" ):
1132- self .fg ._update_iceberg_properties (iceberg_properties = props )
1133-
1134- def test_access_denied_without_lake_formation_raises_permission_error (self ):
1135- """Test PermissionError with IAM message when AccessDenied and no LF config."""
1136- mock_table = self ._setup_get_result ()
1137- mock_table .transaction ().__enter__ ().set_properties .side_effect = self ._make_client_error ("AccessDeniedException" )
1138- self .fg ._has_lake_formation_config .return_value = False
1139-
1140- props = IcebergProperties (properties = {"write.target-file-size-bytes" : "536870912" })
1141- with pytest .raises (PermissionError , match = "glue:UpdateTable" ):
1084+ with pytest .raises (PermissionError , match = "Lake Formation governance" ) as exc_info :
11421085 self .fg ._update_iceberg_properties (iceberg_properties = props )
1086+ assert "glue:GetTable" in str (exc_info .value )
1087+ assert "glue:UpdateTable" in str (exc_info .value )
1088+ assert "ALTER" in str (exc_info .value )
11431089
11441090 def test_non_access_denied_client_error_raises_runtime_error (self ):
11451091 """Test RuntimeError for non-AccessDenied ClientError."""
11461092 mock_table = self ._setup_get_result ()
11471093 mock_table .transaction ().__enter__ ().set_properties .side_effect = self ._make_client_error ("InternalServiceException" )
1148- self .fg ._has_lake_formation_config .return_value = False
11491094
11501095 props = IcebergProperties (properties = {"write.target-file-size-bytes" : "536870912" })
11511096 with pytest .raises (RuntimeError , match = "Failed to update Iceberg properties" ):
@@ -1156,19 +1101,8 @@ def test_access_denied_permission_error_chains_original(self):
11561101 mock_table = self ._setup_get_result ()
11571102 original = self ._make_client_error ("AccessDeniedException" )
11581103 mock_table .transaction ().__enter__ ().set_properties .side_effect = original
1159- self .fg ._has_lake_formation_config .return_value = True
11601104
11611105 props = IcebergProperties (properties = {"write.target-file-size-bytes" : "536870912" })
11621106 with pytest .raises (PermissionError ) as exc_info :
11631107 self .fg ._update_iceberg_properties (iceberg_properties = props )
11641108 assert exc_info .value .__cause__ is original
1165-
1166- def test_access_denied_lf_message_mentions_alter (self ):
1167- """Test that LF error message mentions ALTER permission."""
1168- mock_table = self ._setup_get_result ()
1169- mock_table .transaction ().__enter__ ().set_properties .side_effect = self ._make_client_error ("AccessDeniedException" )
1170- self .fg ._has_lake_formation_config .return_value = True
1171-
1172- props = IcebergProperties (properties = {"write.target-file-size-bytes" : "536870912" })
1173- with pytest .raises (PermissionError , match = "ALTER permission" ):
1174- self .fg ._update_iceberg_properties (iceberg_properties = props )
0 commit comments