since 9a0e3b40 persisted completed downloads in the Downloads view, the Clear Completed button
was hidden for those rows and clear-completed only pruned live session tasks. after a restart
the page filled with persisted completed downloads with no way to clear them.
now Clear Completed clears BOTH:
- live session completed/failed tasks (clear_completed_local, unchanged), AND
- the persisted download-history tail: new clear_completed_download_history() deletes every
library_history event_type='download' row, so the list actually empties and stays empty.
this includes unverified rows (the verification review queue) by design: on a library where
verification never confirmed the imports, ALL completed downloads are 'unverified', so preserving
them made the button a no-op. it only removes HISTORY rows — the actual files and their tracks
entries are untouched, so nothing in the library is lost, only the 'needs verification' flags.
the action confirms first (showConfirmDialog, destructive) and the button now shows whenever any
completed/failed row is present.
3 seam tests (clears all incl unverified; leaves non-download history; empty=0); reconcile +
orphan + JS integrity suites green.
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
"""'Clear Completed' on the Downloads page deletes ALL persisted completed-download
|
|
history (every event_type='download' row), including the unverified review-queue rows
|
|
— the user wants the list emptied, and those unverified rows ARE download-history rows.
|
|
It only removes HISTORY rows; the actual files / `tracks` entries are untouched, so the
|
|
library is never affected — only the 'needs verification' flags. (Clear-button restoration.)"""
|
|
|
|
import sqlite3
|
|
import sys
|
|
import types
|
|
|
|
if "spotipy" not in sys.modules: # match the suite's lightweight stubs
|
|
spotipy = types.ModuleType("spotipy")
|
|
spotipy.Spotify = object
|
|
oauth2 = types.ModuleType("spotipy.oauth2")
|
|
oauth2.SpotifyOAuth = object
|
|
oauth2.SpotifyClientCredentials = object
|
|
spotipy.oauth2 = oauth2
|
|
sys.modules["spotipy"] = spotipy
|
|
sys.modules["spotipy.oauth2"] = oauth2
|
|
|
|
if "config.settings" not in sys.modules:
|
|
config_pkg = types.ModuleType("config")
|
|
settings_mod = types.ModuleType("config.settings")
|
|
|
|
class _DummyConfigManager:
|
|
def get(self, key, default=None):
|
|
return default
|
|
|
|
def get_active_media_server(self):
|
|
return "primary"
|
|
|
|
settings_mod.config_manager = _DummyConfigManager()
|
|
config_pkg.settings = settings_mod
|
|
sys.modules["config"] = config_pkg
|
|
sys.modules["config.settings"] = settings_mod
|
|
|
|
from database.music_database import MusicDatabase # noqa: E402
|
|
|
|
|
|
class _NonClosingConn:
|
|
def __init__(self, real):
|
|
self._real = real
|
|
|
|
def cursor(self):
|
|
return self._real.cursor()
|
|
|
|
def commit(self):
|
|
return self._real.commit()
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
class _InMemoryDB(MusicDatabase):
|
|
def __init__(self):
|
|
self._conn = sqlite3.connect(":memory:")
|
|
self._conn.row_factory = sqlite3.Row
|
|
self._conn.execute(
|
|
"CREATE TABLE library_history ("
|
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, event_type TEXT NOT NULL, "
|
|
"title TEXT, file_path TEXT, verification_status TEXT)")
|
|
|
|
def _get_connection(self):
|
|
return _NonClosingConn(self._conn)
|
|
|
|
def _add(self, event_type, status, title="Song"):
|
|
self._conn.execute(
|
|
"INSERT INTO library_history (event_type, title, verification_status) "
|
|
"VALUES (?, ?, ?)", (event_type, title, status))
|
|
self._conn.commit()
|
|
|
|
def _statuses(self):
|
|
return [r[0] for r in self._conn.execute(
|
|
"SELECT verification_status FROM library_history ORDER BY id").fetchall()]
|
|
|
|
|
|
def test_clears_all_completed_download_rows_including_unverified():
|
|
db = _InMemoryDB()
|
|
db._add("download", "verified")
|
|
db._add("download", "human_verified")
|
|
db._add("download", None) # legacy / unscored completed
|
|
db._add("download", "unverified") # review queue — also cleared (user chose clear-all)
|
|
db._add("download", "force_imported")
|
|
removed = db.clear_completed_download_history()
|
|
assert removed == 5
|
|
assert db._statuses() == []
|
|
|
|
|
|
def test_does_not_touch_non_download_history():
|
|
"""Only event_type='download' rows are the Downloads-page tail; imports etc. stay."""
|
|
db = _InMemoryDB()
|
|
db._add("download", "verified")
|
|
db._add("import", "verified")
|
|
removed = db.clear_completed_download_history()
|
|
assert removed == 1
|
|
# the import row survives
|
|
rows = db._conn.execute("SELECT event_type FROM library_history").fetchall()
|
|
assert [r[0] for r in rows] == ["import"]
|
|
|
|
|
|
def test_empty_history_returns_zero():
|
|
db = _InMemoryDB()
|
|
assert db.clear_completed_download_history() == 0
|