"""Audio validation logic module unit test.""" from unittest.mock import patch from src import audio_validator import pytest # mediainfo mocks valid_assets_info = [ [ b'playtime_seconds 2438 * channels 2 * codec PCM * sample_rate 44100 * bits_per_sample 16 * bit_rate 1411200\n', b'container Wave * mime_type audio/vnd.wave * file_size 4301448\n' ], [ b'playtime_seconds 8994 * channels 2 * codec FLAC * sample_rate 44100 * bits_per_sample 16 * bit_rate 665049\n', b'container FLAC * mime_type audio/x-flac * file_size 756047\n' ], [ b'playtime_seconds 2963 * channels 2 * codec PCM * sample_rate 44100 * bits_per_sample 24 * bit_rate 2116800\n', b'container Wave * mime_type audio/vnd.wave * file_size 784190\n' ], [ b'playtime_seconds 4970 * channels 2 * codec PCM * sample_rate 48000 * bits_per_sample 16 * bit_rate 1536000\n', b'container Wave * mime_type audio/vnd.wave * file_size 9556012\n' ], [ b'playtime_seconds 2964 * channels 2 * codec ALAC * sample_rate 44100 * bits_per_sample 16 * bit_rate 821976\n', b'container MPEG-4 * mime_type audio/mp4 * file_size 305433\n' ], [ b'playtime_seconds 909 * channels 2 * codec ALAC * sample_rate 88200 * bits_per_sample 24 * bit_rate 2437845\n', b'container MPEG-4 * mime_type audio/mp4 * file_size 2749129\n' ] ] @patch('subprocess.check_output') @pytest.mark.parametrize('mediainfo_output', valid_assets_info) def test_validate_audio_asset_success(mediainfo_mock, mediainfo_output): """Test for successful call of validate_audio_asset.""" mediainfo_mock.side_effect = mediainfo_output result = audio_validator.validate_audio_asset('test_url') assert result # mediainfo mocks invalid_assets_info = [ ( [ b'playtime_seconds 2963 * channels 1 * codec PCM * sample_rate 44100 * bits_per_sample 16 * ' b'bit_rate 705600\n', b'container Wave * mime_type audio/vnd.wave * file_size 261420\n' ], {'channels': 'value is less than 2'} ), ( [ b'playtime_seconds 205270 * channels 2 * codec MPEG Audio * sample_rate 44100 * bits_per_sample * ' b'bit_rate 128000\n', b'container MPEG Audio * mime_type audio/mpeg * file_size 3314446\n' ], { 'lossless': "value is not exactly 'True'", 'mime_type': { 0: "value is not exactly 'audio/wav'", 1: "value is not exactly 'audio/x-wav'", 2: "value is not exactly 'audio/vnd.wave'", 3: "value is not exactly 'audio/flac'", 4: "value is not exactly 'audio/x-flac'", 5: "value is not exactly 'audio/mp4'", 6: "value is not exactly 'audio/x-m4a'"}, 'bits_per_sample': { 0: "value is not exactly '16'", 1: "value is not exactly '24'"}, 'container': { 0: "value is not exactly 'flac'", 1: "value is not exactly 'mpeg-4'", 2: "value is not exactly 'wave'"}, 'codec': { 0: "value is not exactly 'pcm'", 1: "value is not exactly 'flac'", 2: "value is not exactly 'alac'"}, 'sample_rate': 'Invalid input value 0 for bits_per_sample. Expected value(s): [16, 24]' } ), ( [ b'playtime_seconds 658 * channels 2 * codec AAC * sample_rate 44100 * bits_per_sample * bit_rate 128000\n', b'container MPEG-4 * mime_type audio/mp4 * file_size 10586764\n' ], { 'bits_per_sample': { 0: "value is not exactly '16'", 1: "value is not exactly '24'"}, 'codec': { 0: "value is not exactly 'pcm'", 1: "value is not exactly 'flac'", 2: "value is not exactly 'alac'"}, 'lossless': "value is not exactly 'True'", 'sample_rate': 'Invalid input value 0 for bits_per_sample. Expected value(s): [16, 24]' } ) ] @patch('subprocess.check_output') @pytest.mark.parametrize('mediainfo_output,expected_errors', invalid_assets_info) def test_validate_audio_asset_error(mediainfo_mock, mediainfo_output, expected_errors): """Test validation error handling.""" mediainfo_mock.side_effect = mediainfo_output with pytest.raises(audio_validator.InvalidAudioError) as err: audio_validator.validate_audio_asset('test_url') assert err.value.errors == expected_errors def test_validate_audio_missing_binary(): """Test for missing binary lets the exception bubble up.""" with pytest.raises(FileNotFoundError) as err: audio_validator.validate_audio_asset('test_url') assert 'No such file or directory' in str(err.value) @patch('subprocess.check_output') def test_validate_audio_binary_returns_none(mediainfo_mock): """Test for binary returning none lets the exception bubble up.""" mediainfo_mock.return_value = None with pytest.raises(RuntimeError) as err: audio_validator.validate_audio_asset('test_url') assert str(err.value) == 'Unable to read asset file technical and tag data' @patch('subprocess.check_output') def test_validate_audio_binary_returns_empty(mediainfo_mock): """Test for binary returning none lets the exception bubble up.""" with pytest.raises(KeyError) as err: audio_validator.validate_audio_asset('test_url') assert str(err.value) == "'container'" @pytest.mark.parametrize('codec, mime_type, expected_result', [ ('pcm', 'audio/wav', True), ('pcm', 'audio/x-wav', True), ('pcm', 'audio/vnd.wave', True), ('flac', 'audio/flac', True), ('alac', 'audio/mp4', True), ('alac', 'audio/x-m4a', True), ('pcm', 'unknown', False), ('unknown', 'image/pjpeg', False)]) def test_get_lossless_flag(codec, mime_type, expected_result): """Test for get_lossless_flag function call.""" result = audio_validator.get_lossless_flag(codec, mime_type) assert result == expected_result @pytest.mark.parametrize('channels, expected_mode', [(1, 'mono'), (2, 'stereo')]) def test_get_channel_mode_success(channels, expected_mode): """Test for success call of get_channel_mode.""" result = audio_validator.get_channel_mode(channels) assert result == expected_mode def test_get_channel_mode_failure(): """Test for failure call of get_channel_mode.""" invalid_channels_number = 10 with pytest.raises(audio_validator.InvalidAudioChannels) as err: audio_validator.get_channel_mode(invalid_channels_number) assert err.value.errors == {'audio_channel_mode_error': 'Can not define channel mode for: 10'}