diff --git a/pyrit/converter/audio_volume_converter.py b/pyrit/converter/audio_volume_converter.py index 0d0afa76dc..f746c74d43 100644 --- a/pyrit/converter/audio_volume_converter.py +++ b/pyrit/converter/audio_volume_converter.py @@ -3,6 +3,7 @@ import io import logging +import math from typing import Any, Literal import numpy as np @@ -46,13 +47,13 @@ def __init__( output_format (str): The format of the audio file, defaults to "wav". volume_factor (float): The factor by which to scale the volume. Values > 1.0 increase volume, values < 1.0 decrease volume. - Must be greater than 0. Defaults to 1.5. + Must be finite and greater than 0. Defaults to 1.5. Raises: - ValueError: If volume_factor is not positive. + ValueError: If volume_factor is non-finite or not positive. """ - if volume_factor <= 0: - raise ValueError("volume_factor must be greater than 0.") + if not math.isfinite(volume_factor) or volume_factor <= 0: + raise ValueError("volume_factor must be finite and greater than 0.") self._output_format = output_format self._volume_factor = volume_factor diff --git a/pyrit/converter/audio_white_noise_converter.py b/pyrit/converter/audio_white_noise_converter.py index 35675091d0..7a7e219fdd 100644 --- a/pyrit/converter/audio_white_noise_converter.py +++ b/pyrit/converter/audio_white_noise_converter.py @@ -3,6 +3,7 @@ import io import logging +import math from typing import Any, Literal import numpy as np @@ -44,14 +45,14 @@ def __init__( output_format (str): The format of the audio file, defaults to "wav". noise_scale (float): Controls the amplitude of the added noise, expressed as a fraction of the signal's maximum possible value. For int16 audio - the noise amplitude will be noise_scale * 32767. Must be greater than 0 - and at most 1.0. Defaults to 0.02. + the noise amplitude will be noise_scale * 32767. Must be finite, greater + than 0, and at most 1.0. Defaults to 0.02. Raises: - ValueError: If noise_scale is not in (0, 1]. + ValueError: If noise_scale is non-finite or not in (0, 1]. """ - if noise_scale <= 0 or noise_scale > 1.0: - raise ValueError("noise_scale must be between 0 (exclusive) and 1.0 (inclusive).") + if not math.isfinite(noise_scale) or noise_scale <= 0 or noise_scale > 1.0: + raise ValueError("noise_scale must be finite, greater than 0, and at most 1.0.") self._output_format = output_format self._noise_scale = noise_scale diff --git a/pyrit/converter/image_color_saturation_converter.py b/pyrit/converter/image_color_saturation_converter.py index 4baae25b02..239edcc7bc 100644 --- a/pyrit/converter/image_color_saturation_converter.py +++ b/pyrit/converter/image_color_saturation_converter.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. import logging +import math from typing import Literal from PIL import Image, ImageEnhance @@ -41,13 +42,14 @@ def __init__( 0.0 produces a grayscale image (black and white). 1.0 preserves the original colors. Values greater than 1.0 oversaturate the colors. - Defaults to 0.0 (grayscale image). + Must be finite. Defaults to 0.0 (grayscale image). Raises: - ValueError: If unsupported output format is specified, or if level is negative. + ValueError: If unsupported output format is specified, or if level is non-finite + or negative. """ - if level < 0: - raise ValueError(f"Level must be non-negative, got {level}") + if not math.isfinite(level) or level < 0: + raise ValueError(f"Level must be finite and non-negative, got {level}") self._level = level super().__init__(output_format=output_format) diff --git a/pyrit/converter/image_rotation_converter.py b/pyrit/converter/image_rotation_converter.py index 865366ad17..8c7efc0514 100644 --- a/pyrit/converter/image_rotation_converter.py +++ b/pyrit/converter/image_rotation_converter.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. import logging +import math from typing import Literal from PIL import Image @@ -40,13 +41,16 @@ def __init__( Must be one of 'JPEG', 'PNG', or 'WEBP'. If None, keeps original format (if supported). angle (float): The rotation angle in degrees (counter-clockwise). - Defaults to 90.0. + Must be finite. Defaults to 90.0. fill_color (tuple[int, int, int]): The RGB color to fill exposed background areas after rotation. Defaults to (255, 255, 255) (white). Raises: - ValueError: If unsupported output format is specified, or if the fill color is out of range. + ValueError: If unsupported output format is specified, if the angle is non-finite, + or if the fill color is out of range. """ + if not math.isfinite(angle): + raise ValueError(f"Angle must be finite, got {angle}") if ( not isinstance(fill_color, tuple) or len(fill_color) != 3 diff --git a/tests/unit/converter/test_audio_volume_converter.py b/tests/unit/converter/test_audio_volume_converter.py index 3a159b00b4..478acca34a 100644 --- a/tests/unit/converter/test_audio_volume_converter.py +++ b/tests/unit/converter/test_audio_volume_converter.py @@ -145,13 +145,13 @@ async def test_convert_async_file_not_found(): def test_invalid_volume_factor_zero(): """volume_factor of 0 should raise ValueError.""" - with pytest.raises(ValueError, match="volume_factor must be greater than 0"): + with pytest.raises(ValueError, match="volume_factor must be finite and greater than 0"): AudioVolumeConverter(volume_factor=0) def test_invalid_volume_factor_negative(): """Negative volume_factor should raise ValueError.""" - with pytest.raises(ValueError, match="volume_factor must be greater than 0"): + with pytest.raises(ValueError, match="volume_factor must be finite and greater than 0"): AudioVolumeConverter(volume_factor=-1.0) @@ -160,3 +160,10 @@ async def test_unsupported_input_type(sqlite_instance): converter = AudioVolumeConverter(volume_factor=1.5) with pytest.raises(ValueError, match="Input type not supported"): await converter.convert_async(prompt="some_file.wav", input_type="text") + + +@pytest.mark.parametrize("volume_factor", [float("nan"), float("inf"), float("-inf")]) +def test_invalid_volume_factor_non_finite(volume_factor: float) -> None: + """Non-finite volume factors should fail during converter construction.""" + with pytest.raises(ValueError, match="volume_factor must be finite"): + AudioVolumeConverter(volume_factor=volume_factor) diff --git a/tests/unit/converter/test_audio_white_noise_converter.py b/tests/unit/converter/test_audio_white_noise_converter.py index d164405805..5f1f1dccc5 100644 --- a/tests/unit/converter/test_audio_white_noise_converter.py +++ b/tests/unit/converter/test_audio_white_noise_converter.py @@ -115,19 +115,19 @@ async def test_white_noise_file_not_found(): def test_white_noise_invalid_scale_zero(): """noise_scale of 0 should raise ValueError.""" - with pytest.raises(ValueError, match="noise_scale must be between 0"): + with pytest.raises(ValueError, match="noise_scale must be finite"): AudioWhiteNoiseConverter(noise_scale=0) def test_white_noise_invalid_scale_negative(): """Negative noise_scale should raise ValueError.""" - with pytest.raises(ValueError, match="noise_scale must be between 0"): + with pytest.raises(ValueError, match="noise_scale must be finite"): AudioWhiteNoiseConverter(noise_scale=-0.1) def test_white_noise_invalid_scale_above_one(): """noise_scale > 1 should raise ValueError.""" - with pytest.raises(ValueError, match="noise_scale must be between 0"): + with pytest.raises(ValueError, match="noise_scale must be finite"): AudioWhiteNoiseConverter(noise_scale=1.5) @@ -170,3 +170,10 @@ async def test_white_noise_initialized_seed_is_repeatable_and_does_not_disturb_n for output_path in output_paths: if os.path.exists(output_path): os.remove(output_path) + + +@pytest.mark.parametrize("noise_scale", [float("nan"), float("inf"), float("-inf")]) +def test_invalid_noise_scale_non_finite(noise_scale: float) -> None: + """Non-finite noise scales should fail during converter construction.""" + with pytest.raises(ValueError, match="noise_scale must be finite"): + AudioWhiteNoiseConverter(noise_scale=noise_scale) diff --git a/tests/unit/converter/test_image_color_saturation_converter.py b/tests/unit/converter/test_image_color_saturation_converter.py index e63594eed7..5c9876fdc4 100644 --- a/tests/unit/converter/test_image_color_saturation_converter.py +++ b/tests/unit/converter/test_image_color_saturation_converter.py @@ -53,7 +53,7 @@ def test_image_color_saturation_converter_initialization_output_format_validatio def test_image_color_saturation_converter_initialization_level_validation(): """Test validation of level parameter.""" for invalid_level in [-0.1, -1.0, -100.0]: - with pytest.raises(ValueError, match="Level must be non-negative"): + with pytest.raises(ValueError, match="Level must be finite and non-negative"): ImageColorSaturationConverter(level=invalid_level) for valid_level in [0.0, 0.5, 1.0, 2.0, 10.0]: @@ -213,3 +213,10 @@ async def test_image_color_saturation_converter_output_format_fallback(): mock_serializer.read_data_async.return_value = img_bytes await converter.convert_async(prompt="test.tiff", input_type="image_path") assert mock_serializer.file_extension == "jpeg" + + +@pytest.mark.parametrize("level", [float("nan"), float("inf"), float("-inf")]) +def test_invalid_level_non_finite(level: float) -> None: + """Non-finite saturation levels should fail during converter construction.""" + with pytest.raises(ValueError, match="Level must be finite"): + ImageColorSaturationConverter(level=level) diff --git a/tests/unit/converter/test_image_rotation_converter.py b/tests/unit/converter/test_image_rotation_converter.py index 420085ab79..705e921f2f 100644 --- a/tests/unit/converter/test_image_rotation_converter.py +++ b/tests/unit/converter/test_image_rotation_converter.py @@ -263,3 +263,10 @@ def test_image_rotation_converter_custom_fill_color(sample_image_bytes): # Check that a corner pixel (exposed area) has the fill color corner_pixel = rotated_image.getpixel((0, 0)) assert corner_pixel[:3] == fill_color + + +@pytest.mark.parametrize("angle", [float("nan"), float("inf"), float("-inf")]) +def test_invalid_angle_non_finite(angle: float) -> None: + """Non-finite rotation angles should fail during converter construction.""" + with pytest.raises(ValueError, match="Angle must be finite"): + ImageRotationConverter(angle=angle)