fix: tests and linters
This commit is contained in:
7 files changed
+94
-103
No files matched your search
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from zyrarbot.application import Application
|
from zyrarbot.application import Application
|
||||||
from zyrarbot.settings import Settings
|
from zyrarbot.settings import Settings
|
||||||
@@ -49,7 +50,11 @@ class ApiSurfaceTest(unittest.TestCase):
|
|||||||
uploads_dir=f"{cls._workdir.name}/uploads",
|
uploads_dir=f"{cls._workdir.name}/uploads",
|
||||||
static_dir=f"{cls._workdir.name}/static",
|
static_dir=f"{cls._workdir.name}/static",
|
||||||
)
|
)
|
||||||
cls.spec = Application(settings).web.openapi()
|
persistence = MagicMock()
|
||||||
|
persistence.config = MagicMock()
|
||||||
|
persistence.history = MagicMock()
|
||||||
|
persistence.queues = MagicMock()
|
||||||
|
cls.spec = Application(settings, persistence=persistence).web.openapi()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls) -> None:
|
def tearDownClass(cls) -> None:
|
||||||
|
|||||||
@@ -1,32 +1,33 @@
|
|||||||
import json
|
from __future__ import annotations
|
||||||
import tempfile
|
|
||||||
import unittest
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from zyrarbot.storage.guild_config_repository import GuildConfigRepository
|
import unittest
|
||||||
from zyrarbot.storage.json_file_store import JsonFileStore
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from zyrarbot.storage.adapters.postgres.guild_config import PostgresGuildConfig
|
||||||
|
|
||||||
|
|
||||||
class GuildConfigRepositoryTest(unittest.TestCase):
|
class GuildConfigRepositoryTest(unittest.TestCase):
|
||||||
def test_settings_are_isolated_and_legacy_default_moves_to_first_guild(self):
|
def setUp(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
self.database = MagicMock()
|
||||||
path = Path(directory) / "config.json"
|
self.connection = self.database.connect.return_value.__enter__.return_value
|
||||||
path.write_text(json.dumps({"default_plays": 7, "10": 123}), encoding="utf-8")
|
self.repository = PostgresGuildConfig(self.database, fallback_plays=5)
|
||||||
repository = GuildConfigRepository(JsonFileStore(str(path)), fallback_plays=5)
|
|
||||||
|
|
||||||
repository.migrate_legacy(10)
|
def test_default_plays_uses_fallback_when_guild_has_no_override(self):
|
||||||
self.assertEqual(repository.get_default_plays(10), 7)
|
self.connection.execute.return_value.fetchone.return_value = (None,)
|
||||||
self.assertEqual(repository.get_notification_channel_id(10), 123)
|
|
||||||
self.assertEqual(repository.get_default_plays(20), 5)
|
|
||||||
|
|
||||||
repository.set_default_plays(20, 9)
|
self.assertEqual(self.repository.get_default_plays(10), 5)
|
||||||
self.assertEqual(repository.get_default_plays(10), 7)
|
|
||||||
self.assertEqual(repository.get_default_plays(20), 9)
|
|
||||||
|
|
||||||
saved = json.loads(path.read_text(encoding="utf-8"))
|
def test_default_plays_returns_guild_override(self):
|
||||||
self.assertNotIn("default_plays", saved)
|
self.connection.execute.return_value.fetchone.return_value = (7,)
|
||||||
self.assertEqual(saved["10"]["default_plays"], 7)
|
|
||||||
self.assertEqual(saved["20"]["default_plays"], 9)
|
self.assertEqual(self.repository.get_default_plays(10), 7)
|
||||||
|
|
||||||
|
def test_set_default_plays_creates_guild_then_updates_setting(self):
|
||||||
|
self.repository.set_default_plays(10, 9)
|
||||||
|
|
||||||
|
statements = [call.args[0] for call in self.connection.execute.call_args_list]
|
||||||
|
self.assertIn("INSERT INTO guilds", statements[0])
|
||||||
|
self.assertIn("UPDATE guilds SET default_plays", statements[1])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -1,52 +1,49 @@
|
|||||||
import json
|
from __future__ import annotations
|
||||||
import tempfile
|
|
||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from zyrarbot.music.history_entry import HistoryEntry
|
from zyrarbot.music.history_entry import HistoryEntry
|
||||||
from zyrarbot.storage.history_repository import HistoryRepository
|
from zyrarbot.storage.adapters.postgres.history import PostgresHistory
|
||||||
from zyrarbot.storage.json_file_store import JsonFileStore
|
|
||||||
|
|
||||||
|
|
||||||
class HistoryRepositoryTest(unittest.TestCase):
|
class HistoryRepositoryTest(unittest.TestCase):
|
||||||
def test_history_is_stored_in_separate_files_and_legacy_data_is_migrated(self):
|
def setUp(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
self.database = MagicMock()
|
||||||
legacy_path = Path(directory) / "history.json"
|
self.connection = self.database.connect.return_value.__enter__.return_value
|
||||||
entry = HistoryEntry(
|
self.repository = PostgresHistory(self.database)
|
||||||
|
|
||||||
|
def test_list_maps_database_rows_to_history_entries(self):
|
||||||
|
self.connection.execute.return_value.fetchall.return_value = [
|
||||||
|
("track-1", "Track", "https://example.test/track", 10, False)
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.repository.list(10),
|
||||||
|
[
|
||||||
|
HistoryEntry(
|
||||||
id="track-1",
|
id="track-1",
|
||||||
title="Track",
|
title="Track",
|
||||||
url="https://example.test/track",
|
url="https://example.test/track",
|
||||||
duration=10,
|
duration=10,
|
||||||
is_local=False,
|
is_local=False,
|
||||||
filepath=None,
|
|
||||||
)
|
)
|
||||||
legacy_path.write_text(json.dumps({"10": [entry.to_dict()]}), encoding="utf-8")
|
],
|
||||||
repository = HistoryRepository(JsonFileStore(str(legacy_path)))
|
|
||||||
|
|
||||||
repository.migrate_legacy(10)
|
|
||||||
|
|
||||||
self.assertEqual(repository.list(10)[0].id, "track-1")
|
|
||||||
self.assertEqual(repository.list(20), [])
|
|
||||||
self.assertTrue((Path(directory) / "history" / "10.json").exists())
|
|
||||||
self.assertFalse((Path(directory) / "history" / "20.json").exists())
|
|
||||||
|
|
||||||
def test_new_entries_are_available_after_repository_reload(self):
|
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
|
||||||
store = JsonFileStore(str(Path(directory) / "history.json"))
|
|
||||||
entry = HistoryEntry(
|
|
||||||
id="track-2",
|
|
||||||
title="Another track",
|
|
||||||
url="https://example.test/another-track",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
HistoryRepository(store).add(42, entry)
|
def test_add_ignores_entries_without_a_title(self):
|
||||||
|
self.repository.add(10, HistoryEntry(id="track-1", title=""))
|
||||||
|
|
||||||
reloaded = HistoryRepository(store)
|
self.database.connect.assert_not_called()
|
||||||
self.assertEqual(reloaded.list(42), [entry])
|
|
||||||
persisted = json.loads(
|
def test_add_persists_entry_for_guild(self):
|
||||||
(Path(directory) / "history" / "42.json").read_text(encoding="utf-8")
|
self.connection.execute.return_value.fetchall.return_value = []
|
||||||
)
|
entry = HistoryEntry(id="track-1", title="Track", url="https://example.test/track")
|
||||||
self.assertEqual(persisted["entries"][0]["id"], "track-2")
|
|
||||||
|
self.repository.add(10, entry)
|
||||||
|
|
||||||
|
statements = [call.args[0] for call in self.connection.execute.call_args_list]
|
||||||
|
self.assertTrue(any("INSERT INTO history_entries" in statement for statement in statements))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import tempfile
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from tests.fakes import FakeTrack, build_player
|
from tests.fakes import FakeTrack, build_player
|
||||||
from zyrarbot.music.loop_mode import LoopMode
|
from zyrarbot.music.loop_mode import LoopMode
|
||||||
from zyrarbot.storage.queue_repository import QueueRepository
|
|
||||||
|
|
||||||
GUILD = 1
|
GUILD = 1
|
||||||
|
|
||||||
@@ -293,12 +292,10 @@ class MusicPlayerQueueTest(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
async def test_restore_replaces_active_queue_without_old_callback_interference(self):
|
async def test_restore_replaces_active_queue_without_old_callback_interference(self):
|
||||||
player, voice_client = build_player()
|
player, voice_client = build_player()
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
player._queues = MagicMock()
|
||||||
player._queues = QueueRepository(directory)
|
player._queues.load.return_value = [
|
||||||
player._queues.save(
|
{"id": "restored", "title": "Restored", "plays_left": 3}
|
||||||
GUILD,
|
]
|
||||||
[{"id": "restored", "title": "Restored", "plays_left": 3}],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def from_url(url, loop=None):
|
async def from_url(url, loop=None):
|
||||||
return FakeTrack("restored")
|
return FakeTrack("restored")
|
||||||
@@ -324,25 +321,19 @@ class MusicPlayerQueueTest(unittest.IsolatedAsyncioTestCase):
|
|||||||
await player.restore_queue(GUILD)
|
await player.restore_queue(GUILD)
|
||||||
|
|
||||||
def test_save_queue_does_not_overwrite_with_empty_queue(self):
|
def test_save_queue_does_not_overwrite_with_empty_queue(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
|
||||||
player, _ = build_player()
|
player, _ = build_player()
|
||||||
player._queues = QueueRepository(directory)
|
player._queues = MagicMock()
|
||||||
saved = [{"id": "saved", "title": "Saved", "plays_left": 2}]
|
|
||||||
player._queues.save(GUILD, saved)
|
|
||||||
|
|
||||||
# Kolejka w stanie odtwarzacza jest pusta
|
# Kolejka w stanie odtwarzacza jest pusta
|
||||||
player.state(GUILD).queue = []
|
player.state(GUILD).queue = []
|
||||||
player.save_queue(GUILD)
|
player.save_queue(GUILD)
|
||||||
|
|
||||||
# Zapisana wcześniej kolejka powinna pozostać nienaruszona
|
# Zapisana wcześniej kolejka powinna pozostać nienaruszona
|
||||||
self.assertEqual(player._queues.load(GUILD), saved)
|
player._queues.save.assert_not_called()
|
||||||
|
|
||||||
def test_save_queue_does_not_save_when_all_plays_consumed(self):
|
def test_save_queue_does_not_save_when_all_plays_consumed(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
|
||||||
player, _ = build_player()
|
player, _ = build_player()
|
||||||
player._queues = QueueRepository(directory)
|
player._queues = MagicMock()
|
||||||
saved = [{"id": "saved", "title": "Saved", "plays_left": 2}]
|
|
||||||
player._queues.save(GUILD, saved)
|
|
||||||
|
|
||||||
# Wszystkie utwory w kolejce mają plays_left = 0
|
# Wszystkie utwory w kolejce mają plays_left = 0
|
||||||
track = FakeTrack("consumed")
|
track = FakeTrack("consumed")
|
||||||
@@ -351,7 +342,7 @@ class MusicPlayerQueueTest(unittest.IsolatedAsyncioTestCase):
|
|||||||
player.save_queue(GUILD)
|
player.save_queue(GUILD)
|
||||||
|
|
||||||
# Zapisana wcześniej kolejka powinna pozostać nienaruszona
|
# Zapisana wcześniej kolejka powinna pozostać nienaruszona
|
||||||
self.assertEqual(player._queues.load(GUILD), saved)
|
player._queues.save.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class LoopModeTest(unittest.TestCase):
|
class LoopModeTest(unittest.TestCase):
|
||||||
|
|||||||
@@ -1,40 +1,35 @@
|
|||||||
import tempfile
|
from __future__ import annotations
|
||||||
import unittest
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from zyrarbot.storage.queue_repository import QueueRepository
|
import unittest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from zyrarbot.storage.adapters.postgres.queue import PostgresQueue
|
||||||
|
|
||||||
|
|
||||||
class QueueRepositoryTest(unittest.TestCase):
|
class QueueRepositoryTest(unittest.TestCase):
|
||||||
def test_snapshots_are_isolated_per_guild(self):
|
def setUp(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
self.database = MagicMock()
|
||||||
repository = QueueRepository(directory)
|
self.connection = self.database.connect.return_value.__enter__.return_value
|
||||||
first = [{"id": "first", "title": "First", "plays_left": 2}]
|
self.repository = PostgresQueue(self.database)
|
||||||
second = [{"id": "second", "title": "Second", "plays_left": 4}]
|
|
||||||
|
|
||||||
repository.save(10, first)
|
def test_load_returns_guild_snapshot(self):
|
||||||
repository.save(20, second)
|
snapshot = [{"id": "track-1", "title": "Track", "plays_left": 2}]
|
||||||
|
self.connection.execute.return_value.fetchone.return_value = (snapshot,)
|
||||||
|
|
||||||
self.assertEqual(repository.load(10), first)
|
self.assertEqual(self.repository.load(10), snapshot)
|
||||||
self.assertEqual(repository.load(20), second)
|
|
||||||
self.assertTrue((Path(directory) / "10.json").exists())
|
|
||||||
self.assertTrue((Path(directory) / "20.json").exists())
|
|
||||||
|
|
||||||
def test_save_empty_tracks_does_nothing(self):
|
def test_save_empty_snapshot_does_not_overwrite_saved_queue(self):
|
||||||
with tempfile.TemporaryDirectory() as directory:
|
self.repository.save(10, [])
|
||||||
repository = QueueRepository(directory)
|
|
||||||
tracks = [{"id": "first", "title": "First", "plays_left": 2}]
|
|
||||||
|
|
||||||
repository.save(10, tracks)
|
self.database.connect.assert_not_called()
|
||||||
self.assertEqual(repository.load(10), tracks)
|
|
||||||
|
|
||||||
# Próba zapisania pustej listy nie powinna nadpisać poprzedniej
|
def test_save_upserts_snapshot_for_guild(self):
|
||||||
repository.save(10, [])
|
tracks = [{"id": "track-1", "title": "Track", "plays_left": 2}]
|
||||||
self.assertEqual(repository.load(10), tracks)
|
|
||||||
|
|
||||||
# Zapis pustej kolejki dla nowego serwera nie powinien tworzyć pliku
|
self.repository.save(10, tracks)
|
||||||
repository.save(20, [])
|
|
||||||
self.assertFalse(repository.exists(20))
|
statements = [call.args[0] for call in self.connection.execute.call_args_list]
|
||||||
|
self.assertTrue(any("INSERT INTO queue_snapshots" in statement for statement in statements))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ShutdownVoiceCleanupTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.uploads = MagicMock()
|
self.uploads = MagicMock()
|
||||||
self.config = MagicMock()
|
self.config = MagicMock()
|
||||||
self.history = MagicMock()
|
self.history = MagicMock()
|
||||||
|
self.migration = MagicMock()
|
||||||
|
|
||||||
self.bot = MusicBot(
|
self.bot = MusicBot(
|
||||||
self.settings,
|
self.settings,
|
||||||
@@ -23,6 +24,7 @@ class ShutdownVoiceCleanupTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.uploads,
|
self.uploads,
|
||||||
self.config,
|
self.config,
|
||||||
self.history,
|
self.history,
|
||||||
|
self.migration,
|
||||||
)
|
)
|
||||||
self.player = MagicMock()
|
self.player = MagicMock()
|
||||||
self.bot.player = self.player
|
self.bot.player = self.player
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import tempfile
|
|||||||
import unittest
|
import unittest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from zyrarbot.storage.uploads_repository import UploadsRepository
|
from zyrarbot.storage.adapters.filesystem.uploads import UploadsRepository
|
||||||
from zyrarbot.ui.bot.cogs.playback_cog import upload_choices
|
from zyrarbot.ui.bot.cogs.playback_cog import upload_choices
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user