from datetime import datetime import pytest import time_machine from scrobbles import tasks as scrobbles_tasks def test_locate_pg_dump_returns_which_result(monkeypatch): monkeypatch.setattr( scrobbles_tasks.shutil, "which", lambda name: "/usr/local/bin/pg_dump" ) assert scrobbles_tasks._locate_pg_dump() == "/usr/local/bin/pg_dump" def test_locate_pg_dump_falls_back_to_common_locations(monkeypatch): monkeypatch.setattr(scrobbles_tasks.shutil, "which", lambda name: None) monkeypatch.setattr( scrobbles_tasks.glob, "glob", lambda pattern: ["/usr/local/bin/pg_dump"] ) assert scrobbles_tasks._locate_pg_dump() == "/usr/local/bin/pg_dump" def test_locate_pg_dump_returns_none_when_unfindable(monkeypatch): monkeypatch.setattr(scrobbles_tasks.shutil, "which", lambda name: None) monkeypatch.setattr(scrobbles_tasks.glob, "glob", lambda pattern: []) assert scrobbles_tasks._locate_pg_dump() is None class FakeWebDAVClient: def __init__(self, files=None): self.files = files if files is not None else {} self.uploaded = [] def mkdir(self, path, recursive=False): self.files.setdefault(path, None) def upload_sync(self, remote_path, local_path): with open(local_path, "rb") as f: self.files[remote_path] = f.read() self.uploaded.append(remote_path) def list(self, remote_path, get_info=False): names = [ k for k in self.files if k.startswith(remote_path) and k != remote_path ] if get_info: return [{"path": n} for n in names] return names def clean(self, remote_path): self.files.pop(remote_path, None) @pytest.fixture def fake_webdav_client(): return FakeWebDAVClient() def test_retention_files_to_delete_keeps_recent_and_monthly(): now = datetime(2026, 8, 10) files = [ "vrobbler-backup-2026_08_10.sql.gz", "vrobbler-backup-2026_08_09.sql.gz", "vrobbler-backup-2026_08_01.sql.gz", "vrobbler-backup-2026_07_31.sql.gz", "vrobbler-backup-2026_07_01.sql.gz", "vrobbler-backup-2026_06_15.sql.gz", "vrobbler-backup-2026_06_02.sql.gz", "not-a-backup.txt", ] to_delete = scrobbles_tasks._retention_files_to_delete(files, now) assert "vrobbler-backup-2026_08_10.sql.gz" not in to_delete assert "vrobbler-backup-2026_08_09.sql.gz" not in to_delete assert "vrobbler-backup-2026_08_01.sql.gz" not in to_delete assert "vrobbler-backup-2026_07_31.sql.gz" not in to_delete assert "vrobbler-backup-2026_06_15.sql.gz" not in to_delete assert "not-a-backup.txt" not in to_delete assert "vrobbler-backup-2026_07_01.sql.gz" in to_delete assert "vrobbler-backup-2026_06_02.sql.gz" in to_delete @time_machine.travel(datetime(2026, 8, 10)) def test_run_webdav_cleanup_prunes_old_backups(fake_webdav_client): path = "var/vrobbler-backups/" fake_webdav_client.files = { f"{path}vrobbler-backup-2026_08_10.sql.gz": b"new", f"{path}vrobbler-backup-2026_07_31.sql.gz": b"monthly", f"{path}vrobbler-backup-2026_07_01.sql.gz": b"old", } summary = scrobbles_tasks._run_webdav_cleanup(fake_webdav_client, path) assert summary == "pruned 1 old webdav backup(s)" assert f"{path}vrobbler-backup-2026_08_10.sql.gz" in fake_webdav_client.files assert f"{path}vrobbler-backup-2026_07_31.sql.gz" in fake_webdav_client.files assert f"{path}vrobbler-backup-2026_07_01.sql.gz" not in fake_webdav_client.files def test_run_webdav_cleanup_no_files(fake_webdav_client): assert ( scrobbles_tasks._run_webdav_cleanup(fake_webdav_client, "var/vrobbler-backups/") is None ) def test_run_webdav_cleanup_ignores_unrelated_files(fake_webdav_client): path = "var/vrobbler-backups/" fake_webdav_client.files = { f"{path}notes.txt": b"x", } assert scrobbles_tasks._run_webdav_cleanup(fake_webdav_client, path) is None