2017-12-24 18:15:21 +00:00
|
|
|
import json
|
|
|
|
import random
|
|
|
|
|
2018-06-10 08:55:16 +00:00
|
|
|
import pytest
|
2017-12-24 18:15:21 +00:00
|
|
|
from django.core.exceptions import ValidationError
|
2018-06-10 08:55:16 +00:00
|
|
|
from django.urls import reverse
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
from funkwhale_api.favorites.models import TrackFavorite
|
2018-06-10 08:55:16 +00:00
|
|
|
from funkwhale_api.radios import models, radios, serializers
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_can_pick_track_from_choices():
|
|
|
|
choices = [1, 2, 3, 4, 5]
|
|
|
|
|
|
|
|
radio = radios.SimpleRadio()
|
|
|
|
|
|
|
|
first_pick = radio.pick(choices=choices)
|
|
|
|
|
|
|
|
assert first_pick in choices
|
|
|
|
|
|
|
|
previous_choices = [first_pick]
|
|
|
|
for remaining_choice in choices:
|
|
|
|
pick = radio.pick(choices=choices, previous_choices=previous_choices)
|
|
|
|
assert pick in set(choices).difference(previous_choices)
|
|
|
|
|
|
|
|
|
|
|
|
def test_can_pick_by_weight():
|
|
|
|
choices_with_weight = [
|
|
|
|
# choice, weight
|
|
|
|
(1, 1),
|
|
|
|
(2, 2),
|
|
|
|
(3, 3),
|
|
|
|
(4, 4),
|
|
|
|
(5, 5),
|
|
|
|
]
|
|
|
|
|
|
|
|
picks = {choice: 0 for choice, weight in choices_with_weight}
|
|
|
|
|
|
|
|
for i in range(1000):
|
|
|
|
radio = radios.SimpleRadio()
|
|
|
|
pick = radio.weighted_pick(choices=choices_with_weight)
|
|
|
|
picks[pick] = picks[pick] + 1
|
|
|
|
|
|
|
|
assert picks[5] > picks[4]
|
|
|
|
assert picks[4] > picks[3]
|
|
|
|
assert picks[3] > picks[2]
|
|
|
|
assert picks[2] > picks[1]
|
|
|
|
|
|
|
|
|
|
|
|
def test_can_get_choices_for_favorites_radio(factories):
|
2018-09-22 12:29:30 +00:00
|
|
|
files = factories["music.Upload"].create_batch(10)
|
2018-02-27 17:35:54 +00:00
|
|
|
tracks = [f.track for f in files]
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
2018-01-07 21:13:32 +00:00
|
|
|
for i in range(5):
|
2017-12-24 18:15:21 +00:00
|
|
|
TrackFavorite.add(track=random.choice(tracks), user=user)
|
|
|
|
|
|
|
|
radio = radios.FavoritesRadio()
|
|
|
|
choices = radio.get_choices(user=user)
|
|
|
|
|
|
|
|
assert choices.count() == user.track_favorites.all().count()
|
|
|
|
|
|
|
|
for favorite in user.track_favorites.all():
|
|
|
|
assert favorite.track in choices
|
|
|
|
|
2018-01-07 21:13:32 +00:00
|
|
|
for i in range(5):
|
2017-12-24 18:15:21 +00:00
|
|
|
pick = radio.pick(user=user)
|
|
|
|
assert pick in choices
|
|
|
|
|
|
|
|
|
2018-01-07 21:13:32 +00:00
|
|
|
def test_can_get_choices_for_custom_radio(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
artist = factories["music.Artist"]()
|
2018-09-22 12:29:30 +00:00
|
|
|
files = factories["music.Upload"].create_batch(5, track__artist=artist)
|
2018-02-27 17:35:54 +00:00
|
|
|
tracks = [f.track for f in files]
|
2018-09-22 12:29:30 +00:00
|
|
|
factories["music.Upload"].create_batch(5)
|
2018-02-27 17:35:54 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
session = factories["radios.CustomRadioSession"](
|
|
|
|
custom_radio__config=[{"type": "artist", "ids": [artist.pk]}]
|
2018-01-07 21:13:32 +00:00
|
|
|
)
|
2018-09-28 20:19:43 +00:00
|
|
|
choices = session.radio.get_choices(filter_playable=False)
|
2018-01-07 21:13:32 +00:00
|
|
|
|
|
|
|
expected = [t.pk for t in tracks]
|
2018-06-09 13:36:16 +00:00
|
|
|
assert list(choices.values_list("id", flat=True)) == expected
|
2018-01-07 21:13:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_cannot_start_custom_radio_if_not_owner_or_not_public(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
|
|
|
artist = factories["music.Artist"]()
|
|
|
|
radio = factories["radios.Radio"](config=[{"type": "artist", "ids": [artist.pk]}])
|
2018-01-07 21:13:32 +00:00
|
|
|
serializer = serializers.RadioSessionSerializer(
|
2018-06-09 13:36:16 +00:00
|
|
|
data={"radio_type": "custom", "custom_radio": radio.pk, "user": user.pk}
|
2018-01-07 21:13:32 +00:00
|
|
|
)
|
|
|
|
message = "You don't have access to this radio"
|
|
|
|
assert not serializer.is_valid()
|
2018-06-09 13:36:16 +00:00
|
|
|
assert message in serializer.errors["non_field_errors"]
|
2018-01-07 21:13:32 +00:00
|
|
|
|
|
|
|
|
2018-09-28 20:19:43 +00:00
|
|
|
def test_can_start_custom_radio_from_api(logged_in_api_client, factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
artist = factories["music.Artist"]()
|
|
|
|
radio = factories["radios.Radio"](
|
2018-09-28 20:19:43 +00:00
|
|
|
config=[{"type": "artist", "ids": [artist.pk]}], user=logged_in_api_client.user
|
2018-01-07 21:13:32 +00:00
|
|
|
)
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:radios:sessions-list")
|
2018-09-28 20:19:43 +00:00
|
|
|
response = logged_in_api_client.post(
|
2018-06-09 13:36:16 +00:00
|
|
|
url, {"radio_type": "custom", "custom_radio": radio.pk}
|
|
|
|
)
|
2018-01-07 21:13:32 +00:00
|
|
|
assert response.status_code == 201
|
2018-06-09 13:36:16 +00:00
|
|
|
session = radio.sessions.latest("id")
|
|
|
|
assert session.radio_type == "custom"
|
2018-09-28 20:19:43 +00:00
|
|
|
assert session.user == logged_in_api_client.user
|
2018-01-07 21:13:32 +00:00
|
|
|
|
|
|
|
|
2017-12-24 18:15:21 +00:00
|
|
|
def test_can_use_radio_session_to_filter_choices(factories):
|
2018-09-22 12:29:30 +00:00
|
|
|
factories["music.Upload"].create_batch(10)
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
2017-12-24 18:15:21 +00:00
|
|
|
radio = radios.RandomRadio()
|
|
|
|
session = radio.start_session(user)
|
|
|
|
|
2018-09-22 12:29:30 +00:00
|
|
|
for i in range(10):
|
2018-09-28 20:19:43 +00:00
|
|
|
radio.pick(filter_playable=False)
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-09-22 12:29:30 +00:00
|
|
|
# ensure 10 differents tracks have been suggested
|
2017-12-24 18:15:21 +00:00
|
|
|
tracks_id = [
|
2018-06-09 13:36:16 +00:00
|
|
|
session_track.track.pk for session_track in session.session_tracks.all()
|
|
|
|
]
|
2018-09-22 12:29:30 +00:00
|
|
|
assert len(set(tracks_id)) == 10
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_can_restore_radio_from_previous_session(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
2017-12-24 18:15:21 +00:00
|
|
|
radio = radios.RandomRadio()
|
|
|
|
session = radio.start_session(user)
|
|
|
|
|
|
|
|
restarted_radio = radios.RandomRadio(session)
|
|
|
|
assert radio.session == restarted_radio.session
|
|
|
|
|
|
|
|
|
2018-09-28 20:19:43 +00:00
|
|
|
def test_can_start_radio_for_logged_in_user(logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:radios:sessions-list")
|
2018-09-28 20:19:43 +00:00
|
|
|
logged_in_api_client.post(url, {"radio_type": "random"})
|
2018-06-09 13:36:16 +00:00
|
|
|
session = models.RadioSession.objects.latest("id")
|
|
|
|
assert session.radio_type == "random"
|
2018-09-28 20:19:43 +00:00
|
|
|
assert session.user == logged_in_api_client.user
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
2018-09-28 20:19:43 +00:00
|
|
|
def test_can_get_track_for_session_from_api(factories, logged_in_api_client):
|
|
|
|
actor = logged_in_api_client.user.create_actor()
|
|
|
|
track = factories["music.Upload"](
|
|
|
|
library__actor=actor, import_status="finished"
|
|
|
|
).track
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:radios:sessions-list")
|
2018-09-28 20:19:43 +00:00
|
|
|
response = logged_in_api_client.post(url, {"radio_type": "random"})
|
2018-06-09 13:36:16 +00:00
|
|
|
session = models.RadioSession.objects.latest("id")
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:radios:tracks-list")
|
2018-09-28 20:19:43 +00:00
|
|
|
response = logged_in_api_client.post(url, {"session": session.pk})
|
2018-06-09 13:36:16 +00:00
|
|
|
data = json.loads(response.content.decode("utf-8"))
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-09-28 20:19:43 +00:00
|
|
|
assert data["track"]["id"] == track.pk
|
2018-06-09 13:36:16 +00:00
|
|
|
assert data["position"] == 1
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-09-28 20:19:43 +00:00
|
|
|
next_track = factories["music.Upload"](
|
|
|
|
library__actor=actor, import_status="finished"
|
|
|
|
).track
|
|
|
|
response = logged_in_api_client.post(url, {"session": session.pk})
|
2018-06-09 13:36:16 +00:00
|
|
|
data = json.loads(response.content.decode("utf-8"))
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert data["track"]["id"] == next_track.id
|
|
|
|
assert data["position"] == 2
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_related_object_radio_validate_related_object(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
2017-12-24 18:15:21 +00:00
|
|
|
# cannot start without related object
|
|
|
|
radio = radios.ArtistRadio()
|
|
|
|
with pytest.raises(ValidationError):
|
|
|
|
radio.start_session(user)
|
|
|
|
|
|
|
|
# cannot start with bad related object type
|
|
|
|
radio = radios.ArtistRadio()
|
|
|
|
with pytest.raises(ValidationError):
|
|
|
|
radio.start_session(user, related_object=user)
|
|
|
|
|
|
|
|
|
|
|
|
def test_can_start_artist_radio(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
|
|
|
artist = factories["music.Artist"]()
|
2018-09-22 12:29:30 +00:00
|
|
|
factories["music.Upload"].create_batch(5)
|
|
|
|
good_files = factories["music.Upload"].create_batch(5, track__artist=artist)
|
2018-02-27 17:35:54 +00:00
|
|
|
good_tracks = [f.track for f in good_files]
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
radio = radios.ArtistRadio()
|
|
|
|
session = radio.start_session(user, related_object=artist)
|
2018-06-09 13:36:16 +00:00
|
|
|
assert session.radio_type == "artist"
|
2017-12-24 18:15:21 +00:00
|
|
|
for i in range(5):
|
2018-09-28 20:19:43 +00:00
|
|
|
assert radio.pick(filter_playable=False) in good_tracks
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_can_start_tag_radio(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
|
|
|
tag = factories["taggit.Tag"]()
|
2018-09-22 12:29:30 +00:00
|
|
|
factories["music.Upload"].create_batch(5)
|
|
|
|
good_files = factories["music.Upload"].create_batch(5, track__tags=[tag])
|
2018-02-27 17:35:54 +00:00
|
|
|
good_tracks = [f.track for f in good_files]
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
radio = radios.TagRadio()
|
|
|
|
session = radio.start_session(user, related_object=tag)
|
2018-06-09 13:36:16 +00:00
|
|
|
assert session.radio_type == "tag"
|
2017-12-24 18:15:21 +00:00
|
|
|
for i in range(5):
|
2018-09-28 20:19:43 +00:00
|
|
|
assert radio.pick(filter_playable=False) in good_tracks
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_can_start_artist_radio_from_api(logged_in_api_client, preferences, factories):
|
|
|
|
artist = factories["music.Artist"]()
|
|
|
|
url = reverse("api:v1:radios:sessions-list")
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-05-06 09:18:28 +00:00
|
|
|
response = logged_in_api_client.post(
|
2018-06-09 13:36:16 +00:00
|
|
|
url, {"radio_type": "artist", "related_object_id": artist.id}
|
|
|
|
)
|
2018-03-28 21:48:34 +00:00
|
|
|
|
|
|
|
assert response.status_code == 201
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
session = models.RadioSession.objects.latest("id")
|
2018-03-28 21:48:34 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert session.radio_type == "artist"
|
2018-05-06 09:18:28 +00:00
|
|
|
assert session.related_object == artist
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_can_start_less_listened_radio(factories):
|
2018-06-09 13:36:16 +00:00
|
|
|
user = factories["users.User"]()
|
2018-09-22 12:29:30 +00:00
|
|
|
wrong_files = factories["music.Upload"].create_batch(5)
|
2018-02-27 17:35:54 +00:00
|
|
|
for f in wrong_files:
|
2018-06-09 13:36:16 +00:00
|
|
|
factories["history.Listening"](track=f.track, user=user)
|
2018-09-22 12:29:30 +00:00
|
|
|
good_files = factories["music.Upload"].create_batch(5)
|
2018-02-27 17:35:54 +00:00
|
|
|
good_tracks = [f.track for f in good_files]
|
2017-12-24 18:15:21 +00:00
|
|
|
radio = radios.LessListenedRadio()
|
2018-06-09 15:41:59 +00:00
|
|
|
radio.start_session(user)
|
2018-05-06 09:18:28 +00:00
|
|
|
|
2017-12-24 18:15:21 +00:00
|
|
|
for i in range(5):
|
2018-09-28 20:19:43 +00:00
|
|
|
assert radio.pick(filter_playable=False) in good_tracks
|
2019-01-30 15:54:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_similar_radio_track(factories):
|
|
|
|
user = factories["users.User"]()
|
|
|
|
seed = factories["music.Track"]()
|
|
|
|
radio = radios.SimilarRadio()
|
|
|
|
radio.start_session(user, related_object=seed)
|
|
|
|
|
|
|
|
factories["music.Track"].create_batch(5)
|
|
|
|
|
|
|
|
# one user listened to this track
|
|
|
|
l1 = factories["history.Listening"](track=seed)
|
|
|
|
|
|
|
|
expected_next = factories["music.Track"]()
|
|
|
|
factories["history.Listening"](track=expected_next, user=l1.user)
|
|
|
|
|
|
|
|
assert radio.pick(filter_playable=False) == expected_next
|
2019-02-14 09:49:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_session_radio_get_queryset_ignore_filtered_track_artist(
|
|
|
|
factories, queryset_equal_list
|
|
|
|
):
|
|
|
|
cf = factories["moderation.UserFilter"](for_artist=True)
|
|
|
|
factories["music.Track"](artist=cf.target_artist)
|
|
|
|
valid_track = factories["music.Track"]()
|
|
|
|
radio = radios.RandomRadio()
|
|
|
|
radio.start_session(user=cf.user)
|
|
|
|
|
|
|
|
assert radio.get_queryset() == [valid_track]
|
|
|
|
|
|
|
|
|
|
|
|
def test_session_radio_get_queryset_ignore_filtered_track_album_artist(
|
|
|
|
factories, queryset_equal_list
|
|
|
|
):
|
|
|
|
cf = factories["moderation.UserFilter"](for_artist=True)
|
|
|
|
factories["music.Track"](album__artist=cf.target_artist)
|
|
|
|
valid_track = factories["music.Track"]()
|
|
|
|
radio = radios.RandomRadio()
|
|
|
|
radio.start_session(user=cf.user)
|
|
|
|
|
|
|
|
assert radio.get_queryset() == [valid_track]
|