Skip to content

Commit 0bffa50

Browse files
authored
MAINT: Fixing low-hanging test warnings (microsoft#1160)
1 parent a2ab184 commit 0bffa50

7 files changed

Lines changed: 69 additions & 30 deletions

File tree

pyrit/prompt_converter/transparency_attack_converter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ async def _save_blended_image(self, attack_image: numpy.ndarray, alpha: numpy.nd
206206
img_serializer.file_extension = "png"
207207

208208
la_image = self._create_blended_image(attack_image, alpha)
209-
la_pil = Image.fromarray(la_image, mode="LA")
209+
# Pillow 10+ can infer the mode from the array dtype and shape
210+
la_pil = Image.fromarray(la_image)
210211
image_buffer = BytesIO()
211212
la_pil.save(image_buffer, format="PNG")
212213
image_str = base64.b64encode(image_buffer.getvalue())

tests/unit/converter/test_math_prompt_converter.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT license.
33

4-
from unittest.mock import AsyncMock
4+
from unittest.mock import AsyncMock, MagicMock
55

66
import pytest
77

@@ -12,8 +12,9 @@
1212

1313
@pytest.mark.asyncio
1414
async def test_math_prompt_converter_convert_async():
15-
# Mock the converter target
16-
mock_converter_target = AsyncMock()
15+
# Mock the converter target - use MagicMock for synchronous methods
16+
mock_converter_target = MagicMock()
17+
mock_converter_target.send_prompt_async = AsyncMock()
1718
# Specify parameters=['prompt'] to match the placeholder in the template
1819
template_value = "Solve the following problem: {{ prompt }}"
1920
dataset_name = "dataset_1"
@@ -66,8 +67,9 @@ async def test_math_prompt_converter_convert_async():
6667

6768
@pytest.mark.asyncio
6869
async def test_math_prompt_converter_handles_disallowed_content():
69-
# Mock the converter target
70-
mock_converter_target = AsyncMock()
70+
# Mock the converter target - use MagicMock for synchronous methods
71+
mock_converter_target = MagicMock()
72+
mock_converter_target.send_prompt_async = AsyncMock()
7173
# Specify parameters=['prompt'] to match the placeholder in the template
7274
template_value = "Encode this instruction: {{ prompt }}"
7375
dataset_name = "dataset_1"
@@ -117,8 +119,9 @@ async def test_math_prompt_converter_handles_disallowed_content():
117119

118120
@pytest.mark.asyncio
119121
async def test_math_prompt_converter_invalid_input_type():
120-
# Mock the converter target
121-
mock_converter_target = AsyncMock()
122+
# Mock the converter target - use MagicMock for synchronous methods
123+
mock_converter_target = MagicMock()
124+
mock_converter_target.send_prompt_async = AsyncMock()
122125
# Specify parameters=['prompt'] to match the placeholder in the template
123126
template_value = "Encode this instruction: {{ prompt }}"
124127
dataset_name = "dataset_1"
@@ -132,13 +135,15 @@ async def test_math_prompt_converter_invalid_input_type():
132135

133136
# Test with an invalid input type
134137
with pytest.raises(ValueError, match="Input type not supported"):
135-
await converter.convert_async(prompt="Test prompt", input_type="unsupported")
138+
# Use type: ignore to suppress the type error for testing invalid input
139+
await converter.convert_async(prompt="Test prompt", input_type="unsupported") # type: ignore
136140

137141

138142
@pytest.mark.asyncio
139143
async def test_math_prompt_converter_error_handling():
140-
# Mock the converter target
141-
mock_converter_target = AsyncMock()
144+
# Mock the converter target - use MagicMock for synchronous methods
145+
mock_converter_target = MagicMock()
146+
mock_converter_target.send_prompt_async = AsyncMock()
142147
# Specify parameters=['prompt'] to match the placeholder in the template
143148
template_value = "Encode this instruction: {{ prompt }}"
144149
dataset_name = "dataset_1"

tests/unit/score/test_self_ask_category.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ async def test_score_prompts_batch_async(
199199
scorer_category_response_false: Message,
200200
patch_central_database,
201201
):
202-
chat_target = AsyncMock()
202+
chat_target = MagicMock()
203+
chat_target.send_prompt_async = AsyncMock()
203204
chat_target._max_requests_per_minute = max_requests_per_minute
204205
with patch.object(CentralMemory, "get_memory_instance", return_value=MagicMock()):
205206
scorer = SelfAskCategoryScorer(

tests/unit/target/test_huggingface_chat_target.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,23 @@ def mock_pretrained_config():
7878
yield
7979

8080

81-
class AwaitableMock(AsyncMock):
81+
class AwaitableTask(AsyncMock):
82+
"""Mock that can be awaited and acts like an asyncio.Task"""
83+
8284
def __await__(self):
83-
return iter([])
85+
# Return a completed future-like object
86+
async def _await():
87+
return None
88+
89+
return _await().__await__()
8490

8591

8692
@pytest.fixture(autouse=True)
8793
def mock_create_task():
88-
with patch("asyncio.create_task", return_value=AwaitableMock(spec=Task)):
89-
yield
94+
with patch("asyncio.create_task") as mock_task:
95+
# Return an AwaitableTask that can be awaited
96+
mock_task.return_value = AwaitableTask(spec=Task)
97+
yield mock_task
9098

9199

92100
@pytest.fixture(autouse=True)
@@ -124,17 +132,23 @@ async def test_hf_initialization(patch_central_database, mock_download_specific_
124132

125133

126134
@pytest.mark.skipif(not is_torch_installed(), reason="torch is not installed")
127-
def test_is_model_id_valid_true():
135+
@pytest.mark.asyncio
136+
async def test_is_model_id_valid_true():
128137
# Simulate valid model ID
129138
hf_chat = HuggingFaceChatTarget(model_id="test_model", use_cuda=False)
139+
# Await the background task to prevent warnings
140+
await hf_chat.load_model_and_tokenizer_task
130141
assert hf_chat.is_model_id_valid()
131142

132143

133144
@pytest.mark.skipif(not is_torch_installed(), reason="torch is not installed")
134-
def test_is_model_id_valid_false():
145+
@pytest.mark.asyncio
146+
async def test_is_model_id_valid_false():
135147
# Simulate invalid model ID by causing an exception
136148
with patch("transformers.PretrainedConfig.from_pretrained", side_effect=Exception("Invalid model")):
137149
hf_chat = HuggingFaceChatTarget(model_id="test_model", use_cuda=False)
150+
# Await the background task to prevent warnings
151+
await hf_chat.load_model_and_tokenizer_task
138152
assert not hf_chat.is_model_id_valid()
139153

140154

@@ -191,8 +205,11 @@ async def test_missing_chat_template_error():
191205

192206

193207
@pytest.mark.skipif(not is_torch_installed(), reason="torch is not installed")
194-
def test_invalid_prompt_request_validation():
208+
@pytest.mark.asyncio
209+
async def test_invalid_prompt_request_validation():
195210
hf_chat = HuggingFaceChatTarget(model_id="test_model", use_cuda=False)
211+
# Await the background task to prevent warnings
212+
await hf_chat.load_model_and_tokenizer_task
196213

197214
# Create an invalid prompt request with multiple message pieces
198215
message_piece1 = MessagePiece(
@@ -301,18 +318,24 @@ async def test_optional_kwargs_args_passed_when_loading_model(mock_transformers)
301318

302319

303320
@pytest.mark.skipif(not is_torch_installed(), reason="torch is not installed")
304-
def test_is_json_response_supported():
321+
@pytest.mark.asyncio
322+
async def test_is_json_response_supported():
305323
hf_chat = HuggingFaceChatTarget(model_id="dummy", use_cuda=False, trust_remote_code=True)
324+
# Await the background task to prevent warnings
325+
await hf_chat.load_model_and_tokenizer_task
306326
assert hf_chat.is_json_response_supported() is False
307327

308328

309329
@pytest.mark.skipif(not is_torch_installed(), reason="torch is not installed")
310-
def test_hugging_face_chat_sets_endpoint_and_rate_limit():
330+
@pytest.mark.asyncio
331+
async def test_hugging_face_chat_sets_endpoint_and_rate_limit():
311332
target = HuggingFaceChatTarget(
312333
model_id="test_model",
313334
use_cuda=False,
314335
max_requests_per_minute=30,
315336
)
337+
# Await the background task to prevent warnings
338+
await target.load_model_and_tokenizer_task
316339
identifier = target.get_identifier()
317340
# HuggingFaceChatTarget doesn't set an endpoint (it's local), so it shouldn't be in identifier
318341
assert "endpoint" not in identifier

tests/unit/test_azure_storage_auth.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Licensed under the MIT license.
33

44
from datetime import datetime, timedelta
5-
from unittest.mock import AsyncMock, patch
5+
from unittest.mock import AsyncMock, MagicMock, patch
66

77
import pytest
88
from azure.storage.blob import UserDelegationKey
@@ -54,7 +54,8 @@ async def test_get_sas_token(
5454

5555
# Mocking the container URL
5656
container_url = "https://mockaccount.blob.core.windows.net/mockcontainer"
57-
mock_container_client_instance = AsyncMock()
57+
# Use MagicMock with spec to avoid AsyncMock behavior for sync properties
58+
mock_container_client_instance = MagicMock(spec=["container_name", "account_name"])
5859
mock_container_client.from_container_url.return_value = mock_container_client_instance
5960
mock_container_client_instance.container_name = "mock_container"
6061
mock_container_client_instance.account_name = "mock_account"

tests/unit/test_hf_model_downloads.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ def setup_environment():
3232
yield token
3333

3434

35-
def test_download_specific_files(setup_environment):
35+
@pytest.mark.asyncio
36+
async def test_download_specific_files(setup_environment):
3637
"""Test downloading specific files"""
3738
token = setup_environment # Get the token from the fixture
3839

3940
with patch("os.makedirs"):
4041
with patch("pyrit.common.download_hf_model.download_files"):
41-
download_specific_files(MODEL_ID, FILE_PATTERNS, token, Path(""))
42+
await download_specific_files(MODEL_ID, FILE_PATTERNS, token, Path(""))

tests/unit/test_prompt_normalizer.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ async def test_send_prompt_async_no_response_adds_memory(mock_memory_instance, s
124124

125125
@pytest.mark.asyncio
126126
async def test_send_prompt_async_empty_response_exception_handled(mock_memory_instance, seed_group):
127-
prompt_target = AsyncMock()
127+
# Use MagicMock with send_prompt_async as AsyncMock to avoid coroutine warnings on other methods
128+
prompt_target = MagicMock()
128129
prompt_target.send_prompt_async = AsyncMock(side_effect=EmptyResponseException(message="Empty response"))
129130

130131
normalizer = PromptNormalizer()
@@ -142,7 +143,8 @@ async def test_send_prompt_async_empty_response_exception_handled(mock_memory_in
142143

143144
@pytest.mark.asyncio
144145
async def test_send_prompt_async_request_response_added_to_memory(mock_memory_instance, seed_group):
145-
prompt_target = AsyncMock()
146+
# Use MagicMock with send_prompt_async as AsyncMock to avoid coroutine warnings
147+
prompt_target = MagicMock()
146148

147149
response = MessagePiece(role="assistant", original_value="test_response").to_message()
148150

@@ -288,6 +290,9 @@ async def test_send_prompt_async_converters_response(mock_memory_instance, seed_
288290
@pytest.mark.asyncio
289291
async def test_send_prompt_async_image_converter(mock_memory_instance):
290292
prompt_target = MagicMock(PromptTarget)
293+
prompt_target.send_prompt_async = AsyncMock(
294+
return_value=MessagePiece(role="assistant", original_value="response").to_message()
295+
)
291296

292297
mock_image_converter = MagicMock(PromptConverter)
293298

@@ -297,9 +302,11 @@ async def test_send_prompt_async_image_converter(mock_memory_instance):
297302
filename = f.name
298303
f.write(b"Hello")
299304

300-
mock_image_converter.convert_tokens_async.return_value = ConverterResult(
301-
output_type="image_path",
302-
output_text=filename,
305+
mock_image_converter.convert_tokens_async = AsyncMock(
306+
return_value=ConverterResult(
307+
output_type="image_path",
308+
output_text=filename,
309+
)
303310
)
304311

305312
prompt_converters = PromptConverterConfiguration(converters=[mock_image_converter])

0 commit comments

Comments
 (0)