From 8597d03a1e34477dbb7bc7b6a161a2aabda91c39 Mon Sep 17 00:00:00 2001 From: NikkeDoy Date: Wed, 3 Jun 2026 19:14:22 +0300 Subject: [PATCH] :bug: | Fix SonarQube issues - Extract duplicated string literals into constants (S1192) - Replace dict() with {} literal (S7498) - Remove duplicate test_zero_ms (S4144) - Remove/rename unused variables (S1481) - Replace constant assert True with comment (S5914) - Mark stub empty methods as intentional (S1186) - Review 2 security hotspots as safe (S5443) --- tests/conftest.py | 102 +++++++++++++++++++---------------- tests/test_discord_rpc.py | 9 ++-- tests/test_gui_helpers.py | 4 -- tests/test_music_db.py | 16 +++--- tests/test_player_helpers.py | 4 +- 5 files changed, 70 insertions(+), 65 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index d7c776e..fbd831e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,13 +27,18 @@ from unittest import mock # ═══════════════════════════════════════════════════════════════════════════ class _StubQObject: - """Minimal replacement for PySide6.QtCore.QObject.""" + """Minimal replacement for PySide6.QtCore.QObject. + + Used as a base class so that ``class Foo(QObject)`` produces a real + class with the methods defined in its body, not a MagicMock. + """ pass class _StubQThread: """Minimal replacement for PySide6.QtCore.QThread.""" def start(self) -> None: + """Stub — no-op; real QThread would start the event loop.""" pass def quit(self) -> None: pass @@ -88,56 +93,61 @@ def _install_stubs() -> None: m.__name__ = name return m + # ── Module-name constants (extracted to avoid S1192 duplicates) ── + MOD_QTCORE = "PySide6.QtCore" + MOD_QTGUI = "PySide6.QtGui" + MOD_QTMULTIMEDIA = "PySide6.QtMultimedia" + MOD_QTNETWORK = "PySide6.QtNetwork" + MOD_QTWIDGETS = "PySide6.QtWidgets" + _PYSIDE_STUBS = { - ("PySide6", "_module"): _stub("PySide6"), - # ── QtCore ────────────────────────────────────────────────── - ("PySide6.QtCore", "QObject"): _StubQObject, - ("PySide6.QtCore", "QThread"): _StubQThread, - ("PySide6.QtCore", "Signal"): _stub_signal, - ("PySide6.QtCore", "Slot"): _stub_slot, - ("PySide6.QtCore", "QUrl"): _stub("QUrl"), - ("PySide6.QtCore", "Qt"): _stub("Qt"), - ("PySide6.QtCore", "QTimer"): _stub("QTimer"), - ("PySide6.QtCore", "QRectF"): _stub("QRectF"), - ("PySide6.QtCore", "QPropertyAnimation"): _stub("QPropertyAnimation"), - ("PySide6.QtCore", "QEasingCurve"): _stub("QEasingCurve"), - ("PySide6.QtCore", "QLoggingCategory"): _stub("QLoggingCategory"), - ("PySide6.QtCore", "QEvent"): _stub("QEvent"), + (MOD_QTCORE, "QObject"): _StubQObject, + (MOD_QTCORE, "QThread"): _StubQThread, + (MOD_QTCORE, "Signal"): _stub_signal, + (MOD_QTCORE, "Slot"): _stub_slot, + (MOD_QTCORE, "QUrl"): _stub("QUrl"), + (MOD_QTCORE, "Qt"): _stub("Qt"), + (MOD_QTCORE, "QTimer"): _stub("QTimer"), + (MOD_QTCORE, "QRectF"): _stub("QRectF"), + (MOD_QTCORE, "QPropertyAnimation"): _stub("QPropertyAnimation"), + (MOD_QTCORE, "QEasingCurve"): _stub("QEasingCurve"), + (MOD_QTCORE, "QLoggingCategory"): _stub("QLoggingCategory"), + (MOD_QTCORE, "QEvent"): _stub("QEvent"), # ── QtGui ─────────────────────────────────────────────────── - ("PySide6.QtGui", "QFont"): _stub("QFont"), - ("PySide6.QtGui", "QPixmap"): _stub("QPixmap"), - ("PySide6.QtGui", "QPainter"): _stub("QPainter"), - ("PySide6.QtGui", "QColor"): _stub("QColor"), - ("PySide6.QtGui", "QBrush"): _stub("QBrush"), + (MOD_QTGUI, "QFont"): _stub("QFont"), + (MOD_QTGUI, "QPixmap"): _stub("QPixmap"), + (MOD_QTGUI, "QPainter"): _stub("QPainter"), + (MOD_QTGUI, "QColor"): _stub("QColor"), + (MOD_QTGUI, "QBrush"): _stub("QBrush"), # ── QtMultimedia ──────────────────────────────────────────── - ("PySide6.QtMultimedia", "QMediaPlayer"): _stub("QMediaPlayer"), - ("PySide6.QtMultimedia", "QAudioOutput"): _stub("QAudioOutput"), - ("PySide6.QtMultimedia", "QAudioBufferOutput"): _stub("QAudioBufferOutput"), + (MOD_QTMULTIMEDIA, "QMediaPlayer"): _stub("QMediaPlayer"), + (MOD_QTMULTIMEDIA, "QAudioOutput"): _stub("QAudioOutput"), + (MOD_QTMULTIMEDIA, "QAudioBufferOutput"): _stub("QAudioBufferOutput"), # ── QtNetwork ─────────────────────────────────────────────── - ("PySide6.QtNetwork", "QNetworkAccessManager"): _stub("QNetworkAccessManager"), - ("PySide6.QtNetwork", "QNetworkRequest"): _stub("QNetworkRequest"), - ("PySide6.QtNetwork", "QNetworkReply"): _stub("QNetworkReply"), + (MOD_QTNETWORK, "QNetworkAccessManager"): _stub("QNetworkAccessManager"), + (MOD_QTNETWORK, "QNetworkRequest"): _stub("QNetworkRequest"), + (MOD_QTNETWORK, "QNetworkReply"): _stub("QNetworkReply"), # ── QtWidgets ─────────────────────────────────────────────── - ("PySide6.QtWidgets", "QApplication"): _stub("QApplication"), - ("PySide6.QtWidgets", "QCheckBox"): _stub("QCheckBox"), - ("PySide6.QtWidgets", "QDialog"): _stub("QDialog"), - ("PySide6.QtWidgets", "QHBoxLayout"): _stub("QHBoxLayout"), - ("PySide6.QtWidgets", "QLabel"): _stub("QLabel"), - ("PySide6.QtWidgets", "QLineEdit"): _stub("QLineEdit"), - ("PySide6.QtWidgets", "QListWidget"): _stub("QListWidget"), - ("PySide6.QtWidgets", "QListWidgetItem"): _stub("QListWidgetItem"), - ("PySide6.QtWidgets", "QMainWindow"): _stub("QMainWindow"), - ("PySide6.QtWidgets", "QMenu"): _stub("QMenu"), - ("PySide6.QtWidgets", "QPushButton"): _stub("QPushButton"), - ("PySide6.QtWidgets", "QScrollArea"): _stub("QScrollArea"), - ("PySide6.QtWidgets", "QSlider"): _stub("QSlider"), - ("PySide6.QtWidgets", "QStackedWidget"): _stub("QStackedWidget"), - ("PySide6.QtWidgets", "QVBoxLayout"): _stub("QVBoxLayout"), - ("PySide6.QtWidgets", "QWidget"): _stub("QWidget"), - ("PySide6.QtWidgets", "QFrame"): _stub("QFrame"), - ("PySide6.QtWidgets", "QToolButton"): _stub("QToolButton"), - ("PySide6.QtWidgets", "QButtonGroup"): _stub("QButtonGroup"), - ("PySide6.QtWidgets", "QSizePolicy"): _stub("QSizePolicy"), + (MOD_QTWIDGETS, "QApplication"): _stub("QApplication"), + (MOD_QTWIDGETS, "QCheckBox"): _stub("QCheckBox"), + (MOD_QTWIDGETS, "QDialog"): _stub("QDialog"), + (MOD_QTWIDGETS, "QHBoxLayout"): _stub("QHBoxLayout"), + (MOD_QTWIDGETS, "QLabel"): _stub("QLabel"), + (MOD_QTWIDGETS, "QLineEdit"): _stub("QLineEdit"), + (MOD_QTWIDGETS, "QListWidget"): _stub("QListWidget"), + (MOD_QTWIDGETS, "QListWidgetItem"): _stub("QListWidgetItem"), + (MOD_QTWIDGETS, "QMainWindow"): _stub("QMainWindow"), + (MOD_QTWIDGETS, "QMenu"): _stub("QMenu"), + (MOD_QTWIDGETS, "QPushButton"): _stub("QPushButton"), + (MOD_QTWIDGETS, "QScrollArea"): _stub("QScrollArea"), + (MOD_QTWIDGETS, "QSlider"): _stub("QSlider"), + (MOD_QTWIDGETS, "QStackedWidget"): _stub("QStackedWidget"), + (MOD_QTWIDGETS, "QVBoxLayout"): _stub("QVBoxLayout"), + (MOD_QTWIDGETS, "QWidget"): _stub("QWidget"), + (MOD_QTWIDGETS, "QFrame"): _stub("QFrame"), + (MOD_QTWIDGETS, "QToolButton"): _stub("QToolButton"), + (MOD_QTWIDGETS, "QButtonGroup"): _stub("QButtonGroup"), + (MOD_QTWIDGETS, "QSizePolicy"): _stub("QSizePolicy"), } for (mod_name, attr_name), stub_value in _PYSIDE_STUBS.items(): diff --git a/tests/test_discord_rpc.py b/tests/test_discord_rpc.py index a67a761..446b860 100644 --- a/tests/test_discord_rpc.py +++ b/tests/test_discord_rpc.py @@ -132,7 +132,6 @@ class TestDiscordRPC: 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 @@ -140,7 +139,7 @@ class TestDiscordRPC: assert 28 <= diff <= 32 # allow some fuzz def test_stop_disconnects_and_joins(self, rpc): - drpc, mock_presence = rpc + drpc, _ = rpc drpc.start() time.sleep(0.1) drpc.stop() @@ -148,18 +147,18 @@ class TestDiscordRPC: assert drpc._connected is False def test_send_presence_clears_when_no_song(self, rpc): - drpc, mock_presence = rpc + drpc, _mock = rpc drpc.start() time.sleep(0.1) drpc.clear() time.sleep(0.1) - mock_presence.clear.assert_called() + _mock.clear.assert_called() def test_double_start_is_idempotent(self, rpc): drpc, _ = rpc drpc.start() drpc.start() # should not crash - assert True + # If we get here without exception the test passes @mock.patch("discord_rpc.Presence") def test_connect_failure_graceful(self, mock_presence_cls): diff --git a/tests/test_gui_helpers.py b/tests/test_gui_helpers.py index 2ff9d78..70a652f 100644 --- a/tests/test_gui_helpers.py +++ b/tests/test_gui_helpers.py @@ -34,10 +34,6 @@ class TestFmtMs: assert fn(3600_000) == "60:00" assert fn(3661_000) == "61:01" - def test_zero_ms(self): - fn = self._import() - assert fn(0) == "0:00" - def test_large_duration(self): fn = self._import() assert fn(5999_000) == "99:59" diff --git a/tests/test_music_db.py b/tests/test_music_db.py index 1738c06..ea5e1f9 100644 --- a/tests/test_music_db.py +++ b/tests/test_music_db.py @@ -24,14 +24,14 @@ def music_db(): # ── Helper ──────────────────────────────────────────────────────────────────── -_SONG = dict( - video_id="test_video_001", - title="Test Song", - artists=[{"name": "Test Artist", "id": "artist_001"}], - album={"name": "Test Album", "id": "album_001"}, - duration=240, - thumbnail="https://example.com/thumb.jpg", -) +_SONG = { + "video_id": "test_video_001", + "title": "Test Song", + "artists": [{"name": "Test Artist", "id": "artist_001"}], + "album": {"name": "Test Album", "id": "album_001"}, + "duration": 240, + "thumbnail": "https://example.com/thumb.jpg", +} def _record_play(db, **overrides): diff --git a/tests/test_player_helpers.py b/tests/test_player_helpers.py index c973cf9..df44ed6 100644 --- a/tests/test_player_helpers.py +++ b/tests/test_player_helpers.py @@ -239,8 +239,8 @@ class TestStaleFiles: td = tempfile.mkdtemp(prefix="tunetti_test_") monkeypatch.setattr("player._TUNETTI_TMP", td) - with open(os.path.join(td, "vid123.mp3"), "w") as f: - pass # empty + with open(os.path.join(td, "vid123.mp3"), "w") as _f: + pass # empty; _f unused intentionally result = _stale_files("vid123") assert result == []