|
@@ -0,0 +1,259 @@
|
|
|
|
|
+"""
|
|
|
|
|
+Unit tests for playlist_manager CRUD operations.
|
|
|
|
|
+
|
|
|
|
|
+Tests the core playlist management functions:
|
|
|
|
|
+- Loading playlists from file
|
|
|
|
|
+- Creating playlists
|
|
|
|
|
+- Getting playlists
|
|
|
|
|
+- Modifying playlists
|
|
|
|
|
+- Deleting playlists
|
|
|
|
|
+- Listing playlists
|
|
|
|
|
+- Renaming playlists
|
|
|
|
|
+"""
|
|
|
|
|
+import json
|
|
|
|
|
+import pytest
|
|
|
|
|
+from unittest.mock import patch, MagicMock
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class TestPlaylistManagerCRUD:
|
|
|
|
|
+ """Tests for playlist CRUD operations."""
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.fixture
|
|
|
|
|
+ def playlists_file(self, tmp_path):
|
|
|
|
|
+ """Create a temporary playlists.json file."""
|
|
|
|
|
+ file_path = tmp_path / "playlists.json"
|
|
|
|
|
+ file_path.write_text("{}")
|
|
|
|
|
+ return str(file_path)
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.fixture
|
|
|
|
|
+ def playlist_manager_patched(self, playlists_file):
|
|
|
|
|
+ """Patch PLAYLISTS_FILE to use temporary file."""
|
|
|
|
|
+ with patch("modules.core.playlist_manager.PLAYLISTS_FILE", playlists_file):
|
|
|
|
|
+ # Need to re-import to get patched version
|
|
|
|
|
+ from modules.core import playlist_manager
|
|
|
|
|
+ yield playlist_manager
|
|
|
|
|
+
|
|
|
|
|
+ def test_load_playlists_empty_file(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test loading playlists from an empty file returns empty dict."""
|
|
|
|
|
+ result = playlist_manager_patched.load_playlists()
|
|
|
|
|
+ assert result == {}
|
|
|
|
|
+
|
|
|
|
|
+ def test_load_playlists_with_data(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test loading playlists with existing data."""
|
|
|
|
|
+ # Write some data to the file
|
|
|
|
|
+ with open(playlists_file, "w") as f:
|
|
|
|
|
+ json.dump({"my_playlist": ["pattern1.thr", "pattern2.thr"]}, f)
|
|
|
|
|
+
|
|
|
|
|
+ result = playlist_manager_patched.load_playlists()
|
|
|
|
|
+
|
|
|
|
|
+ assert "my_playlist" in result
|
|
|
|
|
+ assert result["my_playlist"] == ["pattern1.thr", "pattern2.thr"]
|
|
|
|
|
+
|
|
|
|
|
+ def test_create_playlist(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test creating a new playlist."""
|
|
|
|
|
+ files = ["circle.thr", "spiral.thr"]
|
|
|
|
|
+
|
|
|
|
|
+ result = playlist_manager_patched.create_playlist("test_playlist", files)
|
|
|
|
|
+
|
|
|
|
|
+ assert result is True
|
|
|
|
|
+
|
|
|
|
|
+ # Verify it was saved
|
|
|
|
|
+ playlists = playlist_manager_patched.load_playlists()
|
|
|
|
|
+ assert "test_playlist" in playlists
|
|
|
|
|
+ assert playlists["test_playlist"] == files
|
|
|
|
|
+
|
|
|
|
|
+ def test_create_playlist_overwrites_existing(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test creating a playlist with existing name overwrites it."""
|
|
|
|
|
+ # Create initial playlist
|
|
|
|
|
+ playlist_manager_patched.create_playlist("test_playlist", ["old.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ # Create again with same name
|
|
|
|
|
+ playlist_manager_patched.create_playlist("test_playlist", ["new.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ playlists = playlist_manager_patched.load_playlists()
|
|
|
|
|
+ assert playlists["test_playlist"] == ["new.thr"]
|
|
|
|
|
+
|
|
|
|
|
+ def test_get_playlist_exists(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test getting an existing playlist."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["a.thr", "b.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ result = playlist_manager_patched.get_playlist("my_playlist")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is not None
|
|
|
|
|
+ assert result["name"] == "my_playlist"
|
|
|
|
|
+ assert result["files"] == ["a.thr", "b.thr"]
|
|
|
|
|
+
|
|
|
|
|
+ def test_get_playlist_not_found(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test getting a non-existent playlist returns None."""
|
|
|
|
|
+ result = playlist_manager_patched.get_playlist("nonexistent")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is None
|
|
|
|
|
+
|
|
|
|
|
+ def test_modify_playlist(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test modifying an existing playlist."""
|
|
|
|
|
+ # Create initial playlist
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["old.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ # Modify it
|
|
|
|
|
+ new_files = ["new1.thr", "new2.thr", "new3.thr"]
|
|
|
|
|
+ result = playlist_manager_patched.modify_playlist("my_playlist", new_files)
|
|
|
|
|
+
|
|
|
|
|
+ assert result is True
|
|
|
|
|
+
|
|
|
|
|
+ # Verify changes
|
|
|
|
|
+ playlist = playlist_manager_patched.get_playlist("my_playlist")
|
|
|
|
|
+ assert playlist["files"] == new_files
|
|
|
|
|
+
|
|
|
|
|
+ def test_delete_playlist(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test deleting a playlist."""
|
|
|
|
|
+ # Create a playlist
|
|
|
|
|
+ playlist_manager_patched.create_playlist("to_delete", ["pattern.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ # Delete it
|
|
|
|
|
+ result = playlist_manager_patched.delete_playlist("to_delete")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is True
|
|
|
|
|
+
|
|
|
|
|
+ # Verify it's gone
|
|
|
|
|
+ playlist = playlist_manager_patched.get_playlist("to_delete")
|
|
|
|
|
+ assert playlist is None
|
|
|
|
|
+
|
|
|
|
|
+ def test_delete_playlist_not_found(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test deleting a non-existent playlist returns False."""
|
|
|
|
|
+ result = playlist_manager_patched.delete_playlist("nonexistent")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is False
|
|
|
|
|
+
|
|
|
|
|
+ def test_list_all_playlists(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test listing all playlist names."""
|
|
|
|
|
+ # Create multiple playlists
|
|
|
|
|
+ playlist_manager_patched.create_playlist("playlist1", ["a.thr"])
|
|
|
|
|
+ playlist_manager_patched.create_playlist("playlist2", ["b.thr"])
|
|
|
|
|
+ playlist_manager_patched.create_playlist("playlist3", ["c.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ result = playlist_manager_patched.list_all_playlists()
|
|
|
|
|
+
|
|
|
|
|
+ assert len(result) == 3
|
|
|
|
|
+ assert "playlist1" in result
|
|
|
|
|
+ assert "playlist2" in result
|
|
|
|
|
+ assert "playlist3" in result
|
|
|
|
|
+
|
|
|
|
|
+ def test_list_all_playlists_empty(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test listing playlists when none exist."""
|
|
|
|
|
+ result = playlist_manager_patched.list_all_playlists()
|
|
|
|
|
+
|
|
|
|
|
+ assert result == []
|
|
|
|
|
+
|
|
|
|
|
+ def test_add_to_playlist(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test adding a pattern to an existing playlist."""
|
|
|
|
|
+ # Create playlist
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["existing.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ # Add pattern
|
|
|
|
|
+ result = playlist_manager_patched.add_to_playlist("my_playlist", "new_pattern.thr")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is True
|
|
|
|
|
+
|
|
|
|
|
+ # Verify
|
|
|
|
|
+ playlist = playlist_manager_patched.get_playlist("my_playlist")
|
|
|
|
|
+ assert "new_pattern.thr" in playlist["files"]
|
|
|
|
|
+ assert len(playlist["files"]) == 2
|
|
|
|
|
+
|
|
|
|
|
+ def test_add_to_playlist_not_found(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test adding to a non-existent playlist returns False."""
|
|
|
|
|
+ result = playlist_manager_patched.add_to_playlist("nonexistent", "pattern.thr")
|
|
|
|
|
+
|
|
|
|
|
+ assert result is False
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class TestPlaylistRename:
|
|
|
|
|
+ """Tests for playlist rename functionality."""
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.fixture
|
|
|
|
|
+ def playlists_file(self, tmp_path):
|
|
|
|
|
+ """Create a temporary playlists.json file."""
|
|
|
|
|
+ file_path = tmp_path / "playlists.json"
|
|
|
|
|
+ file_path.write_text("{}")
|
|
|
|
|
+ return str(file_path)
|
|
|
|
|
+
|
|
|
|
|
+ @pytest.fixture
|
|
|
|
|
+ def playlist_manager_patched(self, playlists_file):
|
|
|
|
|
+ """Patch PLAYLISTS_FILE to use temporary file."""
|
|
|
|
|
+ with patch("modules.core.playlist_manager.PLAYLISTS_FILE", playlists_file):
|
|
|
|
|
+ from modules.core import playlist_manager
|
|
|
|
|
+ yield playlist_manager
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_success(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test successfully renaming a playlist."""
|
|
|
|
|
+ # Create initial playlist
|
|
|
|
|
+ playlist_manager_patched.create_playlist("old_name", ["a.thr", "b.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ # Rename it
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("old_name", "new_name")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is True
|
|
|
|
|
+ assert "new_name" in message
|
|
|
|
|
+
|
|
|
|
|
+ # Verify old name is gone
|
|
|
|
|
+ assert playlist_manager_patched.get_playlist("old_name") is None
|
|
|
|
|
+
|
|
|
|
|
+ # Verify new name exists with same files
|
|
|
|
|
+ new_playlist = playlist_manager_patched.get_playlist("new_name")
|
|
|
|
|
+ assert new_playlist is not None
|
|
|
|
|
+ assert new_playlist["files"] == ["a.thr", "b.thr"]
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_not_found(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming a non-existent playlist."""
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("nonexistent", "new_name")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is False
|
|
|
|
|
+ assert "not found" in message.lower()
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_empty_name(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming with empty name fails."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["a.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("my_playlist", "")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is False
|
|
|
|
|
+ assert "empty" in message.lower()
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_whitespace_name(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming with whitespace-only name fails."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["a.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("my_playlist", " ")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is False
|
|
|
|
|
+ assert "empty" in message.lower()
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_same_name(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming to the same name succeeds with unchanged message."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("my_playlist", ["a.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("my_playlist", "my_playlist")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is True
|
|
|
|
|
+ assert "unchanged" in message.lower()
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_name_exists(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming to an existing playlist name fails."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("playlist1", ["a.thr"])
|
|
|
|
|
+ playlist_manager_patched.create_playlist("playlist2", ["b.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("playlist1", "playlist2")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is False
|
|
|
|
|
+ assert "already exists" in message.lower()
|
|
|
|
|
+
|
|
|
|
|
+ def test_rename_playlist_trims_whitespace(self, playlists_file, playlist_manager_patched):
|
|
|
|
|
+ """Test renaming trims whitespace from new name."""
|
|
|
|
|
+ playlist_manager_patched.create_playlist("old_name", ["a.thr"])
|
|
|
|
|
+
|
|
|
|
|
+ success, message = playlist_manager_patched.rename_playlist("old_name", " new_name ")
|
|
|
|
|
+
|
|
|
|
|
+ assert success is True
|
|
|
|
|
+
|
|
|
|
|
+ # Verify trimmed name is used
|
|
|
|
|
+ assert playlist_manager_patched.get_playlist("new_name") is not None
|
|
|
|
|
+ assert playlist_manager_patched.get_playlist(" new_name ") is None
|