Added tests for get_playlists and get_playlist_details
This commit is contained in:
1
Pipfile
1
Pipfile
@@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
dataclasses-json = {git = "https://github.com/sumnerevans/dataclasses-json",ref = "cc2eaeb"}
|
||||
docutils = "*"
|
||||
flake8 = "*"
|
||||
flake8-annotations = "*"
|
||||
|
9
Pipfile.lock
generated
9
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "71577f2c34a30d79301fb7a2c0c80e81d8bec18f7f7038b15ef38980f6c7bea7"
|
||||
"sha256": "1b3ed7bc26fc014d648a0fddf4cde814f5ea583a464bf457548326a67825601c"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -418,6 +418,13 @@
|
||||
],
|
||||
"version": "==5.1"
|
||||
},
|
||||
"dataclasses-json": {
|
||||
"hashes": [
|
||||
"sha256:65ac9ae2f7ec152ee01bf42c8c024736d4cd6f6fb761502dec92bd553931e3d9",
|
||||
"sha256:dbb53ebbac30ef45f44f5f436b21bd5726a80a14e1a193958864229100271372"
|
||||
],
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
|
||||
|
2
setup.py
2
setup.py
@@ -56,7 +56,7 @@ setup(
|
||||
},
|
||||
install_requires=[
|
||||
'bottle',
|
||||
'dataclasses-json',
|
||||
'dataclasses-json @ git+https://github.com/sumnerevans/dataclasses-json@cc2eaeb#egg=dataclasses-json',
|
||||
'deepdiff',
|
||||
'Deprecated',
|
||||
'fuzzywuzzy',
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
@@ -53,7 +52,8 @@ class SubsonicAdapter(Adapter):
|
||||
@property
|
||||
def can_service_requests(self) -> bool:
|
||||
try:
|
||||
self._get_json('ping', timeout=2)
|
||||
# Try to ping the server with a timeout of 2 seconds.
|
||||
self._get_json(self._make_url('ping'), timeout=2)
|
||||
return True
|
||||
except Exception:
|
||||
logging.exception(f'Could not connect to {self.hostname}')
|
||||
@@ -98,6 +98,7 @@ class SubsonicAdapter(Adapter):
|
||||
params[k] = int(v.timestamp() * 1000)
|
||||
|
||||
if self._is_mock:
|
||||
logging.debug('Using mock data')
|
||||
return self._get_mock_data()
|
||||
|
||||
result = requests.get(
|
||||
|
@@ -13,20 +13,24 @@ from dataclasses_json import (
|
||||
DataClassJsonMixin,
|
||||
LetterCase,
|
||||
)
|
||||
from dateutil import parser
|
||||
|
||||
from .. import api_objects as SublimeAPI
|
||||
|
||||
dataclasses_json.cfg.global_config.encoders[datetime] = datetime.isoformat
|
||||
dataclasses_json.cfg.global_config.decoders[datetime] = parser.parse
|
||||
dataclasses_json.cfg.global_config.encoders[timedelta] = (
|
||||
timedelta.total_seconds)
|
||||
dataclasses_json.cfg.global_config.decoders[timedelta] = lambda s: timedelta(
|
||||
seconds=s)
|
||||
# Translation map
|
||||
extra_translation_map = {
|
||||
datetime:
|
||||
(lambda s: datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f%z') if s else None),
|
||||
timedelta: (lambda s: timedelta(seconds=s) if s else None),
|
||||
}
|
||||
|
||||
for type_, translation_function in extra_translation_map.items():
|
||||
dataclasses_json.cfg.global_config.decoders[type_] = translation_function
|
||||
dataclasses_json.cfg.global_config.decoders[
|
||||
Optional[type_]] = translation_function
|
||||
|
||||
|
||||
@dataclass_json(letter_case=LetterCase.CAMEL)
|
||||
@dataclass(frozen=True)
|
||||
@dataclass
|
||||
class Child(SublimeAPI.Song):
|
||||
id: str
|
||||
title: str
|
||||
@@ -81,7 +85,8 @@ class Playlist(SublimeAPI.Playlist):
|
||||
class PlaylistWithSongs(SublimeAPI.PlaylistDetails):
|
||||
id: str
|
||||
name: str
|
||||
songs: List[Child] = field(metadata=config(field_name='entry'))
|
||||
songs: List[Child] = field(
|
||||
default_factory=list, metadata=config(field_name='entry'))
|
||||
song_count: int = field(default=0)
|
||||
duration: timedelta = field(default=timedelta())
|
||||
created: Optional[datetime] = None
|
||||
@@ -97,12 +102,12 @@ class PlaylistWithSongs(SublimeAPI.PlaylistDetails):
|
||||
seconds=sum(s.duration for s in self.songs))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass
|
||||
class Playlists(DataClassJsonMixin):
|
||||
playlist: List[Playlist] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@dataclass
|
||||
class Response(DataClassJsonMixin):
|
||||
"""
|
||||
The base Subsonic response object.
|
||||
|
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"subsonic-response": {
|
||||
"status": "ok",
|
||||
"version": "1.15.0",
|
||||
"playlist": {
|
||||
"id": "2",
|
||||
"name": "Christian",
|
||||
"comment": "",
|
||||
"owner": "sumner",
|
||||
"public": true,
|
||||
"songCount": 2,
|
||||
"duration": 470,
|
||||
"created": "2020-03-27T05:38:45.487Z",
|
||||
"changed": "2020-04-09T16:03:26.039Z",
|
||||
"coverArt": "pl-2",
|
||||
"entry": [
|
||||
{
|
||||
"id": "202",
|
||||
"parent": "318",
|
||||
"isDir": false,
|
||||
"title": "What a Beautiful Name",
|
||||
"album": "What a Beautiful Name - Single",
|
||||
"artist": "Hillsong Worship",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "318",
|
||||
"size": 8381640,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 238,
|
||||
"bitRate": 256,
|
||||
"path": "Hillsong Worship/What a Beautiful Name - Single/01 What a Beautiful Name.m4a",
|
||||
"isVideo": false,
|
||||
"playCount": 20,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:17:07.000Z",
|
||||
"albumId": "48",
|
||||
"artistId": "38",
|
||||
"type": "music"
|
||||
},
|
||||
{
|
||||
"id": "203",
|
||||
"parent": "319",
|
||||
"isDir": false,
|
||||
"title": "Great Are You Lord",
|
||||
"album": "Great Are You Lord EP",
|
||||
"artist": "one sonic society",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "319",
|
||||
"size": 8245813,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 232,
|
||||
"bitRate": 256,
|
||||
"path": "one sonic society/Great Are You Lord EP/01 Great Are You Lord.m4a",
|
||||
"isVideo": false,
|
||||
"playCount": 18,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:32:42.000Z",
|
||||
"albumId": "10",
|
||||
"artistId": "8",
|
||||
"type": "music"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"subsonic-response": {
|
||||
"status": "ok",
|
||||
"version": "1.15.0",
|
||||
"playlist": {
|
||||
"id": "2",
|
||||
"name": "Christian",
|
||||
"comment": "",
|
||||
"owner": "sumner",
|
||||
"public": true,
|
||||
"created": "2020-03-27T05:38:45.487Z",
|
||||
"changed": "2020-04-09T16:03:26.039Z",
|
||||
"coverArt": "pl-2",
|
||||
"entry": [
|
||||
{
|
||||
"id": "202",
|
||||
"parent": "318",
|
||||
"isDir": false,
|
||||
"title": "What a Beautiful Name",
|
||||
"album": "What a Beautiful Name - Single",
|
||||
"artist": "Hillsong Worship",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "318",
|
||||
"size": 8381640,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 238,
|
||||
"bitRate": 256,
|
||||
"path": "Hillsong Worship/What a Beautiful Name - Single/01 What a Beautiful Name.m4a",
|
||||
"isVideo": false,
|
||||
"playCount": 20,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:17:07.000Z",
|
||||
"albumId": "48",
|
||||
"artistId": "38",
|
||||
"type": "music"
|
||||
},
|
||||
{
|
||||
"id": "203",
|
||||
"parent": "319",
|
||||
"isDir": false,
|
||||
"title": "Great Are You Lord",
|
||||
"album": "Great Are You Lord EP",
|
||||
"artist": "one sonic society",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "319",
|
||||
"size": 8245813,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 232,
|
||||
"bitRate": 256,
|
||||
"path": "one sonic society/Great Are You Lord EP/01 Great Are You Lord.m4a",
|
||||
"isVideo": false,
|
||||
"playCount": 18,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:32:42.000Z",
|
||||
"albumId": "10",
|
||||
"artistId": "8",
|
||||
"type": "music"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
34
tests/adapter_tests/mock_data/get_playlists-airsonic.json
Normal file
34
tests/adapter_tests/mock_data/get_playlists-airsonic.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"subsonic-response": {
|
||||
"status": "ok",
|
||||
"version": "1.15.0",
|
||||
"playlists": {
|
||||
"playlist": [
|
||||
{
|
||||
"id": "2",
|
||||
"name": "Test",
|
||||
"comment": "Foo",
|
||||
"owner": "foo",
|
||||
"public": true,
|
||||
"songCount": 132,
|
||||
"duration": 33072,
|
||||
"created": "2020-03-27T05:38:45.000Z",
|
||||
"changed": "2020-04-09T16:03:26.000Z",
|
||||
"coverArt": "pl-2"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "Bar",
|
||||
"comment": "",
|
||||
"owner": "foo",
|
||||
"public": false,
|
||||
"songCount": 23,
|
||||
"duration": 847,
|
||||
"created": "2020-03-27T05:39:04.000Z",
|
||||
"changed": "2020-03-27T05:45:23.000Z",
|
||||
"coverArt": "pl-3"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
3
tests/adapter_tests/mock_data/ping_failed-.json
Normal file
3
tests/adapter_tests/mock_data/ping_failed-.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"I'm json from another service"
|
||||
}
|
7
tests/adapter_tests/mock_data/ping_failed-404.html
Normal file
7
tests/adapter_tests/mock_data/ping_failed-404.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><title>400 Not Found</title></head>
|
||||
<body>
|
||||
<center><h1>400 Not Found</h1></center>
|
||||
<hr><center>nginx</center>
|
||||
</body>
|
||||
</html>
|
@@ -1,231 +0,0 @@
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from dateutil import parser
|
||||
|
||||
from sublime.adapters.subsonic import (
|
||||
SubsonicAdapter, api_objects as SubsonicAPI)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def subsonic_adapter(tmp_path: Path):
|
||||
adapter = SubsonicAdapter(
|
||||
{
|
||||
'server_address': 'http://localhost:4533',
|
||||
'username': 'test',
|
||||
'password': 'testpass',
|
||||
},
|
||||
tmp_path,
|
||||
)
|
||||
adapter._is_mock = True
|
||||
yield adapter
|
||||
|
||||
|
||||
def mock_json(**obj: Any) -> str:
|
||||
return json.dumps(
|
||||
{
|
||||
'subsonic-response': {
|
||||
'status': 'ok',
|
||||
'version': '1.15.0',
|
||||
**obj,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
def camel_to_snake(name: str) -> str:
|
||||
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
|
||||
|
||||
|
||||
def test_request_making_methods(subsonic_adapter: SubsonicAdapter):
|
||||
expected = {
|
||||
'u': 'test',
|
||||
'p': 'testpass',
|
||||
'c': 'Sublime Music',
|
||||
'f': 'json',
|
||||
'v': '1.15.0',
|
||||
}
|
||||
assert (
|
||||
sorted(expected.items()) == sorted(
|
||||
subsonic_adapter._get_params().items()))
|
||||
|
||||
assert subsonic_adapter._make_url(
|
||||
'foo') == 'http://localhost:4533/rest/foo.view'
|
||||
|
||||
|
||||
def test_can_service_requests(subsonic_adapter: SubsonicAdapter):
|
||||
# Mock a connection error
|
||||
subsonic_adapter._set_mock_data(Exception())
|
||||
assert subsonic_adapter.can_service_requests is False
|
||||
|
||||
# Simulate some sort of ping error
|
||||
subsonic_adapter._set_mock_data(
|
||||
mock_json(
|
||||
status='failed',
|
||||
error={
|
||||
'code': '1',
|
||||
'message': 'Test message',
|
||||
},
|
||||
))
|
||||
assert subsonic_adapter.can_service_requests is False
|
||||
|
||||
# Simulate valid ping
|
||||
subsonic_adapter._set_mock_data(mock_json())
|
||||
assert subsonic_adapter.can_service_requests is True
|
||||
|
||||
|
||||
def test_get_playlists(subsonic_adapter: SubsonicAdapter):
|
||||
playlists = [
|
||||
{
|
||||
"id": "6",
|
||||
"name": "Playlist 1",
|
||||
"comment": "Foo",
|
||||
"owner": "test",
|
||||
"public": True,
|
||||
"songCount": 2,
|
||||
"duration": 625,
|
||||
"created": "2020-03-27T05:39:35.188Z",
|
||||
"changed": "2020-04-08T00:07:01.748Z",
|
||||
"coverArt": "pl-6"
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"name": "Playlist 2",
|
||||
"comment": "",
|
||||
"owner": "test",
|
||||
"public": True,
|
||||
"songCount": 3,
|
||||
"duration": 952,
|
||||
"created": "2020-03-27T05:39:43.327Z",
|
||||
"changed": "2020-03-27T05:44:37.275Z",
|
||||
"coverArt": "pl-7"
|
||||
},
|
||||
]
|
||||
subsonic_adapter._set_mock_data(
|
||||
mock_json(playlists={
|
||||
'playlist': playlists,
|
||||
}))
|
||||
|
||||
expected = [
|
||||
SubsonicAPI.Playlist(
|
||||
id='6',
|
||||
name='Playlist 1',
|
||||
song_count=2,
|
||||
duration=timedelta(seconds=625),
|
||||
created=parser.parse('2020-03-27T05:39:35.188Z'),
|
||||
changed=parser.parse('2020-04-08T00:07:01.748Z'),
|
||||
comment='Foo',
|
||||
owner='test',
|
||||
public=True,
|
||||
cover_art='pl-6',
|
||||
),
|
||||
SubsonicAPI.Playlist(
|
||||
id='7',
|
||||
name='Playlist 2',
|
||||
song_count=3,
|
||||
duration=timedelta(seconds=952),
|
||||
created=parser.parse('2020-03-27T05:39:43.327Z'),
|
||||
changed=parser.parse('2020-03-27T05:44:37.275Z'),
|
||||
comment='',
|
||||
owner='test',
|
||||
public=True,
|
||||
cover_art='pl-7',
|
||||
),
|
||||
]
|
||||
assert subsonic_adapter.get_playlists() == expected
|
||||
|
||||
# When playlists is null, expect an empty list.
|
||||
subsonic_adapter._set_mock_data(mock_json())
|
||||
assert subsonic_adapter.get_playlists() == []
|
||||
|
||||
|
||||
def test_get_playlist_details(subsonic_adapter: SubsonicAdapter):
|
||||
playlist = {
|
||||
"id":
|
||||
"6",
|
||||
"name":
|
||||
"Playlist 1",
|
||||
"comment":
|
||||
"Foo",
|
||||
"owner":
|
||||
"test",
|
||||
"public":
|
||||
True,
|
||||
"songCount":
|
||||
2,
|
||||
"duration":
|
||||
952,
|
||||
"created":
|
||||
"2020-03-27T05:39:43.327Z",
|
||||
"changed":
|
||||
"2020-03-27T05:44:37.275Z",
|
||||
"coverArt":
|
||||
"pl-6",
|
||||
"entry": [
|
||||
{
|
||||
"id": "202",
|
||||
"parent": "318",
|
||||
"isDir": False,
|
||||
"title": "What a Beautiful Name",
|
||||
"album": "What a Beautiful Name - Single",
|
||||
"artist": "Hillsong Worship",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "318",
|
||||
"size": 8381640,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 238,
|
||||
"bitRate": 256,
|
||||
"path":
|
||||
"Hillsong Worship/What a Beautiful Name - Single/01 What a Beautiful Name.m4a",
|
||||
"isVideo": False,
|
||||
"playCount": 20,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:17:07.000Z",
|
||||
"albumId": "48",
|
||||
"artistId": "38",
|
||||
"type": "music"
|
||||
},
|
||||
{
|
||||
"id": "203",
|
||||
"parent": "319",
|
||||
"isDir": False,
|
||||
"title": "Great Are You Lord",
|
||||
"album": "Great Are You Lord EP",
|
||||
"artist": "one sonic society",
|
||||
"track": 1,
|
||||
"year": 2016,
|
||||
"genre": "Christian & Gospel",
|
||||
"coverArt": "319",
|
||||
"size": 8245813,
|
||||
"contentType": "audio/mp4",
|
||||
"suffix": "m4a",
|
||||
"transcodedContentType": "audio/mpeg",
|
||||
"transcodedSuffix": "mp3",
|
||||
"duration": 232,
|
||||
"bitRate": 256,
|
||||
"path":
|
||||
"one sonic society/Great Are You Lord EP/01 Great Are You Lord.m4a",
|
||||
"isVideo": False,
|
||||
"playCount": 18,
|
||||
"discNumber": 1,
|
||||
"created": "2020-03-27T05:32:42.000Z",
|
||||
"albumId": "10",
|
||||
"artistId": "8",
|
||||
"type": "music"
|
||||
},
|
||||
],
|
||||
}
|
||||
subsonic_adapter._set_mock_data(mock_json(playlist=playlist))
|
||||
|
||||
playlist_details = subsonic_adapter.get_playlist_details('6')
|
||||
assert len(playlist_details.songs) == 2
|
178
tests/adapter_tests/subsonic_adapter_tests.py
Normal file
178
tests/adapter_tests/subsonic_adapter_tests.py
Normal file
@@ -0,0 +1,178 @@
|
||||
import importlib
|
||||
import importlib.util
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Generator, Optional, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
from sublime.adapters.subsonic import (
|
||||
api_objects as SubsonicAPI,
|
||||
SubsonicAdapter,
|
||||
)
|
||||
|
||||
MOCK_DATA_FILES = Path(__file__).parent.joinpath('mock_data')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def adapter(tmp_path: Path):
|
||||
adapter = SubsonicAdapter(
|
||||
{
|
||||
'server_address': 'http://subsonic.example.com',
|
||||
'username': 'test',
|
||||
'password': 'testpass',
|
||||
},
|
||||
tmp_path,
|
||||
)
|
||||
adapter._is_mock = True
|
||||
yield adapter
|
||||
|
||||
|
||||
def mock_data_files(
|
||||
request_name: str,
|
||||
mode: str = 'r',
|
||||
) -> Generator[str, 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 mock_json(**obj: Any) -> str:
|
||||
return json.dumps(
|
||||
{
|
||||
'subsonic-response': {
|
||||
'status': 'ok',
|
||||
'version': '1.15.0',
|
||||
**obj,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
def camel_to_snake(name: str) -> str:
|
||||
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
|
||||
|
||||
|
||||
def test_request_making_methods(adapter: SubsonicAdapter):
|
||||
expected = {
|
||||
'u': 'test',
|
||||
'p': 'testpass',
|
||||
'c': 'Sublime Music',
|
||||
'f': 'json',
|
||||
'v': '1.15.0',
|
||||
}
|
||||
assert (sorted(expected.items()) == sorted(adapter._get_params().items()))
|
||||
|
||||
assert adapter._make_url(
|
||||
'foo') == 'http://subsonic.example.com/rest/foo.view'
|
||||
|
||||
|
||||
def test_can_service_requests(adapter: SubsonicAdapter):
|
||||
# Mock a connection error
|
||||
adapter._set_mock_data(Exception())
|
||||
assert adapter.can_service_requests is False
|
||||
|
||||
# Simulate some sort of ping error
|
||||
for filename, data in mock_data_files('ping_failed'):
|
||||
logging.info(filename)
|
||||
logging.debug(data)
|
||||
adapter._set_mock_data(data)
|
||||
assert adapter.can_service_requests is False
|
||||
|
||||
# Simulate valid ping
|
||||
adapter._set_mock_data(mock_json())
|
||||
assert adapter.can_service_requests is True
|
||||
|
||||
|
||||
def test_get_playlists(adapter: SubsonicAdapter):
|
||||
expected = [
|
||||
SubsonicAPI.Playlist(
|
||||
id='2',
|
||||
name='Test',
|
||||
song_count=132,
|
||||
duration=timedelta(seconds=33072),
|
||||
created=datetime(2020, 3, 27, 5, 38, 45, 0, tzinfo=timezone.utc),
|
||||
changed=datetime(2020, 4, 9, 16, 3, 26, 0, tzinfo=timezone.utc),
|
||||
comment='Foo',
|
||||
owner='foo',
|
||||
public=True,
|
||||
cover_art='pl-2',
|
||||
),
|
||||
SubsonicAPI.Playlist(
|
||||
id='3',
|
||||
name='Bar',
|
||||
song_count=23,
|
||||
duration=timedelta(seconds=847),
|
||||
created=datetime(2020, 3, 27, 5, 39, 4, 0, tzinfo=timezone.utc),
|
||||
changed=datetime(2020, 3, 27, 5, 45, 23, 0, tzinfo=timezone.utc),
|
||||
comment='',
|
||||
owner='foo',
|
||||
public=False,
|
||||
cover_art='pl-3',
|
||||
),
|
||||
]
|
||||
|
||||
for filename, data in mock_data_files('get_playlists'):
|
||||
logging.info(filename)
|
||||
logging.debug(data)
|
||||
adapter._set_mock_data(data)
|
||||
assert adapter.get_playlists() == expected
|
||||
|
||||
# When playlists is null, expect an empty list.
|
||||
adapter._set_mock_data(mock_json())
|
||||
assert adapter.get_playlists() == []
|
||||
|
||||
|
||||
def test_get_playlist_details(adapter: SubsonicAdapter):
|
||||
for filename, data in mock_data_files('get_playlist_details'):
|
||||
logging.info(filename)
|
||||
logging.debug(data)
|
||||
adapter._set_mock_data(data)
|
||||
|
||||
playlist_details = adapter.get_playlist_details('2')
|
||||
|
||||
# Make sure that the song count is correct even if it's not provided.
|
||||
# Old versions of Subsonic don't have these properties.
|
||||
assert len(playlist_details.songs) == 2
|
||||
assert playlist_details.duration == timedelta(seconds=470)
|
||||
|
||||
# Make sure that at least the first song got decoded properly.
|
||||
assert playlist_details.songs[0] == SubsonicAPI.Child(
|
||||
id='202',
|
||||
parent='318',
|
||||
title='What a Beautiful Name',
|
||||
album='What a Beautiful Name - Single',
|
||||
artist='Hillsong Worship',
|
||||
track=1,
|
||||
year=2016,
|
||||
genre='Christian & Gospel',
|
||||
cover_art='318',
|
||||
size=8381640,
|
||||
content_type="audio/mp4",
|
||||
suffix="m4a",
|
||||
transcoded_content_type="audio/mpeg",
|
||||
transcoded_suffix="mp3",
|
||||
duration=238,
|
||||
bit_rate=256,
|
||||
path='/'.join(
|
||||
(
|
||||
'Hillsong Worship',
|
||||
'What a Beautiful Name - Single',
|
||||
'01 What a Beautiful Name.m4a',
|
||||
)),
|
||||
is_video=False,
|
||||
play_count=20,
|
||||
disc_number=1,
|
||||
created=datetime(2020, 3, 27, 5, 17, 7, tzinfo=timezone.utc),
|
||||
album_id="48",
|
||||
artist_id="38",
|
||||
type=SubsonicAPI.SublimeAPI.MediaType.MUSIC,
|
||||
)
|
Reference in New Issue
Block a user