@@ -565,7 +565,7 @@ def test_anon_drop_requires_confirmation_for_dangerous_ops():
565565
566566@pytest .mark .django_db  
567567@patch ("builtins.input" , return_value = "yes" ) 
568- def  test_anon_drop_interactive_confirmation_accepted (mock_input ):
568+ def  test_anon_drop_interactive_confirmation_accepted (_mock_input ):
569569    """Test drop command with interactive confirmation accepted""" 
570570    baker .make (MaskingRule , table_name = "auth_user" , column_name = "email" )
571571
@@ -789,5 +789,135 @@ def test_anon_fix_permissions_command():
789789            output  =  str (mock_stdout .write .call_args_list )
790790            assert  "Failed"  in  output  or  "failed"  in  output .lower ()
791791
792+     # Test with database error in permission fix 
793+     from  django .db  import  DatabaseError 
794+ 
795+     with  patch ("django_postgres_anon.management.commands.anon_fix_permissions.create_masked_role" ) as  mock_create :
796+         mock_create .side_effect  =  DatabaseError ("permission denied" )
797+         with  patch ("sys.stdout" , new_callable = MagicMock ) as  mock_stdout :
798+             call_command ("anon_fix_permissions" , "--role" , "error_role" )
799+             output  =  str (mock_stdout .write .call_args_list )
800+             assert  "Error fixing permissions"  in  output  or  "permission denied"  in  output 
801+ 
792802    # Clean up 
793803    MaskedRole .objects .all ().delete ()
804+ 
805+ 
806+ @pytest .mark .django_db  
807+ def  test_create_masked_role_permission_failures ():
808+     """Test permission failure scenarios in create_masked_role to improve coverage""" 
809+     from  unittest .mock  import  MagicMock , patch 
810+ 
811+     from  django .db  import  DatabaseError , OperationalError 
812+ 
813+     from  django_postgres_anon .utils  import  create_masked_role 
814+ 
815+     # Test write permission failure 
816+     with  patch ("django.db.connection.cursor" ) as  mock_cursor_ctx :
817+         mock_cursor  =  MagicMock ()
818+         mock_cursor_ctx .return_value .__enter__ .return_value  =  mock_cursor 
819+ 
820+         # Mock table exists check to return True, but write permissions fail 
821+         def  mock_execute_side_effect (sql , params = None ):
822+             if  "INSERT, UPDATE ON TABLE"  in  sql :
823+                 raise  DatabaseError ("permission denied for table test_table" )
824+             elif  "SELECT table_name FROM information_schema.tables"  in  sql :
825+                 return  None   # Query for tables 
826+             # Let other queries pass through (CREATE ROLE, etc.) 
827+ 
828+         mock_cursor .execute .side_effect  =  mock_execute_side_effect 
829+         mock_cursor .fetchall .return_value  =  [("auth_user" ,), ("django_content_type" ,)]  # Mock tables 
830+ 
831+         result  =  create_masked_role ("test_role" )
832+         assert  result  is  True   # Should still succeed despite write permission failure 
833+ 
834+     # Test table permission failure 
835+     with  patch ("django.db.connection.cursor" ) as  mock_cursor_ctx :
836+         mock_cursor  =  MagicMock ()
837+         mock_cursor_ctx .return_value .__enter__ .return_value  =  mock_cursor 
838+ 
839+         def  mock_execute_side_effect (sql , params = None ):
840+             if  "GRANT SELECT ON"  in  sql  and  "TO"  in  sql :
841+                 raise  OperationalError ("permission denied for table test_table" )
842+ 
843+         mock_cursor .execute .side_effect  =  mock_execute_side_effect 
844+         mock_cursor .fetchall .return_value  =  [("auth_user" ,), ("django_content_type" ,)]
845+ 
846+         result  =  create_masked_role ("test_role" )
847+         assert  result  is  True   # Should still succeed despite table permission failure 
848+ 
849+     # Test database CONNECT permission failure 
850+     with  patch ("django.db.connection.cursor" ) as  mock_cursor_ctx :
851+         mock_cursor  =  MagicMock ()
852+         mock_cursor_ctx .return_value .__enter__ .return_value  =  mock_cursor 
853+ 
854+         def  mock_execute_side_effect (sql , params = None ):
855+             if  "GRANT CONNECT ON DATABASE"  in  sql :
856+                 raise  DatabaseError ("permission denied for database" )
857+ 
858+         mock_cursor .execute .side_effect  =  mock_execute_side_effect 
859+         mock_cursor .fetchall .return_value  =  []  # No tables 
860+ 
861+         result  =  create_masked_role ("test_role" )
862+         assert  result  is  True   # Should still succeed despite CONNECT failure 
863+ 
864+     # Test schema USAGE permission failure 
865+     with  patch ("django.db.connection.cursor" ) as  mock_cursor_ctx :
866+         mock_cursor  =  MagicMock ()
867+         mock_cursor_ctx .return_value .__enter__ .return_value  =  mock_cursor 
868+ 
869+         def  mock_execute_side_effect (sql , params = None ):
870+             if  "GRANT USAGE ON SCHEMA"  in  sql :
871+                 raise  OperationalError ("permission denied for schema public" )
872+ 
873+         mock_cursor .execute .side_effect  =  mock_execute_side_effect 
874+         mock_cursor .fetchall .return_value  =  []  # No tables 
875+ 
876+         result  =  create_masked_role ("test_role" )
877+         assert  result  is  True   # Should still succeed despite schema failure 
878+ 
879+ 
880+ @pytest .mark .django_db  
881+ def  test_database_role_permission_failures ():
882+     """Test database role switching permission failures""" 
883+     from  unittest .mock  import  MagicMock , patch 
884+ 
885+     from  django .db  import  OperationalError 
886+ 
887+     from  django_postgres_anon .context_managers  import  database_role 
888+ 
889+     # Test role switching failure - database_role doesn't have auto_create 
890+     with  patch ("django_postgres_anon.utils.switch_to_role" ) as  mock_switch :
891+         mock_switch .return_value  =  False   # Role switch fails 
892+ 
893+         try :
894+             with  database_role ("nonexistent_role" ):
895+                 # Should handle the role switch failure gracefully 
896+                 pass 
897+         except  RuntimeError  as  e :
898+             assert  "does not exist"  in  str (e )
899+ 
900+     # Test role switching permission failure in switch_to_role itself 
901+     with  patch ("django.db.connection.cursor" ) as  mock_cursor_ctx :
902+         mock_cursor  =  MagicMock ()
903+         mock_cursor_ctx .return_value .__enter__ .return_value  =  mock_cursor 
904+ 
905+         def  mock_execute_side_effect (sql , params = None ):
906+             if  "SET ROLE"  in  sql :
907+                 raise  OperationalError ("permission denied to set role" )
908+ 
909+         mock_cursor .execute .side_effect  =  mock_execute_side_effect 
910+ 
911+         from  django_postgres_anon .utils  import  switch_to_role 
912+ 
913+         # Test with auto_create=True 
914+         with  patch ("django_postgres_anon.utils.create_masked_role" ) as  mock_create :
915+             mock_create .return_value  =  True 
916+ 
917+             result  =  switch_to_role ("test_role" , auto_create = True )
918+             mock_create .assert_called_once_with ("test_role" )
919+             assert  result  is  True 
920+ 
921+         # Test with auto_create=False 
922+         result  =  switch_to_role ("test_role" , auto_create = False )
923+         assert  result  is  False 
0 commit comments