From 920d4b18e253f5642b895a1205d9826d54570db8 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 19 Apr 2026 17:41:05 +0200 Subject: feat: add external URL playback support, fix vclogs audio handling --- tests/cogs/test_music_external_play.py | 108 +++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tests/cogs/test_music_external_play.py (limited to 'tests/cogs/test_music_external_play.py') diff --git a/tests/cogs/test_music_external_play.py b/tests/cogs/test_music_external_play.py new file mode 100644 index 0000000..76debc9 --- /dev/null +++ b/tests/cogs/test_music_external_play.py @@ -0,0 +1,108 @@ +import pytest +import os +import asyncio +import tempfile +from unittest.mock import AsyncMock, patch, MagicMock +from tests.conftest import MockApplicationContext + + +@pytest.fixture +def play_commands_cog(bot): + with patch('cogs.utils.temp.get_temp_dir', return_value="/tmp"): + from cogs.music.play_commands import PlayCommands + cog = PlayCommands(bot) + return cog + + +@pytest.fixture +def temp_mp3_file(): + with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f: + f.write(b'fake mp3 content') + temp_path = f.name + yield temp_path + if os.path.exists(temp_path): + os.remove(temp_path) + + +@pytest.mark.asyncio +async def test_external_url_pattern_matches_youtube(play_commands_cog): + from cogs.music.play_commands import EXTERNAL_URL_PATTERN + + assert EXTERNAL_URL_PATTERN.match("https://youtube.com/watch?v=test") + assert EXTERNAL_URL_PATTERN.match("https://youtu.be/test") + assert EXTERNAL_URL_PATTERN.match("https://www.youtube.com/watch?v=test") + + +@pytest.mark.asyncio +async def test_external_url_pattern_matches_soundcloud(play_commands_cog): + from cogs.music.play_commands import EXTERNAL_URL_PATTERN + + assert EXTERNAL_URL_PATTERN.match("https://soundcloud.com/artist/track") + assert EXTERNAL_URL_PATTERN.match("https://api.soundcloud.com/tracks") + + +@pytest.mark.asyncio +async def test_external_url_pattern_matches_bandcamp(play_commands_cog): + from cogs.music.play_commands import EXTERNAL_URL_PATTERN + + assert EXTERNAL_URL_PATTERN.match("https://artist.bandcamp.com/track") + + +@pytest.mark.asyncio +async def test_external_url_pattern_rejects_copyparty(play_commands_cog): + from cogs.music.play_commands import EXTERNAL_URL_PATTERN + + assert not EXTERNAL_URL_PATTERN.match("https://catbox.2137697.xyz/sperma/file.mp3") + assert not EXTERNAL_URL_PATTERN.match("some search query") + + +@pytest.mark.asyncio +async def test_play_command_calls_external_handler(play_commands_cog, mock_channel, mock_guild, bot): + ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild) + + with patch.object(play_commands_cog, '_handle_external_url_play', new_callable=AsyncMock) as mock_handler: + mock_handler.return_value = None + mock_defer = AsyncMock() + ctx.defer = mock_defer + ctx.respond = AsyncMock() + + await play_commands_cog.play.callback( + play_commands_cog, ctx, + melodia="https://youtube.com/watch?v=test", + file=None, compressed=False + ) + + mock_handler.assert_called_once_with(ctx, "https://youtube.com/watch?v=test") + + +@pytest.mark.asyncio +async def test_play_command_calls_local_handler(play_commands_cog, mock_channel, mock_guild, bot): + ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild) + + with patch.object(play_commands_cog, '_handle_local_file_play', new_callable=AsyncMock) as mock_handler: + mock_handler.return_value = None + mock_defer = AsyncMock() + ctx.defer = mock_defer + ctx.respond = AsyncMock() + + await play_commands_cog.play.callback( + play_commands_cog, ctx, + melodia="some search query", + file=None, compressed=False + ) + + mock_handler.assert_called_once_with(ctx, "some search query", False) + + +@pytest.mark.asyncio +async def test_upload_to_copyparty_async(play_commands_cog, temp_mp3_file, monkeypatch): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.return_value = {"fileurl": "https://example.com/file.mp3"} + + monkeypatch.setenv("COPYPARTY_URL_UPLOAD", "https://catbox.2137697.xyz/sperma/") + monkeypatch.setenv("COPYPARTY_PASSWORD", "testpassword") + + with patch('cogs.music.play_commands.requests.post', return_value=mock_response) as mock_post: + await play_commands_cog._upload_to_copyparty_async(temp_mp3_file) + mock_post.assert_called_once() -- cgit v1.2.3 From 6f1225445a9bb988274424e8d7420af5afc392fe Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 19 Apr 2026 17:50:54 +0200 Subject: fix: mock aiohttp instead of requests in external play test --- tests/cogs/test_music_external_play.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'tests/cogs/test_music_external_play.py') diff --git a/tests/cogs/test_music_external_play.py b/tests/cogs/test_music_external_play.py index 76debc9..25c14e0 100644 --- a/tests/cogs/test_music_external_play.py +++ b/tests/cogs/test_music_external_play.py @@ -96,13 +96,16 @@ async def test_play_command_calls_local_handler(play_commands_cog, mock_channel, @pytest.mark.asyncio async def test_upload_to_copyparty_async(play_commands_cog, temp_mp3_file, monkeypatch): - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = {"fileurl": "https://example.com/file.mp3"} - monkeypatch.setenv("COPYPARTY_URL_UPLOAD", "https://catbox.2137697.xyz/sperma/") monkeypatch.setenv("COPYPARTY_PASSWORD", "testpassword") - with patch('cogs.music.play_commands.requests.post', return_value=mock_response) as mock_post: + mock_post = AsyncMock() + mock_response = AsyncMock() + mock_response.status = 200 + mock_response.json = AsyncMock(return_value={"fileurl": "https://example.com/file.mp3"}) + mock_post.return_value.__aenter__ = AsyncMock(return_value=mock_response) + mock_post.return_value.__aexit__ = AsyncMock() + + with patch('aiohttp.ClientSession.post', mock_post): await play_commands_cog._upload_to_copyparty_async(temp_mp3_file) mock_post.assert_called_once() -- cgit v1.2.3 From fca9623e63300af53bbde183496d1b9f3796a6bc Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 19 Apr 2026 18:00:14 +0200 Subject: fix: proper aiohttp async context manager mocking in test --- tests/cogs/test_music_external_play.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'tests/cogs/test_music_external_play.py') diff --git a/tests/cogs/test_music_external_play.py b/tests/cogs/test_music_external_play.py index 25c14e0..fd4b625 100644 --- a/tests/cogs/test_music_external_play.py +++ b/tests/cogs/test_music_external_play.py @@ -99,13 +99,18 @@ async def test_upload_to_copyparty_async(play_commands_cog, temp_mp3_file, monke monkeypatch.setenv("COPYPARTY_URL_UPLOAD", "https://catbox.2137697.xyz/sperma/") monkeypatch.setenv("COPYPARTY_PASSWORD", "testpassword") - mock_post = AsyncMock() mock_response = AsyncMock() mock_response.status = 200 + mock_response.text = AsyncMock(return_value="") mock_response.json = AsyncMock(return_value={"fileurl": "https://example.com/file.mp3"}) - mock_post.return_value.__aenter__ = AsyncMock(return_value=mock_response) - mock_post.return_value.__aexit__ = AsyncMock() + mock_response.__aenter__ = AsyncMock(return_value=mock_response) + mock_response.__aexit__ = AsyncMock() - with patch('aiohttp.ClientSession.post', mock_post): + mock_session_context = AsyncMock() + mock_session_context.__aenter__ = AsyncMock(return_value=mock_session_context) + mock_session_context.__aexit__ = AsyncMock() + mock_session_context.post = MagicMock(return_value=mock_response) + + with patch('aiohttp.ClientSession', return_value=mock_session_context): await play_commands_cog._upload_to_copyparty_async(temp_mp3_file) - mock_post.assert_called_once() + mock_session_context.post.assert_called_once() -- cgit v1.2.3