231 lines
6.8 KiB
Python
231 lines
6.8 KiB
Python
from dataclasses import asdict
|
|
from datetime import timedelta
|
|
from pathlib import Path
|
|
from typing import Any, Generator, Tuple
|
|
|
|
import pytest
|
|
|
|
from sublime.adapters import CacheMissError
|
|
from sublime.adapters.filesystem import FilesystemAdapter
|
|
from sublime.adapters.subsonic import api_objects as SubsonicAPI
|
|
|
|
MOCK_DATA_FILES = Path(__file__).parent.joinpath("mock_data")
|
|
|
|
|
|
@pytest.fixture
|
|
def adapter(tmp_path: Path):
|
|
adapter = FilesystemAdapter({}, tmp_path)
|
|
yield adapter
|
|
adapter.shutdown()
|
|
|
|
|
|
@pytest.fixture
|
|
def cache_adapter(tmp_path: Path):
|
|
adapter = FilesystemAdapter({}, tmp_path, is_cache=True)
|
|
yield adapter
|
|
adapter.shutdown()
|
|
|
|
|
|
def mock_data_files(
|
|
request_name: str, mode: str = "r",
|
|
) -> Generator[Tuple[Path, Any], None, None]:
|
|
"""
|
|
Yields all of the files in the mock_data directory that start with ``request_name``.
|
|
"""
|
|
for file in MOCK_DATA_FILES.iterdir():
|
|
if file.name.split("-")[0] in request_name:
|
|
with open(file, mode) as f:
|
|
yield file, f.read()
|
|
|
|
|
|
def test_caching_get_playlists(cache_adapter: FilesystemAdapter):
|
|
with pytest.raises(CacheMissError):
|
|
cache_adapter.get_playlists()
|
|
|
|
# Ingest an empty list (for example, no playlists added yet to server).
|
|
cache_adapter.ingest_new_data(FilesystemAdapter.FunctionNames.GET_PLAYLISTS, (), [])
|
|
|
|
# After the first cache miss of get_playlists, even if an empty list is
|
|
# returned, the next one should not be a cache miss.
|
|
cache_adapter.get_playlists()
|
|
|
|
# Ingest two playlists.
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLISTS,
|
|
(),
|
|
[
|
|
SubsonicAPI.Playlist("1", "test1", comment="comment"),
|
|
SubsonicAPI.Playlist("2", "test2"),
|
|
],
|
|
)
|
|
|
|
playlists = cache_adapter.get_playlists()
|
|
assert len(playlists) == 2
|
|
assert (playlists[0].id, playlists[0].name, playlists[0].comment) == (
|
|
"1",
|
|
"test1",
|
|
"comment",
|
|
)
|
|
assert (playlists[1].id, playlists[1].name) == ("2", "test2")
|
|
|
|
|
|
def test_no_caching_get_playlists(adapter: FilesystemAdapter):
|
|
adapter.get_playlists()
|
|
|
|
# TODO: Create a playlist (that should be allowed only if this is acting as
|
|
# a ground truth adapter)
|
|
# cache_adapter.create_playlist()
|
|
|
|
adapter.get_playlists()
|
|
# TODO: verify playlist
|
|
|
|
|
|
def test_caching_get_playlist_details(cache_adapter: FilesystemAdapter):
|
|
with pytest.raises(CacheMissError):
|
|
cache_adapter.get_playlist_details("1")
|
|
|
|
# Simulate the playlist being retrieved from Subsonic.
|
|
songs = [
|
|
SubsonicAPI.Song(
|
|
"2",
|
|
"Song 2",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=20.8),
|
|
path="/foo/song2.mp3",
|
|
),
|
|
SubsonicAPI.Song(
|
|
"1",
|
|
"Song 1",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=10.2),
|
|
path="/foo/song1.mp3",
|
|
),
|
|
]
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLIST_DETAILS,
|
|
("1",),
|
|
SubsonicAPI.PlaylistWithSongs("1", "test1", songs=songs),
|
|
)
|
|
|
|
playlist = cache_adapter.get_playlist_details("1")
|
|
assert playlist.id == "1"
|
|
assert playlist.name == "test1"
|
|
assert playlist.song_count == 2
|
|
assert playlist.duration == timedelta(seconds=31)
|
|
for actual, song in zip(playlist.songs, songs):
|
|
for k, v in asdict(song).items():
|
|
assert getattr(actual, k, None) == v
|
|
|
|
# "Force refresh" the playlist
|
|
songs = [
|
|
SubsonicAPI.Song(
|
|
"3",
|
|
"Song 3",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=10.2),
|
|
path="/foo/song3.mp3",
|
|
),
|
|
SubsonicAPI.Song(
|
|
"1",
|
|
"Song 1",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=21.8),
|
|
path="/foo/song1.mp3",
|
|
),
|
|
SubsonicAPI.Song(
|
|
"1",
|
|
"Song 1",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=21.8),
|
|
path="/foo/song1.mp3",
|
|
),
|
|
]
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLIST_DETAILS,
|
|
("1",),
|
|
SubsonicAPI.PlaylistWithSongs("1", "foo", songs=songs),
|
|
)
|
|
|
|
playlist = cache_adapter.get_playlist_details("1")
|
|
assert playlist.id == "1"
|
|
assert playlist.name == "foo"
|
|
assert playlist.song_count == 3
|
|
assert playlist.duration == timedelta(seconds=53.8)
|
|
for actual, song in zip(playlist.songs, songs):
|
|
for k, v in asdict(song).items():
|
|
assert getattr(actual, k, None) == v
|
|
|
|
with pytest.raises(CacheMissError):
|
|
cache_adapter.get_playlist_details("2")
|
|
|
|
|
|
def test_no_caching_get_playlist_details(adapter: FilesystemAdapter):
|
|
with pytest.raises(Exception):
|
|
adapter.get_playlist_details("1")
|
|
|
|
# TODO: Create a playlist (that should be allowed only if this is acting as
|
|
# a ground truth adapter)
|
|
# cache_adapter.create_playlist()
|
|
|
|
# adapter.get_playlist_details('1')
|
|
# TODO: verify playlist details
|
|
|
|
|
|
def test_caching_get_playlist_then_details(cache_adapter: FilesystemAdapter):
|
|
# Ingest a list of playlists (like the sidebar, without songs)
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLISTS,
|
|
(),
|
|
[SubsonicAPI.Playlist("1", "test1"), SubsonicAPI.Playlist("2", "test2")],
|
|
)
|
|
|
|
# Trying to get playlist details should generate a cache miss, but should
|
|
# include the data that we know about.
|
|
try:
|
|
cache_adapter.get_playlist_details("1")
|
|
assert 0, "DID NOT raise CacheMissError"
|
|
except CacheMissError as e:
|
|
assert e.partial_data
|
|
assert e.partial_data.id == "1"
|
|
assert e.partial_data.name == "test1"
|
|
|
|
# Simulate getting playlist details for id=1, then id=2
|
|
songs = [
|
|
SubsonicAPI.Song(
|
|
"3",
|
|
"Song 3",
|
|
parent="foo",
|
|
album="foo",
|
|
artist="foo",
|
|
duration=timedelta(seconds=10.2),
|
|
path="/foo/song3.mp3",
|
|
),
|
|
]
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLIST_DETAILS,
|
|
("1",),
|
|
SubsonicAPI.PlaylistWithSongs("1", "test1", songs=songs),
|
|
)
|
|
|
|
cache_adapter.ingest_new_data(
|
|
FilesystemAdapter.FunctionNames.GET_PLAYLIST_DETAILS,
|
|
("2",),
|
|
SubsonicAPI.PlaylistWithSongs("2", "test2", songs=songs),
|
|
)
|
|
|
|
# Going back and getting playlist details for the first one should not
|
|
# cache miss.
|
|
playlist = cache_adapter.get_playlist_details("1")
|
|
assert playlist.id == "1"
|
|
assert playlist.name == "test1"
|