Files
zyrarbot/tests/fakes.py
T
lgooseandClaude Bot 0879120c85 test: regresja arytmetyki kolejki wskaźnikowej
Kolejka jest nieusuwalna, a pozycję śledzi indeks, więc przeskoki,
usuwanie i zmiana kolejności muszą ten indeks poprawnie przesuwać. To
najbardziej podatna na błędy część przeniesionego kodu, więc dostaje
testy przypinające zachowanie:

- start odtwarzania przy dodaniu do pustej kolejki
- limit odtworzeń usuwający utwór i cofający indeks
- przechodzenie dalej oraz oba tryby zapętlania
- koniec kolejki czyszczący aktualny utwór
- przeskok, usunięcie utworu przed indeksem i usunięcie odtwarzanego
- zmiana kolejności zachowująca indeks na odtwarzanym utworze
- stosowanie głośności serwera i zatrzymanie przy niedziałających utworach

16 przypadków na atrapach Discorda, bez sieci i ffmpega. Uruchamiane
przez python -m unittest discover -s tests -t . - bez nowych zależności.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:37 +02:00

82 lines
2.2 KiB
Python

"""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