Create a testing environment in production for ListenBrainz recommendation engine (troi-recommendation-playground)
This commit is contained in:
parent
cc0f8f395c
commit
f821dcbbc2
19 changed files with 1124 additions and 11 deletions
116
api/tests/radios/test_lb_recommendations.py
Normal file
116
api/tests/radios/test_lb_recommendations.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import pytest
|
||||
import troi.core
|
||||
from django.core.cache import cache
|
||||
from django.db.models import Q
|
||||
from requests.exceptions import ConnectTimeout
|
||||
|
||||
from funkwhale_api.music.models import Track
|
||||
from funkwhale_api.radios import lb_recommendations
|
||||
from funkwhale_api.typesense import factories as custom_factories
|
||||
from funkwhale_api.typesense import utils
|
||||
|
||||
|
||||
def test_can_build_radio_queryset_with_fw_db(factories, mocker):
|
||||
factories["music.Track"](
|
||||
title="I Want It That Way", mbid="87dfa566-21c3-45ed-bc42-1d345b8563fa"
|
||||
)
|
||||
factories["music.Track"](
|
||||
title="The Perfect Kiss", mbid="ec0da94e-fbfe-4eb0-968e-024d4c32d1d0"
|
||||
)
|
||||
factories["music.Track"]()
|
||||
|
||||
qs = Track.objects.all()
|
||||
|
||||
mocker.patch("funkwhale_api.typesense.utils.resolve_recordings_to_fw_track")
|
||||
|
||||
radio_qs = lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
recommended_recording_mbids = [
|
||||
"87dfa566-21c3-45ed-bc42-1d345b8563fa",
|
||||
"ec0da94e-fbfe-4eb0-968e-024d4c32d1d0",
|
||||
]
|
||||
|
||||
assert list(
|
||||
Track.objects.all().filter(Q(mbid__in=recommended_recording_mbids))
|
||||
) == list(radio_qs)
|
||||
|
||||
|
||||
def test_build_radio_queryset_without_fw_db(mocker):
|
||||
resolve_recordings_to_fw_track = mocker.patch.object(
|
||||
utils, "resolve_recordings_to_fw_track", return_value=None
|
||||
)
|
||||
# mocker.patch.object(cache, "get_many", return_value=None)
|
||||
|
||||
qs = Track.objects.all()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
|
||||
assert resolve_recordings_to_fw_track.called_once_with(
|
||||
custom_factories.recommended_recording_mbids
|
||||
)
|
||||
|
||||
|
||||
def test_build_radio_queryset_with_redis_and_fw_db(factories, mocker):
|
||||
factories["music.Track"](
|
||||
pk="1", title="I Want It That Way", mbid="87dfa566-21c3-45ed-bc42-1d345b8563fa"
|
||||
)
|
||||
mocker.patch.object(utils, "resolve_recordings_to_fw_track", return_value=None)
|
||||
redis_cache = {}
|
||||
redis_cache["ec0da94e-fbfe-4eb0-968e-024d4c32d1d0"] = 2
|
||||
mocker.patch.object(cache, "get_many", return_value=redis_cache)
|
||||
|
||||
qs = Track.objects.all()
|
||||
|
||||
assert list(
|
||||
lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
) == list(Track.objects.all().filter(pk__in=[1, 2]))
|
||||
|
||||
|
||||
def test_build_radio_queryset_with_redis_and_without_fw_db(factories, mocker):
|
||||
factories["music.Track"](
|
||||
pk="1", title="Super title", mbid="87dfaaaa-2aaa-45ed-bc42-1d34aaaaaaaa"
|
||||
)
|
||||
mocker.patch.object(utils, "resolve_recordings_to_fw_track", return_value=None)
|
||||
redis_cache = {}
|
||||
redis_cache["87dfa566-21c3-45ed-bc42-1d345b8563fa"] = 1
|
||||
mocker.patch.object(cache, "get_many", return_value=redis_cache)
|
||||
qs = Track.objects.all()
|
||||
|
||||
assert list(
|
||||
lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
) == list(Track.objects.all().filter(pk=1))
|
||||
|
||||
|
||||
def test_build_radio_queryset_catch_troi_ConnectTimeout(mocker):
|
||||
mocker.patch.object(
|
||||
troi.core,
|
||||
"generate_playlist",
|
||||
side_effect=ConnectTimeout,
|
||||
)
|
||||
qs = Track.objects.all()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
|
||||
|
||||
def test_build_radio_queryset_catch_troi_no_candidates(mocker):
|
||||
mocker.patch.object(
|
||||
troi.core,
|
||||
"generate_playlist",
|
||||
)
|
||||
qs = Track.objects.all()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
lb_recommendations.build_radio_queryset(
|
||||
custom_factories.DummyPatch(), {"min_recordings": 1}, qs
|
||||
)
|
||||
|
|
@ -429,3 +429,28 @@ def test_can_start_custom_multiple_radio_from_api(api_client, factories):
|
|||
format="json",
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
def test_can_start_periodic_jams_troi_radio_from_api(api_client, factories):
|
||||
factories["music.Track"].create_batch(5)
|
||||
url = reverse("api:v1:radios:sessions-list")
|
||||
config = {"patch": "periodic-jams", "type": "daily-jams"}
|
||||
response = api_client.post(
|
||||
url,
|
||||
{"radio_type": "troi", "config": config},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
# to do : send error to api ?
|
||||
def test_can_catch_troi_radio_error(api_client, factories):
|
||||
factories["music.Track"].create_batch(5)
|
||||
url = reverse("api:v1:radios:sessions-list")
|
||||
config = {"patch": "periodic-jams", "type": "not_existing_type"}
|
||||
response = api_client.post(
|
||||
url,
|
||||
{"radio_type": "troi", "config": config},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
|
|
|||
58
api/tests/typesense/test_tasks.py
Normal file
58
api/tests/typesense/test_tasks.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import logging
|
||||
|
||||
import requests_mock
|
||||
import typesense
|
||||
|
||||
from funkwhale_api.typesense import tasks
|
||||
|
||||
|
||||
def test_add_tracks_to_index_fails(mocker, caplog):
|
||||
logger = logging.getLogger("funkwhale_api.typesense.tasks")
|
||||
caplog.set_level(logging.INFO)
|
||||
logger.addHandler(caplog.handler)
|
||||
|
||||
client = typesense.Client(
|
||||
{
|
||||
"api_key": "api_key",
|
||||
"nodes": [{"host": "host", "port": "port", "protocol": "protocol"}],
|
||||
"connection_timeout_seconds": 2,
|
||||
}
|
||||
)
|
||||
|
||||
with requests_mock.Mocker() as r_mocker:
|
||||
r_mocker.post(
|
||||
"protocol://host:port/collections/canonical_fw_data/documents/import",
|
||||
json=[{"name": "data"}],
|
||||
)
|
||||
mocker.patch.object(typesense, "Client", return_value=client)
|
||||
mocker.patch.object(
|
||||
typesense.client.ApiCall,
|
||||
"post",
|
||||
side_effect=typesense.exceptions.TypesenseClientError("Hello"),
|
||||
)
|
||||
tasks.add_tracks_to_index([1, 2, 3])
|
||||
assert "Can't build index" in caplog.text
|
||||
|
||||
|
||||
def test_build_canonical_index_success(mocker, caplog, factories):
|
||||
logger = logging.getLogger("funkwhale_api.typesense.tasks")
|
||||
caplog.set_level(logging.INFO)
|
||||
logger.addHandler(caplog.handler)
|
||||
|
||||
client = typesense.Client(
|
||||
{
|
||||
"api_key": "api_key",
|
||||
"nodes": [{"host": "host", "port": "port", "protocol": "protocol"}],
|
||||
"connection_timeout_seconds": 2,
|
||||
}
|
||||
)
|
||||
|
||||
factories["music.Track"].create_batch(size=5)
|
||||
|
||||
with requests_mock.Mocker() as r_mocker:
|
||||
mocker.patch.object(typesense, "Client", return_value=client)
|
||||
|
||||
r_mocker.post("protocol://host:port/collections", json={"name": "data"})
|
||||
|
||||
tasks.build_canonical_index()
|
||||
assert "Launching async task to add " in caplog.text
|
||||
43
api/tests/typesense/test_utils.py
Normal file
43
api/tests/typesense/test_utils.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import requests_mock
|
||||
import typesense
|
||||
from django.core.cache import cache
|
||||
|
||||
from funkwhale_api.typesense import factories as custom_factories
|
||||
from funkwhale_api.typesense import utils
|
||||
|
||||
|
||||
def test_resolve_recordings_to_fw_track(mocker, factories):
|
||||
artist = factories["music.Artist"](name="artist_name")
|
||||
factories["music.Track"](
|
||||
pk=1,
|
||||
title="I Want It That Way",
|
||||
artist=artist,
|
||||
mbid="87dfa566-21c3-45ed-bc42-1d345b8563fa",
|
||||
)
|
||||
factories["music.Track"](
|
||||
pk=2,
|
||||
title="I Want It That Way",
|
||||
artist=artist,
|
||||
)
|
||||
|
||||
client = typesense.Client(
|
||||
{
|
||||
"api_key": "api_key",
|
||||
"nodes": [{"host": "host", "port": "port", "protocol": "protocol"}],
|
||||
"connection_timeout_seconds": 2,
|
||||
}
|
||||
)
|
||||
with requests_mock.Mocker() as r_mocker:
|
||||
mocker.patch.object(typesense, "Client", return_value=client)
|
||||
mocker.patch.object(
|
||||
typesense.client.ApiCall,
|
||||
"post",
|
||||
return_value=custom_factories.typesense_search_result,
|
||||
)
|
||||
r_mocker.get(
|
||||
"protocol://host:port/collections/canonical_fw_data/documents/search",
|
||||
json=custom_factories.typesense_search_result,
|
||||
)
|
||||
|
||||
utils.resolve_recordings_to_fw_track(custom_factories.recording_list)
|
||||
assert cache.get("87dfa566-21c3-45ed-bc42-1d345b8563fa") == "1"
|
||||
Loading…
Add table
Add a link
Reference in a new issue