Skip to content

Commit

Permalink
Fix POST support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kutu committed Jun 2, 2024
1 parent 051faf2 commit e1df80b
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 56 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Changelog

## 0.1.0
- PyPI automatic CI update upload test.
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# TODO

## **Fix POST support.**
- [ ] Remove prints.
- [ ] Make the README.md more beautiful.
- [ ] Add tags to PyPI
- [ ] Release the `1.0.0`.
3 changes: 1 addition & 2 deletions src/knuckles/_api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import hashlib
import json
import secrets
from enum import Enum
from typing import Any
Expand Down Expand Up @@ -146,7 +145,7 @@ def raw_request(
case RequestMethod.POST:
return requests.post(
url=f"{self.url}/rest/{endpoint}",
data=json.dumps(self._generate_params(extra_params)),
data=self._generate_params(extra_params),
)

case RequestMethod.GET | _:
Expand Down
2 changes: 1 addition & 1 deletion src/knuckles/_subsonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(
client: str,
use_https: bool = True,
use_token: bool = True,
request_method: RequestMethod = RequestMethod.POST,
request_method: RequestMethod = RequestMethod.GET,
) -> None:
"""Construction method of the Subsonic object used to
interact with the OpenSubsonic REST API.
Expand Down
2 changes: 1 addition & 1 deletion src/knuckles/models/_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def update(self) -> Self:
"""

self._subsonic.playlists.update_playlist(
self.id, self.name, self.comment, self.public
self.id, comment=self.comment, public=self.public
)

return self
Expand Down
26 changes: 19 additions & 7 deletions tests/api/test_media_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
from typing import Any
from urllib import parse

import knuckles
import pytest
import responses
from _pytest.fixtures import FixtureRequest
from knuckles import Subsonic, SubtitlesFileFormat
from knuckles import Subsonic
from responses import Response

from tests.conftest import AddResponses
Expand Down Expand Up @@ -165,7 +166,14 @@ def test_get_captions_without_a_given_filename(

add_responses(get_mock)

download_path = subsonic.media_retrieval.get_captions(video["id"], tmp_path)
if metadata == "vtt_metadata":
subtitle_format = knuckles.SubtitlesFileFormat.VTT
else:
subtitle_format = knuckles.SubtitlesFileFormat.SRT

download_path = subsonic.media_retrieval.get_captions(
video["id"], tmp_path, subtitle_format
)

# Check if the file data has been altered
with open(tmp_path / get_metadata.default_filename, "r") as file:
Expand All @@ -176,10 +184,10 @@ def test_get_captions_without_a_given_filename(

@responses.activate
@pytest.mark.parametrize(
"mock, metadata, file_format",
"mock, metadata",
[
("mock_get_captions_prefer_vtt", "vtt_metadata", SubtitlesFileFormat.VTT),
("mock_get_captions_prefer_srt", "srt_metadata", SubtitlesFileFormat.SRT),
("mock_get_captions_prefer_vtt", "vtt_metadata"),
("mock_get_captions_prefer_srt", "srt_metadata"),
],
)
def test_get_captions_with_a_preferred_file_format(
Expand All @@ -191,16 +199,20 @@ def test_get_captions_with_a_preferred_file_format(
placeholder_data: str,
video: dict[str, Any],
metadata: str,
file_format: SubtitlesFileFormat,
) -> None:
# Retrieve the mocks dynamically as their tests are equal
get_mock: list[Response] = request.getfixturevalue(mock)
get_metadata: FileMetadata = request.getfixturevalue(metadata)

add_responses(get_mock)

if metadata == "vtt_metadata":
subtitle_format = knuckles.SubtitlesFileFormat.VTT
else:
subtitle_format = knuckles.SubtitlesFileFormat.SRT

download_path = subsonic.media_retrieval.get_captions(
video["id"], tmp_path, file_format
video["id"], tmp_path, subtitle_format
)

# Check if the file data has been altered
Expand Down
54 changes: 24 additions & 30 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import json
import urllib.parse
from typing import Any, Callable, Protocol

import knuckles
Expand Down Expand Up @@ -100,50 +100,42 @@ def __call__(


def match_json(
mocked_data: dict[str, Any],
mocked_params: dict[str, Any],
) -> Callable[[requests.PreparedRequest], tuple[bool, str]]:
def inner(
response: requests.PreparedRequest,
) -> tuple[bool, str]:
print(response.body)
print(mocked_data)

if not isinstance(response.body, str):
return (
False,
"The request body wasn't a string",
)

for key, value in json.loads(response.body).items():
print(key, value)
decoded_post_body = urllib.parse.parse_qs(response.body)

if "t" in decoded_post_body:
del decoded_post_body["t"]

if key not in mocked_data:
if "s" in decoded_post_body:
del decoded_post_body["s"]

for key, value in decoded_post_body.items():
if not isinstance(value, list):
continue

# When the passed value is a list different checks should be made,
# this happens when the same parameters is in the request multiple times,
# this happens for example when using
# the parameter "songId" in the endpoint "createPlaylist".
if (
isinstance(value, list)
and isinstance(mocked_data[key], str)
and (
mocked_data[key] in value
# The values inside the list "value" can sometimes be integers,
# so a extra check is needed for them.
or (
str.isdigit(mocked_data[key]) and int(mocked_data[key]) in value
)
)
):
if len(value) != 1:
continue

if str(value) != str(mocked_data[key]):
return (
False,
f"The value '{value}' in the key '{key}' is"
+ f"not equal to '{mocked_data[key]}'",
)
decoded_post_body[key] = value[0]

print(f"{mocked_params=}")
print(f"{decoded_post_body=}")

if mocked_params != decoded_post_body:
return False, (
"Mismatch data between the request POST body and "
+ "the mocked parameters"
)

return True, ""

Expand All @@ -168,6 +160,8 @@ def inner(
if extra_params is not None:
mocked_params.update(extra_params)

print("CC", extra_params)

mocked_data = {"subsonic-response": {**subsonic_response}}
if extra_data is not None:
mocked_data["subsonic-response"].update(extra_data)
Expand Down
5 changes: 3 additions & 2 deletions tests/mocks/jukebox_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ def mock_jukebox_control_status(

@pytest.fixture
def mock_jukebox_control_set(
song: dict[str, Any],
jukebox_status_generator: JukeboxStatusGenerator,
) -> list[Response]:
return jukebox_status_generator("set")
return jukebox_status_generator("set", {"id": song["id"]})


@pytest.fixture
Expand All @@ -91,7 +92,7 @@ def mock_jukebox_control_stop(
def mock_jukebox_control_skip_without_offset(
jukebox_status_generator: JukeboxStatusGenerator,
) -> list[Response]:
return jukebox_status_generator("skip", {"index": 0})
return jukebox_status_generator("skip", {"index": 0, "offset": 0})


@pytest.fixture
Expand Down
1 change: 1 addition & 0 deletions tests/mocks/lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ def mock_get_random_songs(
"getRandomSongs",
{
"genre": genre["value"],
"size": num_of_songs,
"fromYear": from_year,
"toYear": to_year,
"musicFolderId": music_folders[0]["id"],
Expand Down
6 changes: 4 additions & 2 deletions tests/mocks/media_annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ def mock_unstar_artist(


@pytest.fixture
def mock_set_rating_zero(mock_generator: MockGenerator) -> list[Response]:
return mock_generator("setRating", {"rating": 0})
def mock_set_rating_zero(
song: dict[str, Any], mock_generator: MockGenerator
) -> list[Response]:
return mock_generator("setRating", {"id": song["id"], "rating": 0})


@pytest.fixture
Expand Down
4 changes: 2 additions & 2 deletions tests/mocks/media_retrieval.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def mock_get_captions_vtt(
) -> list[Response]:
return mock_download_file_generator(
"getCaptions",
{"id": video["id"]},
{"id": video["id"], "format": "vtt"},
vtt_metadata.content_type,
)

Expand Down Expand Up @@ -123,7 +123,7 @@ def mock_get_captions_srt(
) -> list[Response]:
return mock_download_file_generator(
"getCaptions",
{"id": video["id"]},
{"id": video["id"], "format": "srt"},
srt_metadata.content_type,
)

Expand Down
63 changes: 57 additions & 6 deletions tests/mocks/searching.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ def mock_search_song_non_id3(
) -> list[Response]:
return mock_generator(
"search2",
{"songCount": 1, "songOffset": 0},
{
"query": song["title"],
"songCount": 1,
"songOffset": 0,
"albumCount": 0,
"albumOffset": 0,
"artistCount": 0,
"artistOffset": 0,
"musicFolderId": 1,
},
{"musicFolderId": music_folders[0]["id"], "searchResult2": {"song": [song]}},
)

Expand All @@ -27,7 +36,16 @@ def mock_search_album_non_id3(
) -> list[Response]:
return mock_generator(
"search2",
{"albumCount": 1, "albumOffset": 0},
{
"query": album["title"],
"songCount": 0,
"songOffset": 0,
"albumCount": 1,
"albumOffset": 0,
"artistCount": 0,
"artistOffset": 0,
"musicFolderId": 1,
},
{"musicFolderId": music_folders[0]["id"], "searchResult2": {"album": [album]}},
)

Expand All @@ -40,7 +58,16 @@ def mock_search_artist_non_id3(
) -> list[Response]:
return mock_generator(
"search2",
{"artistCount": 1, "artistOffset": 0},
{
"query": artist["name"],
"songCount": 0,
"songOffset": 0,
"albumCount": 0,
"albumOffset": 0,
"artistCount": 1,
"artistOffset": 0,
"musicFolderId": 1,
},
{
"musicFolderId": music_folders[0]["id"],
"searchResult2": {"artist": [artist]},
Expand All @@ -56,7 +83,15 @@ def mock_search_song(
) -> list[Response]:
return mock_generator(
"search3",
{"songCount": 1, "songOffset": 0},
{
"query": song["title"],
"songCount": 1,
"songOffset": 0,
"albumCount": 0,
"albumOffset": 0,
"artistCount": 0,
"artistOffset": 0,
},
{"musicFolderId": music_folders[0]["id"], "searchResult3": {"song": [song]}},
)

Expand All @@ -69,7 +104,15 @@ def mock_search_album(
) -> list[Response]:
return mock_generator(
"search3",
{"albumCount": 1, "albumOffset": 0},
{
"query": album["title"],
"songCount": 0,
"songOffset": 0,
"albumCount": 1,
"albumOffset": 0,
"artistCount": 0,
"artistOffset": 0,
},
{"musicFolderId": music_folders[0]["id"], "searchResult3": {"album": [album]}},
)

Expand All @@ -82,7 +125,15 @@ def mock_search_artist(
) -> list[Response]:
return mock_generator(
"search3",
{"artistCount": 1, "artistOffset": 0},
{
"query": artist["name"],
"songCount": 0,
"songOffset": 0,
"albumCount": 0,
"albumOffset": 0,
"artistCount": 1,
"artistOffset": 0,
},
{
"musicFolderId": music_folders[0]["id"],
"searchResult3": {"artist": [artist]},
Expand Down
4 changes: 2 additions & 2 deletions tests/models/test_jukebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ def test_jukebox_skip_with_offset(
add_responses: AddResponses,
subsonic: Subsonic,
mock_jukebox_control_status: list[Response],
mock_jukebox_control_skip_without_offset: list[Response],
mock_jukebox_control_skip_with_offset: list[Response],
offset_time: int,
) -> None:
add_responses(mock_jukebox_control_status)
add_responses(mock_jukebox_control_skip_without_offset)
add_responses(mock_jukebox_control_skip_with_offset)

response: Jukebox = subsonic.jukebox.status()
response = response.skip(0, offset_time)
Expand Down

0 comments on commit e1df80b

Please sign in to comment.