Files
Tunetti/tests/test_discord_rpc.py
NikkeDoy 93f5239ef7
Some checks failed
SonarQube Code Quality Scan / SonarQube Scan (push) Failing after 2m37s
| Add tests
2026-06-03 18:53:24 +03:00

178 lines
5.7 KiB
Python

"""Tests for Discord RPC module (discord_rpc.py)."""
import time
from unittest import mock
import pytest
# ── _safe_artists ─────────────────────────────────────────────────────────────
class TestSafeArtists:
"""Tests for the _safe_artists helper."""
def _import(self):
from discord_rpc import _safe_artists
return _safe_artists
def test_none_song(self):
fn = self._import()
assert fn(None) == ""
def test_empty_dict(self):
fn = self._import()
assert fn({}) == ""
def test_missing_artists_key(self):
fn = self._import()
assert fn({"title": "Test"}) == ""
def test_artists_not_list(self):
fn = self._import()
assert fn({"artists": "not a list"}) == ""
def test_empty_artists_list(self):
fn = self._import()
assert fn({"artists": []}) == ""
def test_single_artist(self):
fn = self._import()
song = {"artists": [{"name": "Test Artist", "id": "x"}]}
assert fn(song) == "Test Artist"
def test_multiple_artists(self):
fn = self._import()
song = {
"artists": [
{"name": "Artist One", "id": "1"},
{"name": "Artist Two", "id": "2"},
]
}
assert fn(song) == "Artist One, Artist Two"
def test_artist_without_name(self):
fn = self._import()
song = {"artists": [{"id": "x"}, {"name": "Real Artist", "id": "y"}]}
assert fn(song) == "Real Artist"
def test_artist_is_not_dict(self):
fn = self._import()
song = {"artists": ["just a string"]}
assert fn(song) == ""
def test_artist_with_empty_name(self):
fn = self._import()
song = {"artists": [{"name": "", "id": "x"}]}
assert fn(song) == ""
# ── DiscordRPC ────────────────────────────────────────────────────────────────
class TestDiscordRPC:
"""Tests for DiscordRPC class with mocked pypresence."""
@pytest.fixture
def rpc(self):
with mock.patch("discord_rpc.Presence") as mock_presence:
# Make the mock Presence instance return a mock
mock_instance = mock.MagicMock()
mock_presence.return_value = mock_instance
from discord_rpc import DiscordRPC
rpc = DiscordRPC(client_id="test_client_123")
yield rpc, mock_instance
rpc.stop()
def test_constructor(self, rpc):
drpc, _ = rpc
assert drpc._client_id == "test_client_123"
assert drpc._connected is False
assert drpc._song is None
def test_start_and_connect(self, rpc):
drpc, mock_presence = rpc
drpc.start()
# Give the thread a moment to connect
import time
time.sleep(0.1)
assert drpc._connected is True
mock_presence.connect.assert_called_once()
def test_update_song_sets_song_and_resets_start(self, rpc):
drpc, _ = rpc
song = {"title": "Test", "artists": [{"name": "A"}], "duration": 200}
drpc.update_song(song, reset_start=True)
assert drpc._song == song
assert drpc._start_ts is not None
def test_update_song_none(self, rpc):
drpc, _ = rpc
drpc.update_song(None)
assert drpc._song is None
assert drpc._start_ts is None
def test_update_song_no_reset(self, rpc):
drpc, _ = rpc
song = {"title": "Test", "artists": [{"name": "A"}], "duration": 200}
drpc.update_song(song, reset_start=True)
old_start = drpc._start_ts
time.sleep(0.01)
# Without reset, start_ts should stay the same
drpc.update_song(song, reset_start=False)
assert drpc._start_ts == old_start
def test_clear_presence(self, rpc):
drpc, _ = rpc
drpc.update_song({"title": "Test"})
assert drpc._song is not None
drpc.clear()
assert drpc._song is None
assert drpc._start_ts is None
def test_seek_to_adjusts_start_ts(self, rpc):
drpc, _ = rpc
drpc.update_song({"title": "Test"}, reset_start=True)
original_start = drpc._start_ts
time.sleep(0.01)
drpc.seek_to(position_ms=30_000) # seek to 30s
# Now the diff between now and start_ts should be ~30s
diff = int(time.time()) - drpc._start_ts
assert 28 <= diff <= 32 # allow some fuzz
def test_stop_disconnects_and_joins(self, rpc):
drpc, mock_presence = rpc
drpc.start()
time.sleep(0.1)
drpc.stop()
assert drpc._rpc is None
assert drpc._connected is False
def test_send_presence_clears_when_no_song(self, rpc):
drpc, mock_presence = rpc
drpc.start()
time.sleep(0.1)
drpc.clear()
time.sleep(0.1)
mock_presence.clear.assert_called()
def test_double_start_is_idempotent(self, rpc):
drpc, _ = rpc
drpc.start()
drpc.start() # should not crash
assert True
@mock.patch("discord_rpc.Presence")
def test_connect_failure_graceful(self, mock_presence_cls):
"""When Discord is not running, connect failure is handled gracefully."""
from discord_rpc import DiscordRPC
mock_instance = mock.MagicMock()
mock_instance.connect.side_effect = Exception("Discord not found")
mock_presence_cls.return_value = mock_instance
drpc = DiscordRPC(client_id="test")
drpc.start()
time.sleep(0.1)
# Should not crash, just log a warning
assert drpc._connected is False
drpc.stop()