"""Atrapy pozwalające testować ``MusicPlayer`` bez Discorda i ffmpeg.""" from __future__ import annotations import asyncio from unittest.mock import MagicMock from zyrarbot.music.music_player import MusicPlayer class FakeTrack: """Odpowiednik ``YTDLSource`` o tym samym interfejsie, ale bez audio.""" def __init__(self, track_id: str): self.id = track_id self.title = track_id self.data: dict = {} self.url = None self.duration = 10 self.elapsed = 0.0 self.plays_left: int | None = None self.played = False self.volume = 0.5 self.is_local = False self.filepath = None self.display_url = None self.has_web_link = False class FakeVoiceClient: """Klient głosowy, który zapamiętuje odtwarzane źródło zamiast je grać. Nie wywołuje zwrotnie ``after`` - w testach kolejny utwór uruchamiamy ręcznie, tak jak zrobiłaby to biblioteka discord.py po zakończeniu ścieżki. """ def __init__(self) -> None: self.playing = None self.after = None self.source = None def is_playing(self) -> bool: return self.playing is not None def is_paused(self) -> bool: return False def play(self, source, after=None) -> None: self.playing = source self.source = source self.after = after def stop(self) -> None: self.playing = None def build_player(default_plays: int = 5) -> tuple[MusicPlayer, FakeVoiceClient]: bot = MagicMock() bot.loop = asyncio.get_event_loop() voice_client = FakeVoiceClient() async def recreate(track, loop=None): fresh = FakeTrack(track.id) fresh.plays_left = track.plays_left return fresh tracks = MagicMock() tracks.recreate = recreate history = MagicMock() history.list.return_value = [] config = MagicMock() config.get_default_plays.return_value = default_plays channels = MagicMock() channels.resolve.return_value = None player = MusicPlayer(bot, tracks, history, config, channels) player.voice_client = lambda guild_id: voice_client # type: ignore[method-assign] return player, voice_client