diff --git a/api/funkwhale_api/common/tests/test_jwt_querystring.py b/api/funkwhale_api/common/tests/test_jwt_querystring.py deleted file mode 100644 index 90e63775d9ef7e5e2aeeaac6a144d3ff1812bd6e..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/common/tests/test_jwt_querystring.py +++ /dev/null @@ -1,32 +0,0 @@ -from test_plus.test import TestCase -from rest_framework_jwt.settings import api_settings - -from funkwhale_api.users.models import User - - -jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER -jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER - - -class TestJWTQueryString(TestCase): - www_authenticate_realm = 'api' - - def test_can_authenticate_using_token_param_in_url(self): - user = User.objects.create_superuser( - username='test', email='test@test.com', password='test') - - url = self.reverse('api:v1:tracks-list') - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = self.client.get(url) - - self.assertEqual(response.status_code, 401) - - payload = jwt_payload_handler(user) - token = jwt_encode_handler(payload) - print(payload, token) - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = self.client.get(url, data={ - 'jwt': token - }) - - self.assertEqual(response.status_code, 200) diff --git a/api/funkwhale_api/downloader/tests/test_downloader.py b/api/funkwhale_api/downloader/tests/test_downloader.py deleted file mode 100644 index 7cfaa63c83e40f040aaa4f21de1a519c74d18582..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/downloader/tests/test_downloader.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from test_plus.test import TestCase -from .. import downloader -from funkwhale_api.utils.tests import TMPDirTestCaseMixin - - -class TestDownloader(TMPDirTestCaseMixin, TestCase): - - def test_can_download_audio_from_youtube_url_to_vorbis(self): - data = downloader.download('https://www.youtube.com/watch?v=tPEE9ZwTmy0', target_directory=self.download_dir) - self.assertEqual( - data['audio_file_path'], - os.path.join(self.download_dir, 'tPEE9ZwTmy0.ogg')) - self.assertTrue(os.path.exists(data['audio_file_path'])) diff --git a/api/funkwhale_api/factories.py b/api/funkwhale_api/factories.py new file mode 100644 index 0000000000000000000000000000000000000000..6fed66edb2ed000e75df819ffb862949672889a5 --- /dev/null +++ b/api/funkwhale_api/factories.py @@ -0,0 +1,30 @@ +import factory +import persisting_theory + + +class FactoriesRegistry(persisting_theory.Registry): + look_into = 'factories' + + def prepare_name(self, data, name=None): + return name or data._meta.model._meta.label + + +registry = FactoriesRegistry() + + +def ManyToManyFromList(field_name): + """ + To automate the pattern described in + http://factoryboy.readthedocs.io/en/latest/recipes.html#simple-many-to-many-relationship + """ + + @factory.post_generation + def inner(self, create, extracted, **kwargs): + if not create: + return + + if extracted: + field = getattr(self, field_name) + field.add(*extracted) + + return inner diff --git a/api/funkwhale_api/favorites/factories.py b/api/funkwhale_api/favorites/factories.py new file mode 100644 index 0000000000000000000000000000000000000000..233dd049c5477fdb0c83719d397f41c9f62536d4 --- /dev/null +++ b/api/funkwhale_api/favorites/factories.py @@ -0,0 +1,15 @@ +import factory + +from funkwhale_api.factories import registry + +from funkwhale_api.music.factories import TrackFactory +from funkwhale_api.users.factories import UserFactory + + +@registry.register +class TrackFavorite(factory.django.DjangoModelFactory): + track = factory.SubFactory(TrackFactory) + user = factory.SubFactory(UserFactory) + + class Meta: + model = 'favorites.TrackFavorite' diff --git a/api/funkwhale_api/favorites/tests/test_favorites.py b/api/funkwhale_api/favorites/tests/test_favorites.py deleted file mode 100644 index 78c64a41338607a11830a8fdb64df758d9214ab6..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/favorites/tests/test_favorites.py +++ /dev/null @@ -1,113 +0,0 @@ -import json -from test_plus.test import TestCase -from django.urls import reverse - -from funkwhale_api.music.models import Track, Artist -from funkwhale_api.favorites.models import TrackFavorite -from funkwhale_api.users.models import User - -class TestFavorites(TestCase): - - def setUp(self): - super().setUp() - self.artist = Artist.objects.create(name='test') - self.track = Track.objects.create(title='test', artist=self.artist) - self.user = User.objects.create_user(username='test', email='test@test.com', password='test') - - def test_user_can_add_favorite(self): - TrackFavorite.add(self.track, self.user) - - favorite = TrackFavorite.objects.latest('id') - self.assertEqual(favorite.track, self.track) - self.assertEqual(favorite.user, self.user) - - def test_user_can_get_his_favorites(self): - favorite = TrackFavorite.add(self.track, self.user) - - url = reverse('api:v1:favorites:tracks-list') - self.client.login(username=self.user.username, password='test') - - response = self.client.get(url) - - expected = [ - { - 'track': self.track.pk, - 'id': favorite.id, - 'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'), - } - ] - parsed_json = json.loads(response.content.decode('utf-8')) - - self.assertEqual(expected, parsed_json['results']) - - def test_user_can_add_favorite_via_api(self): - url = reverse('api:v1:favorites:tracks-list') - self.client.login(username=self.user.username, password='test') - response = self.client.post(url, {'track': self.track.pk}) - - favorite = TrackFavorite.objects.latest('id') - expected = { - 'track': self.track.pk, - 'id': favorite.id, - 'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'), - } - parsed_json = json.loads(response.content.decode('utf-8')) - - self.assertEqual(expected, parsed_json) - self.assertEqual(favorite.track, self.track) - self.assertEqual(favorite.user, self.user) - - def test_user_can_remove_favorite_via_api(self): - favorite = TrackFavorite.add(self.track, self.user) - - url = reverse('api:v1:favorites:tracks-detail', kwargs={'pk': favorite.pk}) - self.client.login(username=self.user.username, password='test') - response = self.client.delete(url, {'track': self.track.pk}) - self.assertEqual(response.status_code, 204) - self.assertEqual(TrackFavorite.objects.count(), 0) - - def test_user_can_remove_favorite_via_api_using_track_id(self): - favorite = TrackFavorite.add(self.track, self.user) - - url = reverse('api:v1:favorites:tracks-remove') - self.client.login(username=self.user.username, password='test') - response = self.client.delete( - url, json.dumps({'track': self.track.pk}), - content_type='application/json' - ) - - self.assertEqual(response.status_code, 204) - self.assertEqual(TrackFavorite.objects.count(), 0) - - from funkwhale_api.users.models import User - - def test_can_restrict_api_views_to_authenticated_users(self): - urls = [ - ('api:v1:favorites:tracks-list', 'get'), - ] - - for route_name, method in urls: - url = self.reverse(route_name) - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = getattr(self.client, method)(url) - self.assertEqual(response.status_code, 401) - - self.client.login(username=self.user.username, password='test') - - for route_name, method in urls: - url = self.reverse(route_name) - with self.settings(API_AUTHENTICATION_REQUIRED=False): - response = getattr(self.client, method)(url) - self.assertEqual(response.status_code, 200) - - def test_can_filter_tracks_by_favorites(self): - favorite = TrackFavorite.add(self.track, self.user) - - url = reverse('api:v1:tracks-list') - self.client.login(username=self.user.username, password='test') - - response = self.client.get(url, data={'favorites': True}) - - parsed_json = json.loads(response.content.decode('utf-8')) - self.assertEqual(parsed_json['count'], 1) - self.assertEqual(parsed_json['results'][0]['id'], self.track.id) diff --git a/api/funkwhale_api/history/tests/factories.py b/api/funkwhale_api/history/factories.py similarity index 58% rename from api/funkwhale_api/history/tests/factories.py rename to api/funkwhale_api/history/factories.py index 0a411adf0ce02467e41fe8dd94958755606f0b95..86fea64d251c3a0228bc2a0c5edd5525e10cd7dd 100644 --- a/api/funkwhale_api/history/tests/factories.py +++ b/api/funkwhale_api/history/factories.py @@ -1,9 +1,11 @@ import factory -from funkwhale_api.music.tests import factories -from funkwhale_api.users.tests.factories import UserFactory +from funkwhale_api.factories import registry +from funkwhale_api.music import factories +from funkwhale_api.users.factories import UserFactory +@registry.register class ListeningFactory(factory.django.DjangoModelFactory): user = factory.SubFactory(UserFactory) track = factory.SubFactory(factories.TrackFactory) diff --git a/api/funkwhale_api/history/tests/test_history.py b/api/funkwhale_api/history/tests/test_history.py deleted file mode 100644 index 5cb45c946d092d8e0d58d357397581b117bb616f..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/history/tests/test_history.py +++ /dev/null @@ -1,50 +0,0 @@ -import random -import json -from test_plus.test import TestCase -from django.urls import reverse -from django.core.exceptions import ValidationError -from django.utils import timezone - -from funkwhale_api.music.tests.factories import TrackFactory - -from funkwhale_api.users.models import User -from funkwhale_api.history import models - - -class TestHistory(TestCase): - - def setUp(self): - super().setUp() - self.user = User.objects.create_user(username='test', email='test@test.com', password='test') - - def test_can_create_listening(self): - track = TrackFactory() - now = timezone.now() - l = models.Listening.objects.create(user=self.user, track=track) - - def test_anonymous_user_can_create_listening_via_api(self): - track = TrackFactory() - url = self.reverse('api:v1:history:listenings-list') - response = self.client.post(url, { - 'track': track.pk, - }) - - listening = models.Listening.objects.latest('id') - - self.assertEqual(listening.track, track) - self.assertIsNotNone(listening.session_key) - - def test_logged_in_user_can_create_listening_via_api(self): - track = TrackFactory() - - self.client.login(username=self.user.username, password='test') - - url = self.reverse('api:v1:history:listenings-list') - response = self.client.post(url, { - 'track': track.pk, - }) - - listening = models.Listening.objects.latest('id') - - self.assertEqual(listening.track, track) - self.assertEqual(listening.user, self.user) diff --git a/api/funkwhale_api/music/tests/factories.py b/api/funkwhale_api/music/factories.py similarity index 81% rename from api/funkwhale_api/music/tests/factories.py rename to api/funkwhale_api/music/factories.py index 567e2a765f426e8ca9ce4359bf6644431ef97dec..d776cd9459cd42fdbac62e7168b7262ef2718b0e 100644 --- a/api/funkwhale_api/music/tests/factories.py +++ b/api/funkwhale_api/music/factories.py @@ -1,11 +1,16 @@ import factory import os -from funkwhale_api.users.tests.factories import UserFactory +from funkwhale_api.factories import registry, ManyToManyFromList +from funkwhale_api.users.factories import UserFactory -SAMPLES_PATH = os.path.dirname(os.path.abspath(__file__)) +SAMPLES_PATH = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + 'tests', 'music' +) +@registry.register class ArtistFactory(factory.django.DjangoModelFactory): name = factory.Faker('name') mbid = factory.Faker('uuid4') @@ -14,6 +19,7 @@ class ArtistFactory(factory.django.DjangoModelFactory): model = 'music.Artist' +@registry.register class AlbumFactory(factory.django.DjangoModelFactory): title = factory.Faker('sentence', nb_words=3) mbid = factory.Faker('uuid4') @@ -26,17 +32,19 @@ class AlbumFactory(factory.django.DjangoModelFactory): model = 'music.Album' +@registry.register class TrackFactory(factory.django.DjangoModelFactory): title = factory.Faker('sentence', nb_words=3) mbid = factory.Faker('uuid4') album = factory.SubFactory(AlbumFactory) artist = factory.SelfAttribute('album.artist') position = 1 - + tags = ManyToManyFromList('tags') class Meta: model = 'music.Track' +@registry.register class TrackFileFactory(factory.django.DjangoModelFactory): track = factory.SubFactory(TrackFactory) audio_file = factory.django.FileField( @@ -46,6 +54,7 @@ class TrackFileFactory(factory.django.DjangoModelFactory): model = 'music.TrackFile' +@registry.register class ImportBatchFactory(factory.django.DjangoModelFactory): submitted_by = factory.SubFactory(UserFactory) @@ -53,14 +62,17 @@ class ImportBatchFactory(factory.django.DjangoModelFactory): model = 'music.ImportBatch' +@registry.register class ImportJobFactory(factory.django.DjangoModelFactory): batch = factory.SubFactory(ImportBatchFactory) source = factory.Faker('url') + mbid = factory.Faker('uuid4') class Meta: model = 'music.ImportJob' +@registry.register class WorkFactory(factory.django.DjangoModelFactory): mbid = factory.Faker('uuid4') language = 'eng' @@ -71,6 +83,7 @@ class WorkFactory(factory.django.DjangoModelFactory): model = 'music.Work' +@registry.register class LyricsFactory(factory.django.DjangoModelFactory): work = factory.SubFactory(WorkFactory) url = factory.Faker('url') @@ -80,6 +93,7 @@ class LyricsFactory(factory.django.DjangoModelFactory): model = 'music.Lyrics' +@registry.register class TagFactory(factory.django.DjangoModelFactory): name = factory.SelfAttribute('slug') slug = factory.Faker('slug') diff --git a/api/funkwhale_api/music/tests/test_api.py b/api/funkwhale_api/music/tests/test_api.py deleted file mode 100644 index 2460fa97d0830a8b66ceaeef80f680def65b02cc..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/music/tests/test_api.py +++ /dev/null @@ -1,256 +0,0 @@ -import json -import unittest -from test_plus.test import TestCase -from django.urls import reverse - -from funkwhale_api.music import models -from funkwhale_api.utils.tests import TMPDirTestCaseMixin -from funkwhale_api.musicbrainz import api -from funkwhale_api.music import serializers -from funkwhale_api.users.models import User - -from . import data as api_data -from . import factories - - -class TestAPI(TMPDirTestCaseMixin, TestCase): - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['adhesive_wombat']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get']['marsupial']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', return_value=api_data.tracks['get']['8bitadventures']) - @unittest.mock.patch('funkwhale_api.music.models.TrackFile.download_file', return_value=None) - def test_can_submit_youtube_url_for_track_import(self, *mocks): - mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' - video_id = 'tPEE9ZwTmy0' - url = reverse('api:v1:submit-single') - user = User.objects.create_superuser(username='test', email='test@test.com', password='test') - self.client.login(username=user.username, password='test') - response = self.client.post(url, {'import_url': 'https://www.youtube.com/watch?v={0}'.format(video_id), 'mbid': mbid}) - track = models.Track.objects.get(mbid=mbid) - self.assertEqual(track.artist.name, 'Adhesive Wombat') - self.assertEqual(track.album.title, 'Marsupial Madness') - # self.assertIn(video_id, track.files.first().audio_file.name) - - def test_import_creates_an_import_with_correct_data(self): - user = User.objects.create_superuser(username='test', email='test@test.com', password='test') - mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' - video_id = 'tPEE9ZwTmy0' - url = reverse('api:v1:submit-single') - self.client.login(username=user.username, password='test') - with self.settings(CELERY_ALWAYS_EAGER=False): - response = self.client.post(url, {'import_url': 'https://www.youtube.com/watch?v={0}'.format(video_id), 'mbid': mbid}) - - batch = models.ImportBatch.objects.latest('id') - self.assertEqual(batch.jobs.count(), 1) - self.assertEqual(batch.submitted_by, user) - self.assertEqual(batch.status, 'pending') - job = batch.jobs.first() - self.assertEqual(str(job.mbid), mbid) - self.assertEqual(job.status, 'pending') - self.assertEqual(job.source, 'https://www.youtube.com/watch?v={0}'.format(video_id)) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['soad']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=b'') - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get_with_includes']['hypnotize']) - def test_can_import_whole_album(self, *mocks): - user = User.objects.create_superuser(username='test', email='test@test.com', password='test') - payload = { - 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', - 'tracks': [ - { - 'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=1111111111', - }, - { - 'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=2222222222', - }, - { - 'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=3333333333', - }, - ] - } - url = reverse('api:v1:submit-album') - self.client.login(username=user.username, password='test') - with self.settings(CELERY_ALWAYS_EAGER=False): - response = self.client.post(url, json.dumps(payload), content_type="application/json") - - batch = models.ImportBatch.objects.latest('id') - self.assertEqual(batch.jobs.count(), 3) - self.assertEqual(batch.submitted_by, user) - self.assertEqual(batch.status, 'pending') - - album = models.Album.objects.latest('id') - self.assertEqual(str(album.mbid), '47ae093f-1607-49a3-be11-a15d335ccc94') - medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0] - self.assertEqual(int(medium_data['track-count']), album.tracks.all().count()) - - for track in medium_data['track-list']: - instance = models.Track.objects.get(mbid=track['recording']['id']) - self.assertEqual(instance.title, track['recording']['title']) - self.assertEqual(instance.position, int(track['position'])) - self.assertEqual(instance.title, track['recording']['title']) - - for row in payload['tracks']: - job = models.ImportJob.objects.get(mbid=row['mbid']) - self.assertEqual(str(job.mbid), row['mbid']) - self.assertEqual(job.status, 'pending') - self.assertEqual(job.source, row['source']) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['soad']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=b'') - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get_with_includes']['hypnotize']) - def test_can_import_whole_artist(self, *mocks): - user = User.objects.create_superuser(username='test', email='test@test.com', password='test') - payload = { - 'artistId': 'mbid', - 'albums': [ - { - 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', - 'tracks': [ - { - 'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=1111111111', - }, - { - 'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=2222222222', - }, - { - 'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed', - 'source': 'https://www.youtube.com/watch?v=3333333333', - }, - ] - } - ] - } - url = reverse('api:v1:submit-artist') - self.client.login(username=user.username, password='test') - with self.settings(CELERY_ALWAYS_EAGER=False): - response = self.client.post(url, json.dumps(payload), content_type="application/json") - - batch = models.ImportBatch.objects.latest('id') - self.assertEqual(batch.jobs.count(), 3) - self.assertEqual(batch.submitted_by, user) - self.assertEqual(batch.status, 'pending') - - album = models.Album.objects.latest('id') - self.assertEqual(str(album.mbid), '47ae093f-1607-49a3-be11-a15d335ccc94') - medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0] - self.assertEqual(int(medium_data['track-count']), album.tracks.all().count()) - - for track in medium_data['track-list']: - instance = models.Track.objects.get(mbid=track['recording']['id']) - self.assertEqual(instance.title, track['recording']['title']) - self.assertEqual(instance.position, int(track['position'])) - self.assertEqual(instance.title, track['recording']['title']) - - for row in payload['albums'][0]['tracks']: - job = models.ImportJob.objects.get(mbid=row['mbid']) - self.assertEqual(str(job.mbid), row['mbid']) - self.assertEqual(job.status, 'pending') - self.assertEqual(job.source, row['source']) - - def test_user_can_query_api_for_his_own_batches(self): - user1 = User.objects.create_superuser(username='test1', email='test1@test.com', password='test') - user2 = User.objects.create_superuser(username='test2', email='test2@test.com', password='test') - mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' - source = 'https://www.youtube.com/watch?v=tPEE9ZwTmy0' - - batch = models.ImportBatch.objects.create(submitted_by=user1) - job = models.ImportJob.objects.create(batch=batch, mbid=mbid, source=source) - - url = reverse('api:v1:import-batches-list') - - self.client.login(username=user2.username, password='test') - response2 = self.client.get(url) - self.assertJSONEqual(response2.content.decode('utf-8'), '{"count":0,"next":null,"previous":null,"results":[]}') - self.client.logout() - - self.client.login(username=user1.username, password='test') - response1 = self.client.get(url) - self.assertIn(mbid, response1.content.decode('utf-8')) - - def test_can_search_artist(self): - artist1 = models.Artist.objects.create(name='Test1') - artist2 = models.Artist.objects.create(name='Test2') - query = 'test1' - expected = '[{0}]'.format(json.dumps(serializers.ArtistSerializerNested(artist1).data)) - url = self.reverse('api:v1:artists-search') - response = self.client.get(url + '?query={0}'.format(query)) - - self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8'))) - - def test_can_search_artist_by_name_start(self): - artist1 = factories.ArtistFactory(name='alpha') - artist2 = factories.ArtistFactory(name='beta') - results = { - 'next': None, - 'previous': None, - 'count': 1, - 'results': [serializers.ArtistSerializerNested(artist1).data] - } - expected = json.dumps(results) - url = self.reverse('api:v1:artists-list') - response = self.client.get(url, {'name__startswith': 'a'}) - - self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8'))) - - def test_can_search_tracks(self): - artist1 = models.Artist.objects.create(name='Test1') - artist2 = models.Artist.objects.create(name='Test2') - track1 = models.Track.objects.create(artist=artist1, title="test_track1") - track2 = models.Track.objects.create(artist=artist2, title="test_track2") - query = 'test track 1' - expected = '[{0}]'.format(json.dumps(serializers.TrackSerializerNested(track1).data)) - url = self.reverse('api:v1:tracks-search') - response = self.client.get(url + '?query={0}'.format(query)) - - self.assertJSONEqual(expected, json.loads(response.content.decode('utf-8'))) - - def test_can_restrict_api_views_to_authenticated_users(self): - urls = [ - ('api:v1:tags-list', 'get'), - ('api:v1:tracks-list', 'get'), - ('api:v1:artists-list', 'get'), - ('api:v1:albums-list', 'get'), - ] - - for route_name, method in urls: - url = self.reverse(route_name) - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = getattr(self.client, method)(url) - self.assertEqual(response.status_code, 401) - - user = User.objects.create_superuser(username='test', email='test@test.com', password='test') - self.client.login(username=user.username, password='test') - - for route_name, method in urls: - url = self.reverse(route_name) - with self.settings(API_AUTHENTICATION_REQUIRED=False): - response = getattr(self.client, method)(url) - self.assertEqual(response.status_code, 200) - - def test_track_file_url_is_restricted_to_authenticated_users(self): - f = factories.TrackFileFactory() - self.assertNotEqual(f.audio_file, None) - url = f.path - - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = self.client.get(url) - - self.assertEqual(response.status_code, 401) - - user = User.objects.create_superuser( - username='test', email='test@test.com', password='test') - self.client.login(username=user.username, password='test') - with self.settings(API_AUTHENTICATION_REQUIRED=True): - response = self.client.get(url) - - self.assertEqual(response.status_code, 200) - - self.assertEqual( - response['X-Accel-Redirect'], - '/_protected{}'.format(f.audio_file.url) - ) diff --git a/api/funkwhale_api/music/tests/test_lyrics.py b/api/funkwhale_api/music/tests/test_lyrics.py deleted file mode 100644 index 9a05e5eb87b88752ea9aa81ac081a0e2981d9570..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/music/tests/test_lyrics.py +++ /dev/null @@ -1,75 +0,0 @@ -import json -import unittest -from test_plus.test import TestCase -from django.urls import reverse - -from funkwhale_api.music import models -from funkwhale_api.musicbrainz import api -from funkwhale_api.music import serializers -from funkwhale_api.users.models import User -from funkwhale_api.music import lyrics as lyrics_utils - -from .mocking import lyricswiki -from . import factories -from . import data as api_data - - - -class TestLyrics(TestCase): - - @unittest.mock.patch('funkwhale_api.music.lyrics._get_html', - return_value=lyricswiki.content) - def test_works_import_lyrics_if_any(self, *mocks): - lyrics = factories.LyricsFactory( - url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') - - lyrics.fetch_content() - self.assertIn( - 'Grab a brush and put on a little makeup', - lyrics.content, - ) - - def test_clean_content(self): - c = """<div class="lyricbox">Hello<br /><script>alert('hello');</script>Is it me you're looking for?<br /></div>""" - d = lyrics_utils.extract_content(c) - d = lyrics_utils.clean_content(d) - - expected = """Hello -Is it me you're looking for? -""" - self.assertEqual(d, expected) - - def test_markdown_rendering(self): - content = """Hello -Is it me you're looking for?""" - - l = factories.LyricsFactory(content=content) - - expected = "<p>Hello<br />Is it me you're looking for?</p>" - self.assertHTMLEqual(expected, l.content_rendered) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['chop_suey']) - @unittest.mock.patch('funkwhale_api.music.lyrics._get_html', - return_value=lyricswiki.content) - def test_works_import_lyrics_if_any(self, *mocks): - track = factories.TrackFactory( - work=None, - mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') - - url = reverse('api:v1:tracks-lyrics', kwargs={'pk': track.pk}) - user = User.objects.create_user( - username='test', email='test@test.com', password='test') - self.client.login(username=user.username, password='test') - response = self.client.get(url) - - self.assertEqual(response.status_code, 200) - - track.refresh_from_db() - lyrics = models.Lyrics.objects.latest('id') - work = models.Work.objects.latest('id') - - self.assertEqual(track.work, work) - self.assertEqual(lyrics.work, work) diff --git a/api/funkwhale_api/music/tests/test_metadata.py b/api/funkwhale_api/music/tests/test_metadata.py deleted file mode 100644 index 9b8c7665337597c04d5770efb4bdc829f18ba380..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/music/tests/test_metadata.py +++ /dev/null @@ -1,80 +0,0 @@ -import unittest -import os -import datetime -from test_plus.test import TestCase -from funkwhale_api.music import metadata - -DATA_DIR = os.path.dirname(os.path.abspath(__file__)) - - -class TestMetadata(TestCase): - - def test_can_get_metadata_from_ogg_file(self, *mocks): - path = os.path.join(DATA_DIR, 'test.ogg') - data = metadata.Metadata(path) - - self.assertEqual( - data.get('title'), - 'Peer Gynt Suite no. 1, op. 46: I. Morning' - ) - self.assertEqual( - data.get('artist'), - 'Edvard Grieg' - ) - self.assertEqual( - data.get('album'), - 'Peer Gynt Suite no. 1, op. 46' - ) - self.assertEqual( - data.get('date'), - datetime.date(2012, 8, 15), - ) - self.assertEqual( - data.get('track_number'), - 1 - ) - - self.assertEqual( - data.get('musicbrainz_albumid'), - 'a766da8b-8336-47aa-a3ee-371cc41ccc75') - self.assertEqual( - data.get('musicbrainz_recordingid'), - 'bd21ac48-46d8-4e78-925f-d9cc2a294656') - self.assertEqual( - data.get('musicbrainz_artistid'), - '013c8e5b-d72a-4cd3-8dee-6c64d6125823') - - def test_can_get_metadata_from_id3_mp3_file(self, *mocks): - path = os.path.join(DATA_DIR, 'test.mp3') - data = metadata.Metadata(path) - - self.assertEqual( - data.get('title'), - 'Bend' - ) - self.assertEqual( - data.get('artist'), - 'Binärpilot' - ) - self.assertEqual( - data.get('album'), - 'You Can\'t Stop Da Funk' - ) - self.assertEqual( - data.get('date'), - datetime.date(2006, 2, 7), - ) - self.assertEqual( - data.get('track_number'), - 1 - ) - - self.assertEqual( - data.get('musicbrainz_albumid'), - 'ce40cdb1-a562-4fd8-a269-9269f98d4124') - self.assertEqual( - data.get('musicbrainz_recordingid'), - 'f269d497-1cc0-4ae4-a0c4-157ec7d73fcb') - self.assertEqual( - data.get('musicbrainz_artistid'), - '9c6bddde-6228-4d9f-ad0d-03f6fcb19e13') diff --git a/api/funkwhale_api/music/tests/test_music.py b/api/funkwhale_api/music/tests/test_music.py deleted file mode 100644 index 5cf9d0cf96d53c6060759b6a8c65cf1aaef6917f..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/music/tests/test_music.py +++ /dev/null @@ -1,113 +0,0 @@ -from test_plus.test import TestCase -import unittest.mock -from funkwhale_api.music import models -import datetime - -from . import factories -from . import data as api_data -from .cover import binary_data - - -class TestMusic(TestCase): - - @unittest.mock.patch('musicbrainzngs.search_artists', return_value=api_data.artists['search']['adhesive_wombat']) - def test_can_create_artist_from_api(self, *mocks): - artist = models.Artist.create_from_api(query="Adhesive wombat") - data = models.Artist.api.search(query='Adhesive wombat')['artist-list'][0] - - self.assertEqual(int(data['ext:score']), 100) - self.assertEqual(data['id'], '62c3befb-6366-4585-b256-809472333801') - self.assertEqual(artist.mbid, data['id']) - self.assertEqual(artist.name, 'Adhesive Wombat') - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.search', return_value=api_data.albums['search']['hypnotize']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['soad']) - def test_can_create_album_from_api(self, *mocks): - album = models.Album.create_from_api(query="Hypnotize", artist='system of a down', type='album') - data = models.Album.api.search(query='Hypnotize', artist='system of a down', type='album')['release-list'][0] - - self.assertEqual(album.mbid, data['id']) - self.assertEqual(album.title, 'Hypnotize') - with self.assertRaises(ValueError): - self.assertFalse(album.cover.path is None) - self.assertEqual(album.release_date, datetime.date(2005, 1, 1)) - self.assertEqual(album.artist.name, 'System of a Down') - self.assertEqual(album.artist.mbid, data['artist-credit'][0]['artist']['id']) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['adhesive_wombat']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get']['marsupial']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.search', return_value=api_data.tracks['search']['8bitadventures']) - def test_can_create_track_from_api(self, *mocks): - track = models.Track.create_from_api(query="8-bit adventure") - data = models.Track.api.search(query='8-bit adventure')['recording-list'][0] - self.assertEqual(int(data['ext:score']), 100) - self.assertEqual(data['id'], '9968a9d6-8d92-4051-8f76-674e157b6eed') - self.assertEqual(track.mbid, data['id']) - self.assertTrue(track.artist.pk is not None) - self.assertEqual(str(track.artist.mbid), '62c3befb-6366-4585-b256-809472333801') - self.assertEqual(track.artist.name, 'Adhesive Wombat') - self.assertEqual(str(track.album.mbid), 'a50d2a81-2a50-484d-9cb4-b9f6833f583e') - self.assertEqual(track.album.title, 'Marsupial Madness') - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['adhesive_wombat']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get']['marsupial']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', return_value=api_data.tracks['get']['8bitadventures']) - def test_can_create_track_from_api_with_corresponding_tags(self, *mocks): - track = models.Track.create_from_api(id='9968a9d6-8d92-4051-8f76-674e157b6eed') - expected_tags = ['techno', 'good-music'] - track_tags = [tag.slug for tag in track.tags.all()] - for tag in expected_tags: - self.assertIn(tag, track_tags) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.artists.get', return_value=api_data.artists['get']['adhesive_wombat']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.releases.get', return_value=api_data.albums['get']['marsupial']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.search', return_value=api_data.tracks['search']['8bitadventures']) - def test_can_get_or_create_track_from_api(self, *mocks): - track = models.Track.create_from_api(query="8-bit adventure") - data = models.Track.api.search(query='8-bit adventure')['recording-list'][0] - self.assertEqual(int(data['ext:score']), 100) - self.assertEqual(data['id'], '9968a9d6-8d92-4051-8f76-674e157b6eed') - self.assertEqual(track.mbid, data['id']) - self.assertTrue(track.artist.pk is not None) - self.assertEqual(str(track.artist.mbid), '62c3befb-6366-4585-b256-809472333801') - self.assertEqual(track.artist.name, 'Adhesive Wombat') - - track2, created = models.Track.get_or_create_from_api(mbid=data['id']) - self.assertFalse(created) - self.assertEqual(track, track2) - - def test_album_tags_deduced_from_tracks_tags(self): - tag = factories.TagFactory() - album = factories.AlbumFactory() - tracks = factories.TrackFactory.create_batch(album=album, size=5) - - for track in tracks: - track.tags.add(tag) - - album = models.Album.objects.prefetch_related('tracks__tags').get(pk=album.pk) - - with self.assertNumQueries(0): - self.assertIn(tag, album.tags) - - def test_artist_tags_deduced_from_album_tags(self): - tag = factories.TagFactory() - artist = factories.ArtistFactory() - album = factories.AlbumFactory(artist=artist) - tracks = factories.TrackFactory.create_batch(album=album, size=5) - - for track in tracks: - track.tags.add(tag) - - artist = models.Artist.objects.prefetch_related('albums__tracks__tags').get(pk=artist.pk) - - with self.assertNumQueries(0): - self.assertIn(tag, artist.tags) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=binary_data) - def test_can_download_image_file_for_album(self, *mocks): - # client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed') - album = factories.AlbumFactory(mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed') - album.get_image() - album.save() - - self.assertEqual(album.cover.file.read(), binary_data) diff --git a/api/funkwhale_api/music/tests/test_works.py b/api/funkwhale_api/music/tests/test_works.py deleted file mode 100644 index 55714bce2c142c36b86f52c9361292df79689695..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/music/tests/test_works.py +++ /dev/null @@ -1,66 +0,0 @@ -import json -import unittest -from test_plus.test import TestCase -from django.urls import reverse - -from funkwhale_api.music import models -from funkwhale_api.musicbrainz import api -from funkwhale_api.music import serializers -from funkwhale_api.music.tests import factories -from funkwhale_api.users.models import User - -from . import data as api_data - - -class TestWorks(TestCase): - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) - def test_can_import_work(self, *mocks): - recording = factories.TrackFactory( - mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') - mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' - work = models.Work.create_from_api(id=mbid) - - self.assertEqual(work.title, 'Chop Suey!') - self.assertEqual(work.nature, 'song') - self.assertEqual(work.language, 'eng') - self.assertEqual(work.mbid, mbid) - - # a imported work should also be linked to corresponding recordings - - recording.refresh_from_db() - self.assertEqual(recording.work, work) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) - @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['chop_suey']) - def test_can_get_work_from_recording(self, *mocks): - recording = factories.TrackFactory( - work=None, - mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') - mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' - - self.assertEqual(recording.work, None) - - work = recording.get_work() - - self.assertEqual(work.title, 'Chop Suey!') - self.assertEqual(work.nature, 'song') - self.assertEqual(work.language, 'eng') - self.assertEqual(work.mbid, mbid) - - recording.refresh_from_db() - self.assertEqual(recording.work, work) - - @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) - def test_works_import_lyrics_if_any(self, *mocks): - mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' - work = models.Work.create_from_api(id=mbid) - - lyrics = models.Lyrics.objects.latest('id') - self.assertEqual(lyrics.work, work) - self.assertEqual( - lyrics.url, 'http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') diff --git a/api/funkwhale_api/musicbrainz/tests/__init__.py b/api/funkwhale_api/musicbrainz/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/musicbrainz/tests/test_api.py b/api/funkwhale_api/musicbrainz/tests/test_api.py deleted file mode 100644 index b0911f1c54983fd0f41eda12027ea0c836d587a0..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/musicbrainz/tests/test_api.py +++ /dev/null @@ -1,87 +0,0 @@ -import json -import unittest -from test_plus.test import TestCase -from django.urls import reverse - -from funkwhale_api.musicbrainz import api -from . import data as api_data - - -class TestAPI(TestCase): - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.recordings['search']['brontide matador']) - def test_can_search_recording_in_musicbrainz_api(self, *mocks): - query = 'brontide matador' - url = reverse('api:v1:providers:musicbrainz:search-recordings') - expected = api_data.recordings['search']['brontide matador'] - response = self.client.get(url, data={'query': query}) - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) - - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.releases.search', - return_value=api_data.releases['search']['brontide matador']) - def test_can_search_release_in_musicbrainz_api(self, *mocks): - query = 'brontide matador' - url = reverse('api:v1:providers:musicbrainz:search-releases') - expected = api_data.releases['search']['brontide matador'] - response = self.client.get(url, data={'query': query}) - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) - - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.artists.search', - return_value=api_data.artists['search']['lost fingers']) - def test_can_search_artists_in_musicbrainz_api(self, *mocks): - query = 'lost fingers' - url = reverse('api:v1:providers:musicbrainz:search-artists') - expected = api_data.artists['search']['lost fingers'] - response = self.client.get(url, data={'query': query}) - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) - - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['lost fingers']) - def test_can_get_artist_in_musicbrainz_api(self, *mocks): - uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9' - url = reverse('api:v1:providers:musicbrainz:artist-detail', kwargs={ - 'uuid': uuid, - }) - response = self.client.get(url) - expected = api_data.artists['get']['lost fingers'] - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) - - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.release_groups.browse', - return_value=api_data.release_groups['browse']['lost fingers']) - def test_can_broswe_release_group_using_musicbrainz_api(self, *mocks): - uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9' - url = reverse( - 'api:v1:providers:musicbrainz:release-group-browse', - kwargs={ - 'artist_uuid': uuid, - } - ) - response = self.client.get(url) - expected = api_data.release_groups['browse']['lost fingers'] - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) - - @unittest.mock.patch( - 'funkwhale_api.musicbrainz.api.releases.browse', - return_value=api_data.releases['browse']['Lost in the 80s']) - def test_can_broswe_releases_using_musicbrainz_api(self, *mocks): - uuid = 'f04ed607-11b7-3843-957e-503ecdd485d1' - url = reverse( - 'api:v1:providers:musicbrainz:release-browse', - kwargs={ - 'release_group_uuid': uuid, - } - ) - response = self.client.get(url) - expected = api_data.releases['browse']['Lost in the 80s'] - - self.assertEqual(expected, json.loads(response.content.decode('utf-8'))) diff --git a/api/funkwhale_api/musicbrainz/tests/test_cache.py b/api/funkwhale_api/musicbrainz/tests/test_cache.py deleted file mode 100644 index d2d1260ecb1cb36dc95e5268d269b0d8dbcf774c..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/musicbrainz/tests/test_cache.py +++ /dev/null @@ -1,17 +0,0 @@ -import unittest -from test_plus.test import TestCase - -from funkwhale_api.musicbrainz import client - - -class TestAPI(TestCase): - def test_can_search_recording_in_musicbrainz_api(self, *mocks): - r = {'hello': 'world'} - mocked = 'funkwhale_api.musicbrainz.client._api.search_artists' - with unittest.mock.patch(mocked, return_value=r) as m: - self.assertEqual(client.api.artists.search('test'), r) - # now call from cache - self.assertEqual(client.api.artists.search('test'), r) - self.assertEqual(client.api.artists.search('test'), r) - - self.assertEqual(m.call_count, 1) diff --git a/api/funkwhale_api/playlists/factories.py b/api/funkwhale_api/playlists/factories.py new file mode 100644 index 0000000000000000000000000000000000000000..19e4770cfae15fd6d790063026dbdb04770b2e1b --- /dev/null +++ b/api/funkwhale_api/playlists/factories.py @@ -0,0 +1,13 @@ +import factory + +from funkwhale_api.factories import registry +from funkwhale_api.users.factories import UserFactory + + +@registry.register +class PlaylistFactory(factory.django.DjangoModelFactory): + name = factory.Faker('name') + user = factory.SubFactory(UserFactory) + + class Meta: + model = 'playlists.Playlist' diff --git a/api/funkwhale_api/playlists/tests/__init__.py b/api/funkwhale_api/playlists/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/playlists/tests/test_playlists.py b/api/funkwhale_api/playlists/tests/test_playlists.py deleted file mode 100644 index 2f61889ee13d5afbc949ba36a06e9b30b6dbfad7..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/playlists/tests/test_playlists.py +++ /dev/null @@ -1,64 +0,0 @@ -import json -from test_plus.test import TestCase -from django.urls import reverse -from django.core.exceptions import ValidationError -from django.utils import timezone - -from funkwhale_api.music.tests import factories -from funkwhale_api.users.models import User -from funkwhale_api.playlists import models -from funkwhale_api.playlists.serializers import PlaylistSerializer - - -class TestPlayLists(TestCase): - - def setUp(self): - super().setUp() - self.user = User.objects.create_user(username='test', email='test@test.com', password='test') - - def test_can_create_playlist(self): - tracks = factories.TrackFactory.create_batch(size=5) - playlist = models.Playlist.objects.create(user=self.user, name="test") - - previous = None - for i in range(len(tracks)): - previous = playlist.add_track(tracks[i], previous=previous) - - playlist_tracks = list(playlist.playlist_tracks.all()) - - previous = None - for idx, track in enumerate(tracks): - plt = playlist_tracks[idx] - self.assertEqual(plt.position, idx) - self.assertEqual(plt.track, track) - if previous: - self.assertEqual(playlist_tracks[idx + 1], previous) - self.assertEqual(plt.playlist, playlist) - - def test_can_create_playlist_via_api(self): - self.client.login(username=self.user.username, password='test') - url = reverse('api:v1:playlists-list') - data = { - 'name': 'test', - } - - response = self.client.post(url, data) - - playlist = self.user.playlists.latest('id') - self.assertEqual(playlist.name, 'test') - - def test_can_add_playlist_track_via_api(self): - tracks = factories.TrackFactory.create_batch(size=5) - playlist = models.Playlist.objects.create(user=self.user, name="test") - - self.client.login(username=self.user.username, password='test') - - url = reverse('api:v1:playlist-tracks-list') - data = { - 'playlist': playlist.pk, - 'track': tracks[0].pk - } - - response = self.client.post(url, data) - plts = self.user.playlists.latest('id').playlist_tracks.all() - self.assertEqual(plts.first().track, tracks[0]) diff --git a/api/funkwhale_api/providers/audiofile/tests/__init__.py b/api/funkwhale_api/providers/audiofile/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/providers/audiofile/tests/test_disk_import.py b/api/funkwhale_api/providers/audiofile/tests/test_disk_import.py deleted file mode 100644 index f8d36986a3170fea19b6be1b9030b354fddcba29..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/providers/audiofile/tests/test_disk_import.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -import datetime -import unittest -from test_plus.test import TestCase - -from funkwhale_api.providers.audiofile import tasks - -DATA_DIR = os.path.dirname(os.path.abspath(__file__)) - - -class TestAudioFile(TestCase): - def test_can_import_single_audio_file(self, *mocks): - metadata = { - 'artist': ['Test artist'], - 'album': ['Test album'], - 'title': ['Test track'], - 'TRACKNUMBER': ['4'], - 'date': ['2012-08-15'], - 'musicbrainz_albumid': ['a766da8b-8336-47aa-a3ee-371cc41ccc75'], - 'musicbrainz_trackid': ['bd21ac48-46d8-4e78-925f-d9cc2a294656'], - 'musicbrainz_artistid': ['013c8e5b-d72a-4cd3-8dee-6c64d6125823'], - } - - m1 = unittest.mock.patch('mutagen.File', return_value=metadata) - m2 = unittest.mock.patch( - 'funkwhale_api.music.metadata.Metadata.get_file_type', - return_value='OggVorbis', - ) - with m1, m2: - track_file = tasks.from_path( - os.path.join(DATA_DIR, 'dummy_file.ogg')) - - self.assertEqual( - track_file.track.title, metadata['title'][0]) - self.assertEqual( - track_file.track.mbid, metadata['musicbrainz_trackid'][0]) - self.assertEqual( - track_file.track.position, 4) - self.assertEqual( - track_file.track.album.title, metadata['album'][0]) - self.assertEqual( - track_file.track.album.mbid, - metadata['musicbrainz_albumid'][0]) - self.assertEqual( - track_file.track.album.release_date, datetime.date(2012, 8, 15)) - self.assertEqual( - track_file.track.artist.name, metadata['artist'][0]) - self.assertEqual( - track_file.track.artist.mbid, - metadata['musicbrainz_artistid'][0]) diff --git a/api/funkwhale_api/providers/youtube/tests/__init__.py b/api/funkwhale_api/providers/youtube/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/providers/youtube/tests/test_youtube.py b/api/funkwhale_api/providers/youtube/tests/test_youtube.py deleted file mode 100644 index 8a1dd1eb70a601e56082ec7dce8fe3cae32c7095..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/providers/youtube/tests/test_youtube.py +++ /dev/null @@ -1,99 +0,0 @@ -import json -from collections import OrderedDict -import unittest -from test_plus.test import TestCase -from django.urls import reverse -from funkwhale_api.providers.youtube.client import client - -from . import data as api_data - -class TestAPI(TestCase): - maxDiff = None - @unittest.mock.patch( - 'funkwhale_api.providers.youtube.client._do_search', - return_value=api_data.search['8 bit adventure']) - def test_can_get_search_results_from_youtube(self, *mocks): - query = '8 bit adventure' - - results = client.search(query) - self.assertEqual(results[0]['id']['videoId'], '0HxZn6CzOIo') - self.assertEqual(results[0]['snippet']['title'], 'AdhesiveWombat - 8 Bit Adventure') - self.assertEqual(results[0]['full_url'], 'https://www.youtube.com/watch?v=0HxZn6CzOIo') - - @unittest.mock.patch( - 'funkwhale_api.providers.youtube.client._do_search', - return_value=api_data.search['8 bit adventure']) - def test_can_get_search_results_from_funkwhale(self, *mocks): - query = '8 bit adventure' - url = self.reverse('api:v1:providers:youtube:search') - response = self.client.get(url + '?query={0}'.format(query)) - # we should cast the youtube result to something more generic - expected = { - "id": "0HxZn6CzOIo", - "url": "https://www.youtube.com/watch?v=0HxZn6CzOIo", - "type": "youtube#video", - "description": "Make sure to apply adhesive evenly before use. GET IT HERE: http://adhesivewombat.bandcamp.com/album/marsupial-madness Facebook: ...", - "channelId": "UCps63j3krzAG4OyXeEyuhFw", - "title": "AdhesiveWombat - 8 Bit Adventure", - "channelTitle": "AdhesiveWombat", - "publishedAt": "2012-08-22T18:41:03.000Z", - "cover": "https://i.ytimg.com/vi/0HxZn6CzOIo/hqdefault.jpg" - } - - self.assertEqual( - json.loads(response.content.decode('utf-8'))[0], expected) - - @unittest.mock.patch( - 'funkwhale_api.providers.youtube.client._do_search', - side_effect=[ - api_data.search['8 bit adventure'], - api_data.search['system of a down toxicity'], - ] - ) - def test_can_send_multiple_queries_at_once(self, *mocks): - queries = OrderedDict() - queries['1'] = { - 'q': '8 bit adventure', - } - queries['2'] = { - 'q': 'system of a down toxicity', - } - - results = client.search_multiple(queries) - - self.assertEqual(results['1'][0]['id']['videoId'], '0HxZn6CzOIo') - self.assertEqual(results['1'][0]['snippet']['title'], 'AdhesiveWombat - 8 Bit Adventure') - self.assertEqual(results['1'][0]['full_url'], 'https://www.youtube.com/watch?v=0HxZn6CzOIo') - self.assertEqual(results['2'][0]['id']['videoId'], 'BorYwGi2SJc') - self.assertEqual(results['2'][0]['snippet']['title'], 'System of a Down: Toxicity') - self.assertEqual(results['2'][0]['full_url'], 'https://www.youtube.com/watch?v=BorYwGi2SJc') - - @unittest.mock.patch( - 'funkwhale_api.providers.youtube.client._do_search', - return_value=api_data.search['8 bit adventure'], - ) - def test_can_send_multiple_queries_at_once_from_funwkhale(self, *mocks): - queries = OrderedDict() - queries['1'] = { - 'q': '8 bit adventure', - } - - expected = { - "id": "0HxZn6CzOIo", - "url": "https://www.youtube.com/watch?v=0HxZn6CzOIo", - "type": "youtube#video", - "description": "Make sure to apply adhesive evenly before use. GET IT HERE: http://adhesivewombat.bandcamp.com/album/marsupial-madness Facebook: ...", - "channelId": "UCps63j3krzAG4OyXeEyuhFw", - "title": "AdhesiveWombat - 8 Bit Adventure", - "channelTitle": "AdhesiveWombat", - "publishedAt": "2012-08-22T18:41:03.000Z", - "cover": "https://i.ytimg.com/vi/0HxZn6CzOIo/hqdefault.jpg" - } - - url = self.reverse('api:v1:providers:youtube:searchs') - response = self.client.post( - url, json.dumps(queries), content_type='application/json') - - self.assertEqual( - expected, - json.loads(response.content.decode('utf-8'))['1'][0]) diff --git a/api/funkwhale_api/radios/tests/__init__.py b/api/funkwhale_api/radios/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/radios/tests/test_radios.py b/api/funkwhale_api/radios/tests/test_radios.py deleted file mode 100644 index ab27d45166a343c3bb731608db750312b934d241..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/radios/tests/test_radios.py +++ /dev/null @@ -1,196 +0,0 @@ -import random -import json -from test_plus.test import TestCase -from django.urls import reverse -from django.core.exceptions import ValidationError - - -from funkwhale_api.radios import radios -from funkwhale_api.radios import models -from funkwhale_api.favorites.models import TrackFavorite -from funkwhale_api.users.models import User -from funkwhale_api.music.models import Artist -from funkwhale_api.music.tests import factories -from funkwhale_api.history.tests.factories import ListeningFactory - - -class TestRadios(TestCase): - - def setUp(self): - super().setUp() - self.user = User.objects.create_user(username='test', email='test@test.com', password='test') - - def test_can_pick_track_from_choices(self): - choices = [1, 2, 3, 4, 5] - - radio = radios.SimpleRadio() - - first_pick = radio.pick(choices=choices) - - self.assertIn(first_pick, choices) - - previous_choices = [first_pick] - for remaining_choice in choices: - pick = radio.pick(choices=choices, previous_choices=previous_choices) - self.assertIn(pick, set(choices).difference(previous_choices)) - - def test_can_pick_by_weight(self): - 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 - - self.assertTrue(picks[5] > picks[4]) - self.assertTrue(picks[4] > picks[3]) - self.assertTrue(picks[3] > picks[2]) - self.assertTrue(picks[2] > picks[1]) - - def test_can_get_choices_for_favorites_radio(self): - tracks = factories.TrackFactory.create_batch(size=100) - - for i in range(20): - TrackFavorite.add(track=random.choice(tracks), user=self.user) - - radio = radios.FavoritesRadio() - choices = radio.get_choices(user=self.user) - - self.assertEqual(choices.count(), self.user.track_favorites.all().count()) - - for favorite in self.user.track_favorites.all(): - self.assertIn(favorite.track, choices) - - for i in range(20): - pick = radio.pick(user=self.user) - self.assertIn(pick, choices) - - def test_can_use_radio_session_to_filter_choices(self): - tracks = factories.TrackFactory.create_batch(size=30) - radio = radios.RandomRadio() - session = radio.start_session(self.user) - - for i in range(30): - p = radio.pick() - - # ensure 30 differents tracks have been suggested - tracks_id = [session_track.track.pk for session_track in session.session_tracks.all()] - self.assertEqual(len(set(tracks_id)), 30) - - def test_can_restore_radio_from_previous_session(self): - tracks = factories.TrackFactory.create_batch(size=30) - - radio = radios.RandomRadio() - session = radio.start_session(self.user) - - restarted_radio = radios.RandomRadio(session) - self.assertEqual(radio.session, restarted_radio.session) - - def test_can_get_start_radio_from_api(self): - url = reverse('api:v1:radios:sessions-list') - response = self.client.post(url, {'radio_type': 'random'}) - session = models.RadioSession.objects.latest('id') - self.assertEqual(session.radio_type, 'random') - self.assertEqual(session.user, None) - - self.client.login(username=self.user.username, password='test') - response = self.client.post(url, {'radio_type': 'random'}) - session = models.RadioSession.objects.latest('id') - self.assertEqual(session.radio_type, 'random') - self.assertEqual(session.user, self.user) - - def test_can_start_radio_for_anonymous_user(self): - url = reverse('api:v1:radios:sessions-list') - response = self.client.post(url, {'radio_type': 'random'}) - session = models.RadioSession.objects.latest('id') - - self.assertIsNone(session.user) - self.assertIsNotNone(session.session_key) - - def test_can_get_track_for_session_from_api(self): - tracks = factories.TrackFactory.create_batch(size=1) - - self.client.login(username=self.user.username, password='test') - url = reverse('api:v1:radios:sessions-list') - response = self.client.post(url, {'radio_type': 'random'}) - session = models.RadioSession.objects.latest('id') - - url = reverse('api:v1:radios:tracks-list') - response = self.client.post(url, {'session': session.pk}) - data = json.loads(response.content.decode('utf-8')) - - self.assertEqual(data['track']['id'], tracks[0].id) - self.assertEqual(data['position'], 1) - - next_track = factories.TrackFactory() - response = self.client.post(url, {'session': session.pk}) - data = json.loads(response.content.decode('utf-8')) - - self.assertEqual(data['track']['id'], next_track.id) - self.assertEqual(data['position'], 2) - - def test_related_object_radio_validate_related_object(self): - # cannot start without related object - radio = radios.ArtistRadio() - with self.assertRaises(ValidationError): - radio.start_session(self.user) - - # cannot start with bad related object type - radio = radios.ArtistRadio() - with self.assertRaises(ValidationError): - radio.start_session(self.user, related_object=self.user) - - def test_can_start_artist_radio(self): - artist = factories.ArtistFactory() - wrong_tracks = factories.TrackFactory.create_batch(size=30) - good_tracks = factories.TrackFactory.create_batch( - artist=artist, size=5) - - radio = radios.ArtistRadio() - session = radio.start_session(self.user, related_object=artist) - self.assertEqual(session.radio_type, 'artist') - for i in range(5): - self.assertIn(radio.pick(), good_tracks) - - def test_can_start_tag_radio(self): - tag = factories.TagFactory() - wrong_tracks = factories.TrackFactory.create_batch(size=30) - good_tracks = factories.TrackFactory.create_batch(size=5) - for track in good_tracks: - track.tags.add(tag) - - radio = radios.TagRadio() - session = radio.start_session(self.user, related_object=tag) - self.assertEqual(session.radio_type, 'tag') - for i in range(5): - self.assertIn(radio.pick(), good_tracks) - - def test_can_start_artist_radio_from_api(self): - artist = factories.ArtistFactory() - url = reverse('api:v1:radios:sessions-list') - - response = self.client.post(url, {'radio_type': 'artist', 'related_object_id': artist.id}) - session = models.RadioSession.objects.latest('id') - self.assertEqual(session.radio_type, 'artist') - self.assertEqual(session.related_object, artist) - - def test_can_start_less_listened_radio(self): - history = ListeningFactory.create_batch(size=5, user=self.user) - wrong_tracks = [h.track for h in history] - - good_tracks = factories.TrackFactory.create_batch(size=30) - - radio = radios.LessListenedRadio() - session = radio.start_session(self.user) - self.assertEqual(session.related_object, self.user) - for i in range(5): - self.assertIn(radio.pick(), good_tracks) diff --git a/api/funkwhale_api/users/tests/factories.py b/api/funkwhale_api/users/factories.py similarity index 82% rename from api/funkwhale_api/users/tests/factories.py rename to api/funkwhale_api/users/factories.py index 351884ff4543a6625cb24dba904ff6c2b415bae3..0af155e77339177323d46f32f2ad9a7e6cf99f5c 100644 --- a/api/funkwhale_api/users/tests/factories.py +++ b/api/funkwhale_api/users/factories.py @@ -1,12 +1,14 @@ import factory +from funkwhale_api.factories import registry from django.contrib.auth.models import Permission +@registry.register class UserFactory(factory.django.DjangoModelFactory): username = factory.Sequence(lambda n: 'user-{0}'.format(n)) email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n)) - password = factory.PostGenerationMethodCall('set_password', 'password') + password = factory.PostGenerationMethodCall('set_password', 'test') class Meta: model = 'users.User' @@ -28,3 +30,9 @@ class UserFactory(factory.django.DjangoModelFactory): ] # A list of permissions were passed in, use them self.user_permissions.add(*perms) + + +@registry.register(name='users.SuperUser') +class SuperUserFactory(UserFactory): + is_staff = True + is_superuser = True diff --git a/api/funkwhale_api/users/tests/__init__.py b/api/funkwhale_api/users/tests/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/api/funkwhale_api/users/tests/test_admin.py b/api/funkwhale_api/users/tests/test_admin.py deleted file mode 100644 index 10b07b749dc56476687502d86cb9fd6e99d71274..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/users/tests/test_admin.py +++ /dev/null @@ -1,40 +0,0 @@ -from test_plus.test import TestCase - -from ..admin import MyUserCreationForm - - -class TestMyUserCreationForm(TestCase): - - def setUp(self): - self.user = self.make_user() - - def test_clean_username_success(self): - # Instantiate the form with a new username - form = MyUserCreationForm({ - 'username': 'alamode', - 'password1': '123456', - 'password2': '123456', - }) - # Run is_valid() to trigger the validation - valid = form.is_valid() - self.assertTrue(valid) - - # Run the actual clean_username method - username = form.clean_username() - self.assertEqual('alamode', username) - - def test_clean_username_false(self): - # Instantiate the form with the same username as self.user - form = MyUserCreationForm({ - 'username': self.user.username, - 'password1': '123456', - 'password2': '123456', - }) - # Run is_valid() to trigger the validation, which is going to fail - # because the username is already taken - valid = form.is_valid() - self.assertFalse(valid) - - # The form.errors dict should contain a single error called 'username' - self.assertTrue(len(form.errors) == 1) - self.assertTrue('username' in form.errors) diff --git a/api/funkwhale_api/users/tests/test_models.py b/api/funkwhale_api/users/tests/test_models.py deleted file mode 100644 index fbc7eb5f9a944c82be63d3beb9fe4a2ef958afff..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/users/tests/test_models.py +++ /dev/null @@ -1,13 +0,0 @@ -from test_plus.test import TestCase - - -class TestUser(TestCase): - - def setUp(self): - self.user = self.make_user() - - def test__str__(self): - self.assertEqual( - self.user.__str__(), - "testuser" # This is the default username for self.make_user() - ) diff --git a/api/funkwhale_api/users/tests/test_views.py b/api/funkwhale_api/users/tests/test_views.py deleted file mode 100644 index 52826cfa4b53da83487d86bea8ca0899d5e2e97c..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/users/tests/test_views.py +++ /dev/null @@ -1,73 +0,0 @@ -import json - -from django.test import RequestFactory - -from test_plus.test import TestCase -from funkwhale_api.users.models import User - -from . factories import UserFactory - - -class UserTestCase(TestCase): - - def setUp(self): - self.user = self.make_user() - self.factory = RequestFactory() - - def test_can_create_user_via_api(self): - url = self.reverse('rest_register') - data = { - 'username': 'test1', - 'email': 'test1@test.com', - 'password1': 'testtest', - 'password2': 'testtest', - } - with self.settings(REGISTRATION_MODE="public"): - response = self.client.post(url, data) - self.assertEqual(response.status_code, 201) - - u = User.objects.get(email='test1@test.com') - self.assertEqual(u.username, 'test1') - - def test_can_disable_registration_view(self): - url = self.reverse('rest_register') - data = { - 'username': 'test1', - 'email': 'test1@test.com', - 'password1': 'testtest', - 'password2': 'testtest', - } - with self.settings(REGISTRATION_MODE="disabled"): - response = self.client.post(url, data) - self.assertEqual(response.status_code, 403) - - def test_can_fetch_data_from_api(self): - url = self.reverse('api:v1:users:users-me') - response = self.client.get(url) - # login required - self.assertEqual(response.status_code, 401) - - user = UserFactory( - is_staff=True, - perms=[ - 'music.add_importbatch', - 'dynamic_preferences.change_globalpreferencemodel', - ] - ) - self.assertTrue(user.has_perm('music.add_importbatch')) - self.login(user) - - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - payload = json.loads(response.content.decode('utf-8')) - - self.assertEqual(payload['username'], user.username) - self.assertEqual(payload['is_staff'], user.is_staff) - self.assertEqual(payload['is_superuser'], user.is_superuser) - self.assertEqual(payload['email'], user.email) - self.assertEqual(payload['name'], user.name) - self.assertEqual( - payload['permissions']['import.launch']['status'], True) - self.assertEqual( - payload['permissions']['settings.change']['status'], True) diff --git a/api/funkwhale_api/utils/tests.py b/api/funkwhale_api/utils/tests.py deleted file mode 100644 index 2605d3b4c9ff0c5b78393ca515afa9efde6b5308..0000000000000000000000000000000000000000 --- a/api/funkwhale_api/utils/tests.py +++ /dev/null @@ -1,12 +0,0 @@ -import tempfile -import shutil - - -class TMPDirTestCaseMixin(object): - def setUp(self): - super().tearDown() - self.download_dir = tempfile.mkdtemp() - - def tearDown(self): - super().tearDown() - shutil.rmtree(self.download_dir) diff --git a/api/requirements/local.txt b/api/requirements/local.txt index d8a1561e0a31705150d00bdf48ffeaf3431ee4da..b466b20fdfc20c5815fb2f7be8daedccc3f360ac 100644 --- a/api/requirements/local.txt +++ b/api/requirements/local.txt @@ -5,7 +5,6 @@ django_coverage_plugin>=1.5,<1.6 Sphinx>=1.6,<1.7 django-extensions>=1.9,<1.10 Werkzeug>=0.13,<0.14 -django-test-plus>=1.0.20 factory_boy>=2.8.1 # django-debug-toolbar that works with Django 1.5+ diff --git a/api/requirements/test.txt b/api/requirements/test.txt index bde5a2df97b8b21f2a1d7dfe20ad4e18d38eea1c..c12b44827ebbf9440e180de7d686b8f2e190118c 100644 --- a/api/requirements/test.txt +++ b/api/requirements/test.txt @@ -2,7 +2,10 @@ flake8 pytest -pytest-django +# pytest-django until a new release containing django_assert_num_queries +# is deployed to pypi +git+https://github.com/pytest-dev/pytest-django.git@d3d9bb3ef6f0377cb5356eb368992a0834692378 + pytest-mock pytest-sugar pytest-xdist diff --git a/api/funkwhale_api/downloader/tests/__init__.py b/api/tests/__init__.py similarity index 100% rename from api/funkwhale_api/downloader/tests/__init__.py rename to api/tests/__init__.py diff --git a/api/tests/conftest.py b/api/tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..37f3dae3afd72f88db2d7f1c2a9c5a40a6f43cec --- /dev/null +++ b/api/tests/conftest.py @@ -0,0 +1,42 @@ +import tempfile +import shutil +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def factories_autodiscover(): + from django.apps import apps + from funkwhale_api import factories + app_names = [app.name for app in apps.app_configs.values()] + factories.registry.autodiscover(app_names) + + +@pytest.fixture +def factories(db): + from funkwhale_api import factories + yield factories.registry + + +@pytest.fixture +def tmpdir(): + d = tempfile.mkdtemp() + yield d + shutil.rmtree(d) + + +@pytest.fixture +def logged_in_client(db, factories, client): + user = factories['users.User']() + assert client.login(username=user.username, password='test') + setattr(client, 'user', user) + yield client + delattr(client, 'user') + + +@pytest.fixture +def superuser_client(db, factories, client): + user = factories['users.SuperUser']() + assert client.login(username=user.username, password='test') + setattr(client, 'user', user) + yield client + delattr(client, 'user') diff --git a/api/funkwhale_api/providers/youtube/tests/data.py b/api/tests/data/youtube.py similarity index 100% rename from api/funkwhale_api/providers/youtube/tests/data.py rename to api/tests/data/youtube.py diff --git a/api/funkwhale_api/providers/audiofile/tests/dummy_file.ogg b/api/tests/files/dummy_file.ogg similarity index 100% rename from api/funkwhale_api/providers/audiofile/tests/dummy_file.ogg rename to api/tests/files/dummy_file.ogg diff --git a/api/funkwhale_api/favorites/tests/__init__.py b/api/tests/music/__init__.py similarity index 100% rename from api/funkwhale_api/favorites/tests/__init__.py rename to api/tests/music/__init__.py diff --git a/api/funkwhale_api/music/tests/cover.py b/api/tests/music/cover.py similarity index 100% rename from api/funkwhale_api/music/tests/cover.py rename to api/tests/music/cover.py diff --git a/api/funkwhale_api/music/tests/data.py b/api/tests/music/data.py similarity index 100% rename from api/funkwhale_api/music/tests/data.py rename to api/tests/music/data.py diff --git a/api/funkwhale_api/music/tests/mocking/lyricswiki.py b/api/tests/music/mocking/lyricswiki.py similarity index 100% rename from api/funkwhale_api/music/tests/mocking/lyricswiki.py rename to api/tests/music/mocking/lyricswiki.py diff --git a/api/funkwhale_api/music/tests/test.mp3 b/api/tests/music/test.mp3 similarity index 100% rename from api/funkwhale_api/music/tests/test.mp3 rename to api/tests/music/test.mp3 diff --git a/api/funkwhale_api/music/tests/test.ogg b/api/tests/music/test.ogg similarity index 100% rename from api/funkwhale_api/music/tests/test.ogg rename to api/tests/music/test.ogg diff --git a/api/tests/music/test_api.py b/api/tests/music/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..e29aaf107c922f117e63c2f67c5ca613bce6733b --- /dev/null +++ b/api/tests/music/test_api.py @@ -0,0 +1,253 @@ +import json +import pytest +from django.urls import reverse + +from funkwhale_api.music import models +from funkwhale_api.musicbrainz import api +from funkwhale_api.music import serializers + +from . import data as api_data + + +def test_can_submit_youtube_url_for_track_import(mocker, superuser_client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['adhesive_wombat']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get']['marsupial']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.get', + return_value=api_data.tracks['get']['8bitadventures']) + mocker.patch( + 'funkwhale_api.music.models.TrackFile.download_file', + return_value=None) + mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' + video_id = 'tPEE9ZwTmy0' + url = reverse('api:v1:submit-single') + response = superuser_client.post( + url, + {'import_url': 'https://www.youtube.com/watch?v={0}'.format(video_id), + 'mbid': mbid}) + track = models.Track.objects.get(mbid=mbid) + assert track.artist.name == 'Adhesive Wombat' + assert track.album.title == 'Marsupial Madness' + + +def test_import_creates_an_import_with_correct_data(superuser_client, settings): + mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' + video_id = 'tPEE9ZwTmy0' + url = reverse('api:v1:submit-single') + settings.CELERY_ALWAYS_EAGER = False + response = superuser_client.post( + url, + {'import_url': 'https://www.youtube.com/watch?v={0}'.format(video_id), + 'mbid': mbid}) + + batch = models.ImportBatch.objects.latest('id') + assert batch.jobs.count() == 1 + assert batch.submitted_by == superuser_client.user + assert batch.status == 'pending' + job = batch.jobs.first() + assert str(job.mbid) == mbid + assert job.status == 'pending' + assert job.source == 'https://www.youtube.com/watch?v={0}'.format(video_id) + + +def test_can_import_whole_album(mocker, superuser_client, settings): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['soad']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.images.get_front', + return_value=b'') + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get_with_includes']['hypnotize']) + payload = { + 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', + 'tracks': [ + { + 'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=1111111111', + }, + { + 'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=2222222222', + }, + { + 'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=3333333333', + }, + ] + } + url = reverse('api:v1:submit-album') + settings.CELERY_ALWAYS_EAGER = False + response = superuser_client.post( + url, json.dumps(payload), content_type="application/json") + + batch = models.ImportBatch.objects.latest('id') + assert batch.jobs.count() == 3 + assert batch.submitted_by == superuser_client.user + assert batch.status == 'pending' + + album = models.Album.objects.latest('id') + assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94' + medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0] + assert int(medium_data['track-count']) == album.tracks.all().count() + + for track in medium_data['track-list']: + instance = models.Track.objects.get(mbid=track['recording']['id']) + assert instance.title == track['recording']['title'] + assert instance.position == int(track['position']) + assert instance.title == track['recording']['title'] + + for row in payload['tracks']: + job = models.ImportJob.objects.get(mbid=row['mbid']) + assert str(job.mbid) == row['mbid'] + assert job.status == 'pending' + assert job.source == row['source'] + + +def test_can_import_whole_artist(mocker, superuser_client, settings): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['soad']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.images.get_front', + return_value=b'') + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get_with_includes']['hypnotize']) + payload = { + 'artistId': 'mbid', + 'albums': [ + { + 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', + 'tracks': [ + { + 'mbid': '1968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=1111111111', + }, + { + 'mbid': '2968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=2222222222', + }, + { + 'mbid': '3968a9d6-8d92-4051-8f76-674e157b6eed', + 'source': 'https://www.youtube.com/watch?v=3333333333', + }, + ] + } + ] + } + url = reverse('api:v1:submit-artist') + settings.CELERY_ALWAYS_EAGER = False + response = superuser_client.post( + url, json.dumps(payload), content_type="application/json") + + batch = models.ImportBatch.objects.latest('id') + assert batch.jobs.count() == 3 + assert batch.submitted_by == superuser_client.user + assert batch.status == 'pending' + + album = models.Album.objects.latest('id') + assert str(album.mbid) == '47ae093f-1607-49a3-be11-a15d335ccc94' + medium_data = api_data.albums['get_with_includes']['hypnotize']['release']['medium-list'][0] + assert int(medium_data['track-count']) == album.tracks.all().count() + + for track in medium_data['track-list']: + instance = models.Track.objects.get(mbid=track['recording']['id']) + assert instance.title == track['recording']['title'] + assert instance.position == int(track['position']) + assert instance.title == track['recording']['title'] + + for row in payload['albums'][0]['tracks']: + job = models.ImportJob.objects.get(mbid=row['mbid']) + assert str(job.mbid) == row['mbid'] + assert job.status == 'pending' + assert job.source == row['source'] + + +def test_user_can_query_api_for_his_own_batches(client, factories): + user1 = factories['users.SuperUser']() + user2 = factories['users.SuperUser']() + + job = factories['music.ImportJob'](batch__submitted_by=user1) + url = reverse('api:v1:import-batches-list') + + client.login(username=user2.username, password='test') + response2 = client.get(url) + results = json.loads(response2.content.decode('utf-8')) + assert results['count'] == 0 + client.logout() + + client.login(username=user1.username, password='test') + response1 = client.get(url) + results = json.loads(response1.content.decode('utf-8')) + assert results['count'] == 1 + assert results['results'][0]['jobs'][0]['mbid'] == job.mbid + + +def test_can_search_artist(factories, client): + artist1 = factories['music.Artist']() + artist2 = factories['music.Artist']() + expected = [serializers.ArtistSerializerNested(artist1).data] + url = reverse('api:v1:artists-search') + response = client.get(url, {'query': artist1.name}) + assert json.loads(response.content.decode('utf-8')) == expected + + +def test_can_search_artist_by_name_start(factories, client): + artist1 = factories['music.Artist'](name='alpha') + artist2 = factories['music.Artist'](name='beta') + expected = { + 'next': None, + 'previous': None, + 'count': 1, + 'results': [serializers.ArtistSerializerNested(artist1).data] + } + url = reverse('api:v1:artists-list') + response = client.get(url, {'name__startswith': 'a'}) + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_search_tracks(factories, client): + track1 = factories['music.Track'](title="test track 1") + track2 = factories['music.Track']() + query = 'test track 1' + expected = [serializers.TrackSerializerNested(track1).data] + url = reverse('api:v1:tracks-search') + response = client.get(url, {'query': query}) + + assert expected == json.loads(response.content.decode('utf-8')) + + +@pytest.mark.parametrize('route,method', [ + ('api:v1:tags-list', 'get'), + ('api:v1:tracks-list', 'get'), + ('api:v1:artists-list', 'get'), + ('api:v1:albums-list', 'get'), +]) +def test_can_restrict_api_views_to_authenticated_users(db, route, method, settings, client): + url = reverse(route) + settings.API_AUTHENTICATION_REQUIRED = True + response = getattr(client, method)(url) + assert response.status_code == 401 + + +def test_track_file_url_is_restricted_to_authenticated_users(client, factories, settings): + settings.API_AUTHENTICATION_REQUIRED = True + f = factories['music.TrackFile']() + assert f.audio_file is not None + url = f.path + response = client.get(url) + assert response.status_code == 401 + + user = factories['users.SuperUser']() + client.login(username=user.username, password='test') + response = client.get(url) + + assert response.status_code == 200 + assert response['X-Accel-Redirect'] == '/_protected{}'.format(f.audio_file.url) diff --git a/api/tests/music/test_lyrics.py b/api/tests/music/test_lyrics.py new file mode 100644 index 0000000000000000000000000000000000000000..3670a2e5c1bfb1e24b673c86613fe16073f0d201 --- /dev/null +++ b/api/tests/music/test_lyrics.py @@ -0,0 +1,73 @@ +import json +from django.urls import reverse + +from funkwhale_api.music import models +from funkwhale_api.musicbrainz import api +from funkwhale_api.music import serializers +from funkwhale_api.music import lyrics as lyrics_utils + +from .mocking import lyricswiki +from . import data as api_data + + + +def test_works_import_lyrics_if_any(mocker, factories): + mocker.patch( + 'funkwhale_api.music.lyrics._get_html', + return_value=lyricswiki.content) + lyrics = factories['music.Lyrics']( + url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') + + lyrics.fetch_content() + self.assertIn( + 'Grab a brush and put on a little makeup', + lyrics.content, + ) + + +def test_clean_content(): + c = """<div class="lyricbox">Hello<br /><script>alert('hello');</script>Is it me you're looking for?<br /></div>""" + d = lyrics_utils.extract_content(c) + d = lyrics_utils.clean_content(d) + + expected = """Hello +Is it me you're looking for? +""" + assert d == expected + + +def test_markdown_rendering(factories): + content = """Hello +Is it me you're looking for?""" + + l = factories['music.Lyrics'](content=content) + + expected = "<p>Hello<br />\nIs it me you're looking for?</p>" + assert expected == l.content_rendered + + +def test_works_import_lyrics_if_any(mocker, factories, logged_in_client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.works.get', + return_value=api_data.works['get']['chop_suey']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.get', + return_value=api_data.tracks['get']['chop_suey']) + mocker.patch( + 'funkwhale_api.music.lyrics._get_html', + return_value=lyricswiki.content) + track = factories['music.Track']( + work=None, + mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') + + url = reverse('api:v1:tracks-lyrics', kwargs={'pk': track.pk}) + response = logged_in_client.get(url) + + assert response.status_code == 200 + + track.refresh_from_db() + lyrics = models.Lyrics.objects.latest('id') + work = models.Work.objects.latest('id') + + assert track.work == work + assert lyrics.work == work diff --git a/api/tests/music/test_metadata.py b/api/tests/music/test_metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..5df2dbcf117965cf41563a6d028cbe804c3e0fd8 --- /dev/null +++ b/api/tests/music/test_metadata.py @@ -0,0 +1,41 @@ +import datetime +import os +import pytest + +from funkwhale_api.music import metadata + +DATA_DIR = os.path.dirname(os.path.abspath(__file__)) + + +@pytest.mark.parametrize('field,value', [ + ('title', 'Peer Gynt Suite no. 1, op. 46: I. Morning'), + ('artist', 'Edvard Grieg'), + ('album', 'Peer Gynt Suite no. 1, op. 46'), + ('date', datetime.date(2012, 8, 15)), + ('track_number', 1), + ('musicbrainz_albumid', 'a766da8b-8336-47aa-a3ee-371cc41ccc75'), + ('musicbrainz_recordingid', 'bd21ac48-46d8-4e78-925f-d9cc2a294656'), + ('musicbrainz_artistid', '013c8e5b-d72a-4cd3-8dee-6c64d6125823'), +]) +def test_can_get_metadata_from_ogg_file(field, value): + path = os.path.join(DATA_DIR, 'test.ogg') + data = metadata.Metadata(path) + + assert data.get(field) == value + + +@pytest.mark.parametrize('field,value', [ + ('title', 'Bend'), + ('artist', 'Binärpilot'), + ('album', 'You Can\'t Stop Da Funk'), + ('date', datetime.date(2006, 2, 7)), + ('track_number', 1), + ('musicbrainz_albumid', 'ce40cdb1-a562-4fd8-a269-9269f98d4124'), + ('musicbrainz_recordingid', 'f269d497-1cc0-4ae4-a0c4-157ec7d73fcb'), + ('musicbrainz_artistid', '9c6bddde-6228-4d9f-ad0d-03f6fcb19e13'), +]) +def test_can_get_metadata_from_id3_mp3_file(field, value): + path = os.path.join(DATA_DIR, 'test.mp3') + data = metadata.Metadata(path) + + assert data.get(field) == value diff --git a/api/funkwhale_api/music/tests/test_models.py b/api/tests/music/test_models.py similarity index 78% rename from api/funkwhale_api/music/tests/test_models.py rename to api/tests/music/test_models.py index 4b43e46382ad28d4bbf382fef854c39b8cae5d48..2ec192517bbf7d060baf37bbcbb5670f1bb2635a 100644 --- a/api/funkwhale_api/music/tests/test_models.py +++ b/api/tests/music/test_models.py @@ -2,16 +2,14 @@ import pytest from funkwhale_api.music import models from funkwhale_api.music import importers -from . import factories -def test_can_store_release_group_id_on_album(db): - album = factories.AlbumFactory() +def test_can_store_release_group_id_on_album(factories): + album = factories['music.Album']() assert album.release_group_id is not None -def test_import_album_stores_release_group(db): - +def test_import_album_stores_release_group(factories): album_data = { "artist-credit": [ { @@ -31,7 +29,7 @@ def test_import_album_stores_release_group(db): "title": "Marsupial Madness", 'release-group': {'id': '447b4979-2178-405c-bfe6-46bf0b09e6c7'} } - artist = factories.ArtistFactory( + artist = factories['music.Artist']( mbid=album_data['artist-credit'][0]['artist']['id'] ) cleaned_data = models.Album.clean_musicbrainz_data(album_data) @@ -41,9 +39,9 @@ def test_import_album_stores_release_group(db): assert album.artist == artist -def test_import_job_is_bound_to_track_file(db, mocker): - track = factories.TrackFactory() - job = factories.ImportJobFactory(mbid=track.mbid) +def test_import_job_is_bound_to_track_file(factories, mocker): + track = factories['music.Track']() + job = factories['music.ImportJob'](mbid=track.mbid) mocker.patch('funkwhale_api.music.models.TrackFile.download_file') job.run() diff --git a/api/tests/music/test_music.py b/api/tests/music/test_music.py new file mode 100644 index 0000000000000000000000000000000000000000..076ad2bd05cb714c6436592666dffeeae61396ba --- /dev/null +++ b/api/tests/music/test_music.py @@ -0,0 +1,138 @@ +import pytest +from funkwhale_api.music import models +import datetime + +from . import data as api_data +from .cover import binary_data + + +def test_can_create_artist_from_api(mocker, db): + mocker.patch( + 'musicbrainzngs.search_artists', + return_value=api_data.artists['search']['adhesive_wombat']) + artist = models.Artist.create_from_api(query="Adhesive wombat") + data = models.Artist.api.search(query='Adhesive wombat')['artist-list'][0] + + assert int(data['ext:score']), 100 + assert data['id'], '62c3befb-6366-4585-b256-809472333801' + assert artist.mbid, data['id'] + assert artist.name, 'Adhesive Wombat' + + +def test_can_create_album_from_api(mocker, db): + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.search', + return_value=api_data.albums['search']['hypnotize']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['soad']) + album = models.Album.create_from_api(query="Hypnotize", artist='system of a down', type='album') + data = models.Album.api.search(query='Hypnotize', artist='system of a down', type='album')['release-list'][0] + + assert album.mbid, data['id'] + assert album.title, 'Hypnotize' + with pytest.raises(ValueError): + assert album.cover.path is not None + assert album.release_date, datetime.date(2005, 1, 1) + assert album.artist.name, 'System of a Down' + assert album.artist.mbid, data['artist-credit'][0]['artist']['id'] + + +def test_can_create_track_from_api(mocker, db): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['adhesive_wombat']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get']['marsupial']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.search', + return_value=api_data.tracks['search']['8bitadventures']) + track = models.Track.create_from_api(query="8-bit adventure") + data = models.Track.api.search(query='8-bit adventure')['recording-list'][0] + assert int(data['ext:score']) == 100 + assert data['id'] == '9968a9d6-8d92-4051-8f76-674e157b6eed' + assert track.mbid == data['id'] + assert track.artist.pk is not None + assert str(track.artist.mbid) == '62c3befb-6366-4585-b256-809472333801' + assert track.artist.name == 'Adhesive Wombat' + assert str(track.album.mbid) == 'a50d2a81-2a50-484d-9cb4-b9f6833f583e' + assert track.album.title == 'Marsupial Madness' + + +def test_can_create_track_from_api_with_corresponding_tags(mocker, db): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['adhesive_wombat']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get']['marsupial']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.get', + return_value=api_data.tracks['get']['8bitadventures']) + track = models.Track.create_from_api(id='9968a9d6-8d92-4051-8f76-674e157b6eed') + expected_tags = ['techno', 'good-music'] + track_tags = [tag.slug for tag in track.tags.all()] + for tag in expected_tags: + assert tag in track_tags + + +def test_can_get_or_create_track_from_api(mocker, db): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['adhesive_wombat']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.get', + return_value=api_data.albums['get']['marsupial']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.search', + return_value=api_data.tracks['search']['8bitadventures']) + track = models.Track.create_from_api(query="8-bit adventure") + data = models.Track.api.search(query='8-bit adventure')['recording-list'][0] + assert int(data['ext:score']) == 100 + assert data['id'] == '9968a9d6-8d92-4051-8f76-674e157b6eed' + assert track.mbid == data['id'] + assert track.artist.pk is not None + assert str(track.artist.mbid) == '62c3befb-6366-4585-b256-809472333801' + assert track.artist.name == 'Adhesive Wombat' + + track2, created = models.Track.get_or_create_from_api(mbid=data['id']) + assert not created + assert track == track2 + + +def test_album_tags_deduced_from_tracks_tags(factories, django_assert_num_queries): + tag = factories['taggit.Tag']() + album = factories['music.Album']() + tracks = factories['music.Track'].create_batch( + 5, album=album, tags=[tag]) + + album = models.Album.objects.prefetch_related('tracks__tags').get(pk=album.pk) + + with django_assert_num_queries(0): + assert tag in album.tags + + +def test_artist_tags_deduced_from_album_tags(factories, django_assert_num_queries): + tag = factories['taggit.Tag']() + album = factories['music.Album']() + artist = album.artist + tracks = factories['music.Track'].create_batch( + 5, album=album, tags=[tag]) + + artist = models.Artist.objects.prefetch_related('albums__tracks__tags').get(pk=artist.pk) + + with django_assert_num_queries(0): + assert tag in artist.tags + + +def test_can_download_image_file_for_album(mocker, factories): + mocker.patch( + 'funkwhale_api.musicbrainz.api.images.get_front', + return_value=binary_data) + # client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed') + album = factories['music.Album'](mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed') + album.get_image() + album.save() + + assert album.cover.file.read() == binary_data diff --git a/api/tests/music/test_works.py b/api/tests/music/test_works.py new file mode 100644 index 0000000000000000000000000000000000000000..9b72768ad07bf05adae848eef73784866de83c7d --- /dev/null +++ b/api/tests/music/test_works.py @@ -0,0 +1,65 @@ +import json +from django.urls import reverse + +from funkwhale_api.music import models +from funkwhale_api.musicbrainz import api +from funkwhale_api.music import serializers + +from . import data as api_data + + +def test_can_import_work(factories, mocker): + mocker.patch( + 'funkwhale_api.musicbrainz.api.works.get', + return_value=api_data.works['get']['chop_suey']) + recording = factories['music.Track']( + mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') + mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' + work = models.Work.create_from_api(id=mbid) + + assert work.title == 'Chop Suey!' + assert work.nature == 'song' + assert work.language == 'eng' + assert work.mbid == mbid + + # a imported work should also be linked to corresponding recordings + + recording.refresh_from_db() + assert recording.work == work + + +def test_can_get_work_from_recording(factories, mocker): + mocker.patch( + 'funkwhale_api.musicbrainz.api.works.get', + return_value=api_data.works['get']['chop_suey']) + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.get', + return_value=api_data.tracks['get']['chop_suey']) + recording = factories['music.Track']( + work=None, + mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') + mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' + + assert recording.work == None + + work = recording.get_work() + + assert work.title == 'Chop Suey!' + assert work.nature == 'song' + assert work.language == 'eng' + assert work.mbid == mbid + + recording.refresh_from_db() + assert recording.work == work + + +def test_works_import_lyrics_if_any(db, mocker): + mocker.patch( + 'funkwhale_api.musicbrainz.api.works.get', + return_value=api_data.works['get']['chop_suey']) + mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' + work = models.Work.create_from_api(id=mbid) + + lyrics = models.Lyrics.objects.latest('id') + assert lyrics.work == work + assert lyrics.url == 'http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!' diff --git a/api/funkwhale_api/history/tests/__init__.py b/api/tests/musicbrainz/__init__.py similarity index 100% rename from api/funkwhale_api/history/tests/__init__.py rename to api/tests/musicbrainz/__init__.py diff --git a/api/funkwhale_api/musicbrainz/tests/data.py b/api/tests/musicbrainz/data.py similarity index 100% rename from api/funkwhale_api/musicbrainz/tests/data.py rename to api/tests/musicbrainz/data.py diff --git a/api/tests/musicbrainz/test_api.py b/api/tests/musicbrainz/test_api.py new file mode 100644 index 0000000000000000000000000000000000000000..bbade340060dae4cbc4f2a64d296eb86e6c59e79 --- /dev/null +++ b/api/tests/musicbrainz/test_api.py @@ -0,0 +1,90 @@ +import json +from django.urls import reverse + +from funkwhale_api.musicbrainz import api +from . import data as api_data + + + +def test_can_search_recording_in_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.recordings.search', + return_value=api_data.recordings['search']['brontide matador']) + query = 'brontide matador' + url = reverse('api:v1:providers:musicbrainz:search-recordings') + expected = api_data.recordings['search']['brontide matador'] + response = client.get(url, data={'query': query}) + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_search_release_in_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.search', + return_value=api_data.releases['search']['brontide matador']) + query = 'brontide matador' + url = reverse('api:v1:providers:musicbrainz:search-releases') + expected = api_data.releases['search']['brontide matador'] + response = client.get(url, data={'query': query}) + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_search_artists_in_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.search', + return_value=api_data.artists['search']['lost fingers']) + query = 'lost fingers' + url = reverse('api:v1:providers:musicbrainz:search-artists') + expected = api_data.artists['search']['lost fingers'] + response = client.get(url, data={'query': query}) + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_get_artist_in_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.artists.get', + return_value=api_data.artists['get']['lost fingers']) + uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9' + url = reverse('api:v1:providers:musicbrainz:artist-detail', kwargs={ + 'uuid': uuid, + }) + response = client.get(url) + expected = api_data.artists['get']['lost fingers'] + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_broswe_release_group_using_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.release_groups.browse', + return_value=api_data.release_groups['browse']['lost fingers']) + uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9' + url = reverse( + 'api:v1:providers:musicbrainz:release-group-browse', + kwargs={ + 'artist_uuid': uuid, + } + ) + response = client.get(url) + expected = api_data.release_groups['browse']['lost fingers'] + + assert expected == json.loads(response.content.decode('utf-8')) + + +def test_can_broswe_releases_using_musicbrainz_api(db, mocker, client): + mocker.patch( + 'funkwhale_api.musicbrainz.api.releases.browse', + return_value=api_data.releases['browse']['Lost in the 80s']) + uuid = 'f04ed607-11b7-3843-957e-503ecdd485d1' + url = reverse( + 'api:v1:providers:musicbrainz:release-browse', + kwargs={ + 'release_group_uuid': uuid, + } + ) + response = client.get(url) + expected = api_data.releases['browse']['Lost in the 80s'] + + assert expected == json.loads(response.content.decode('utf-8')) diff --git a/api/tests/musicbrainz/test_cache.py b/api/tests/musicbrainz/test_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..fe0d5677302b08bb3f75c45a31f23f3238d034ff --- /dev/null +++ b/api/tests/musicbrainz/test_cache.py @@ -0,0 +1,13 @@ +from funkwhale_api.musicbrainz import client + + +def test_can_search_recording_in_musicbrainz_api(mocker): + r = {'hello': 'world'} + m = mocker.patch( + 'funkwhale_api.musicbrainz.client._api.search_artists', + return_value=r) + assert client.api.artists.search('test') == r + # now call from cache + assert client.api.artists.search('test') == r + assert client.api.artists.search('test') == r + assert m.call_count == 1 diff --git a/api/tests/test_disk_import.py b/api/tests/test_disk_import.py new file mode 100644 index 0000000000000000000000000000000000000000..9aaf399750e3c756c9087d58e6e61931834ab19a --- /dev/null +++ b/api/tests/test_disk_import.py @@ -0,0 +1,39 @@ +import os +import datetime + +from funkwhale_api.providers.audiofile import tasks + +DATA_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'files' +) + + +def test_can_import_single_audio_file(db, mocker): + metadata = { + 'artist': ['Test artist'], + 'album': ['Test album'], + 'title': ['Test track'], + 'TRACKNUMBER': ['4'], + 'date': ['2012-08-15'], + 'musicbrainz_albumid': ['a766da8b-8336-47aa-a3ee-371cc41ccc75'], + 'musicbrainz_trackid': ['bd21ac48-46d8-4e78-925f-d9cc2a294656'], + 'musicbrainz_artistid': ['013c8e5b-d72a-4cd3-8dee-6c64d6125823'], + } + + m1 = mocker.patch('mutagen.File', return_value=metadata) + m2 = mocker.patch( + 'funkwhale_api.music.metadata.Metadata.get_file_type', + return_value='OggVorbis', + ) + track_file = tasks.from_path(os.path.join(DATA_DIR, 'dummy_file.ogg')) + track = track_file.track + + assert track.title == metadata['title'][0] + assert track.mbid == metadata['musicbrainz_trackid'][0] + assert track.position == 4 + assert track.album.title == metadata['album'][0] + assert track.album.mbid == metadata['musicbrainz_albumid'][0] + assert track.album.release_date == datetime.date(2012, 8, 15) + assert track.artist.name == metadata['artist'][0] + assert track.artist.mbid == metadata['musicbrainz_artistid'][0] diff --git a/api/tests/test_downloader.py b/api/tests/test_downloader.py new file mode 100644 index 0000000000000000000000000000000000000000..ede7bb16cc9571607f0b3c73114010f02e411349 --- /dev/null +++ b/api/tests/test_downloader.py @@ -0,0 +1,11 @@ +import os + +from funkwhale_api import downloader + + +def test_can_download_audio_from_youtube_url_to_vorbis(tmpdir): + data = downloader.download( + 'https://www.youtube.com/watch?v=tPEE9ZwTmy0', + target_directory=tmpdir) + assert data['audio_file_path'] == os.path.join(tmpdir, 'tPEE9ZwTmy0.ogg') + assert os.path.exists(data['audio_file_path']) diff --git a/api/tests/test_favorites.py b/api/tests/test_favorites.py new file mode 100644 index 0000000000000000000000000000000000000000..418166d8e0c11aa133e87a44a55f7b1713a3ad2f --- /dev/null +++ b/api/tests/test_favorites.py @@ -0,0 +1,92 @@ +import json +import pytest +from django.urls import reverse + +from funkwhale_api.music.models import Track, Artist +from funkwhale_api.favorites.models import TrackFavorite + + + +def test_user_can_add_favorite(factories): + track = factories['music.Track']() + user = factories['users.User']() + f = TrackFavorite.add(track, user) + + assert f.track == track + assert f.user == user + + +def test_user_can_get_his_favorites(factories, logged_in_client, client): + favorite = factories['favorites.TrackFavorite'](user=logged_in_client.user) + url = reverse('api:v1:favorites:tracks-list') + response = logged_in_client.get(url) + + expected = [ + { + 'track': favorite.track.pk, + 'id': favorite.id, + 'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'), + } + ] + parsed_json = json.loads(response.content.decode('utf-8')) + + assert expected == parsed_json['results'] + + +def test_user_can_add_favorite_via_api(factories, logged_in_client, client): + track = factories['music.Track']() + url = reverse('api:v1:favorites:tracks-list') + response = logged_in_client.post(url, {'track': track.pk}) + + favorite = TrackFavorite.objects.latest('id') + expected = { + 'track': track.pk, + 'id': favorite.id, + 'creation_date': favorite.creation_date.isoformat().replace('+00:00', 'Z'), + } + parsed_json = json.loads(response.content.decode('utf-8')) + + assert expected == parsed_json + assert favorite.track == track + assert favorite.user == logged_in_client.user + + +def test_user_can_remove_favorite_via_api(logged_in_client, factories, client): + favorite = factories['favorites.TrackFavorite'](user=logged_in_client.user) + url = reverse('api:v1:favorites:tracks-detail', kwargs={'pk': favorite.pk}) + response = client.delete(url, {'track': favorite.track.pk}) + assert response.status_code == 204 + assert TrackFavorite.objects.count() == 0 + +def test_user_can_remove_favorite_via_api_using_track_id(factories, logged_in_client): + favorite = factories['favorites.TrackFavorite'](user=logged_in_client.user) + + url = reverse('api:v1:favorites:tracks-remove') + response = logged_in_client.delete( + url, json.dumps({'track': favorite.track.pk}), + content_type='application/json' + ) + + assert response.status_code == 204 + assert TrackFavorite.objects.count() == 0 + + +@pytest.mark.parametrize('url,method', [ + ('api:v1:favorites:tracks-list', 'get'), +]) +def test_url_require_auth(url, method, db, settings, client): + settings.API_AUTHENTICATION_REQUIRED = True + url = reverse(url) + response = getattr(client, method)(url) + assert response.status_code == 401 + + +def test_can_filter_tracks_by_favorites(factories, logged_in_client): + favorite = factories['favorites.TrackFavorite'](user=logged_in_client.user) + + url = reverse('api:v1:tracks-list') + response = logged_in_client.get(url, data={'favorites': True}) + + parsed_json = json.loads(response.content.decode('utf-8')) + assert parsed_json['count'] == 1 + assert parsed_json['results'][0]['id'] == favorite.track.id diff --git a/api/tests/test_history.py b/api/tests/test_history.py new file mode 100644 index 0000000000000000000000000000000000000000..113e5ff6405f60665305f04b379997e8b9343d9c --- /dev/null +++ b/api/tests/test_history.py @@ -0,0 +1,42 @@ +import random +import json +from django.urls import reverse +from django.core.exceptions import ValidationError +from django.utils import timezone + +from funkwhale_api.history import models + + +def test_can_create_listening(factories): + track = factories['music.Track']() + user = factories['users.User']() + now = timezone.now() + l = models.Listening.objects.create(user=user, track=track) + + +def test_anonymous_user_can_create_listening_via_api(client, factories, settings): + settings.API_AUTHENTICATION_REQUIRED = False + track = factories['music.Track']() + url = reverse('api:v1:history:listenings-list') + response = client.post(url, { + 'track': track.pk, + }) + + listening = models.Listening.objects.latest('id') + + assert listening.track == track + assert listening.session_key == client.session.session_key + + +def test_logged_in_user_can_create_listening_via_api(logged_in_client, factories): + track = factories['music.Track']() + + url = reverse('api:v1:history:listenings-list') + response = logged_in_client.post(url, { + 'track': track.pk, + }) + + listening = models.Listening.objects.latest('id') + + assert listening.track == track + assert listening.user == logged_in_client.user diff --git a/api/tests/test_jwt_querystring.py b/api/tests/test_jwt_querystring.py new file mode 100644 index 0000000000000000000000000000000000000000..bd07e1dc3212476f01dd7c60f341e8718c60351c --- /dev/null +++ b/api/tests/test_jwt_querystring.py @@ -0,0 +1,21 @@ +from django.urls import reverse +from rest_framework_jwt.settings import api_settings + +jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER +jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER + + +def test_can_authenticate_using_token_param_in_url(factories, settings, client): + user = factories['users.User']() + settings.API_AUTHENTICATION_REQUIRED = True + url = reverse('api:v1:tracks-list') + response = client.get(url) + + assert response.status_code == 401 + + payload = jwt_payload_handler(user) + token = jwt_encode_handler(payload) + response = client.get(url, data={ + 'jwt': token + }) + assert response.status_code == 200 diff --git a/api/tests/test_playlists.py b/api/tests/test_playlists.py new file mode 100644 index 0000000000000000000000000000000000000000..f496a64cb5d93a14aacc8bb73ddc2a58e3c7b50e --- /dev/null +++ b/api/tests/test_playlists.py @@ -0,0 +1,54 @@ +import json +from django.urls import reverse +from django.core.exceptions import ValidationError +from django.utils import timezone + +from funkwhale_api.playlists import models +from funkwhale_api.playlists.serializers import PlaylistSerializer + + + +def test_can_create_playlist(factories): + tracks = factories['music.Track'].create_batch(5) + playlist = factories['playlists.Playlist']() + + previous = None + for track in tracks: + previous = playlist.add_track(track, previous=previous) + + playlist_tracks = list(playlist.playlist_tracks.all()) + + previous = None + for idx, track in enumerate(tracks): + plt = playlist_tracks[idx] + assert plt.position == idx + assert plt.track == track + if previous: + assert playlist_tracks[idx + 1] == previous + assert plt.playlist == playlist + + +def test_can_create_playlist_via_api(logged_in_client): + url = reverse('api:v1:playlists-list') + data = { + 'name': 'test', + } + + response = logged_in_client.post(url, data) + + playlist = logged_in_client.user.playlists.latest('id') + assert playlist.name == 'test' + + +def test_can_add_playlist_track_via_api(factories, logged_in_client): + tracks = factories['music.Track'].create_batch(5) + playlist = factories['playlists.Playlist'](user=logged_in_client.user) + url = reverse('api:v1:playlist-tracks-list') + data = { + 'playlist': playlist.pk, + 'track': tracks[0].pk + } + + response = logged_in_client.post(url, data) + plts = logged_in_client.user.playlists.latest('id').playlist_tracks.all() + assert plts.first().track == tracks[0] diff --git a/api/tests/test_radios.py b/api/tests/test_radios.py new file mode 100644 index 0000000000000000000000000000000000000000..d67611123ce0febb2623345d4fe388304b86f7bf --- /dev/null +++ b/api/tests/test_radios.py @@ -0,0 +1,195 @@ +import json +import random +import pytest + +from django.urls import reverse +from django.core.exceptions import ValidationError + + +from funkwhale_api.radios import radios +from funkwhale_api.radios import models +from funkwhale_api.favorites.models import TrackFavorite + + +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): + tracks = factories['music.Track'].create_batch(100) + user = factories['users.User']() + for i in range(20): + 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 + + for i in range(20): + pick = radio.pick(user=user) + assert pick in choices + + +def test_can_use_radio_session_to_filter_choices(factories): + tracks = factories['music.Track'].create_batch(30) + user = factories['users.User']() + radio = radios.RandomRadio() + session = radio.start_session(user) + + for i in range(30): + p = radio.pick() + + # ensure 30 differents tracks have been suggested + tracks_id = [ + session_track.track.pk + for session_track in session.session_tracks.all()] + assert len(set(tracks_id)) == 30 + + +def test_can_restore_radio_from_previous_session(factories): + user = factories['users.User']() + radio = radios.RandomRadio() + session = radio.start_session(user) + + restarted_radio = radios.RandomRadio(session) + assert radio.session == restarted_radio.session + + +def test_can_start_radio_for_logged_in_user(logged_in_client): + url = reverse('api:v1:radios:sessions-list') + response = logged_in_client.post(url, {'radio_type': 'random'}) + session = models.RadioSession.objects.latest('id') + assert session.radio_type == 'random' + assert session.user == logged_in_client.user + + +def test_can_start_radio_for_anonymous_user(client, db): + url = reverse('api:v1:radios:sessions-list') + response = client.post(url, {'radio_type': 'random'}) + session = models.RadioSession.objects.latest('id') + + assert session.radio_type == 'random' + assert session.user is None + assert session.session_key == client.session.session_key + + +def test_can_get_track_for_session_from_api(factories, logged_in_client): + tracks = factories['music.Track'].create_batch(size=1) + + url = reverse('api:v1:radios:sessions-list') + response = logged_in_client.post(url, {'radio_type': 'random'}) + session = models.RadioSession.objects.latest('id') + + url = reverse('api:v1:radios:tracks-list') + response = logged_in_client.post(url, {'session': session.pk}) + data = json.loads(response.content.decode('utf-8')) + + assert data['track']['id'] == tracks[0].id + assert data['position'] == 1 + + next_track = factories['music.Track']() + response = logged_in_client.post(url, {'session': session.pk}) + data = json.loads(response.content.decode('utf-8')) + + assert data['track']['id'] == next_track.id + assert data['position'] == 2 + + +def test_related_object_radio_validate_related_object(factories): + user = factories['users.User']() + # 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): + user = factories['users.User']() + artist = factories['music.Artist']() + wrong_tracks = factories['music.Track'].create_batch(5) + good_tracks = factories['music.Track'].create_batch(5, artist=artist) + + radio = radios.ArtistRadio() + session = radio.start_session(user, related_object=artist) + assert session.radio_type == 'artist' + for i in range(5): + assert radio.pick() in good_tracks + + +def test_can_start_tag_radio(factories): + user = factories['users.User']() + tag = factories['taggit.Tag']() + wrong_tracks = factories['music.Track'].create_batch(5) + good_tracks = factories['music.Track'].create_batch(5, tags=[tag]) + + radio = radios.TagRadio() + session = radio.start_session(user, related_object=tag) + assert session.radio_type =='tag' + for i in range(5): + assert radio.pick() in good_tracks + + +def test_can_start_artist_radio_from_api(client, factories): + artist = factories['music.Artist']() + url = reverse('api:v1:radios:sessions-list') + + response = client.post( + url, {'radio_type': 'artist', 'related_object_id': artist.id}) + session = models.RadioSession.objects.latest('id') + assert session.radio_type, 'artist' + assert session.related_object, artist + + +def test_can_start_less_listened_radio(factories): + user = factories['users.User']() + history = factories['history.Listening'].create_batch(5, user=user) + wrong_tracks = [h.track for h in history] + good_tracks = factories['music.Track'].create_batch(size=5) + radio = radios.LessListenedRadio() + session = radio.start_session(user) + assert session.related_object == user + for i in range(5): + assert radio.pick() in good_tracks diff --git a/api/tests/test_youtube.py b/api/tests/test_youtube.py new file mode 100644 index 0000000000000000000000000000000000000000..017d742ef834562f33425cbc84046fdf7d580bb4 --- /dev/null +++ b/api/tests/test_youtube.py @@ -0,0 +1,95 @@ +import json +from collections import OrderedDict +from django.urls import reverse +from funkwhale_api.providers.youtube.client import client + +from .data import youtube as api_data + + +def test_can_get_search_results_from_youtube(mocker): + mocker.patch( + 'funkwhale_api.providers.youtube.client._do_search', + return_value=api_data.search['8 bit adventure']) + query = '8 bit adventure' + results = client.search(query) + assert results[0]['id']['videoId'] == '0HxZn6CzOIo' + assert results[0]['snippet']['title'] == 'AdhesiveWombat - 8 Bit Adventure' + assert results[0]['full_url'] == 'https://www.youtube.com/watch?v=0HxZn6CzOIo' + + +def test_can_get_search_results_from_funkwhale(mocker, client, db): + mocker.patch( + 'funkwhale_api.providers.youtube.client._do_search', + return_value=api_data.search['8 bit adventure']) + query = '8 bit adventure' + url = reverse('api:v1:providers:youtube:search') + response = client.get(url, {'query': query}) + # we should cast the youtube result to something more generic + expected = { + "id": "0HxZn6CzOIo", + "url": "https://www.youtube.com/watch?v=0HxZn6CzOIo", + "type": "youtube#video", + "description": "Make sure to apply adhesive evenly before use. GET IT HERE: http://adhesivewombat.bandcamp.com/album/marsupial-madness Facebook: ...", + "channelId": "UCps63j3krzAG4OyXeEyuhFw", + "title": "AdhesiveWombat - 8 Bit Adventure", + "channelTitle": "AdhesiveWombat", + "publishedAt": "2012-08-22T18:41:03.000Z", + "cover": "https://i.ytimg.com/vi/0HxZn6CzOIo/hqdefault.jpg" + } + + assert json.loads(response.content.decode('utf-8'))[0] == expected + + +def test_can_send_multiple_queries_at_once(mocker): + mocker.patch( + 'funkwhale_api.providers.youtube.client._do_search', + side_effect=[ + api_data.search['8 bit adventure'], + api_data.search['system of a down toxicity'], + ] + ) + + queries = OrderedDict() + queries['1'] = { + 'q': '8 bit adventure', + } + queries['2'] = { + 'q': 'system of a down toxicity', + } + + results = client.search_multiple(queries) + + assert results['1'][0]['id']['videoId'] == '0HxZn6CzOIo' + assert results['1'][0]['snippet']['title'] == 'AdhesiveWombat - 8 Bit Adventure' + assert results['1'][0]['full_url'] == 'https://www.youtube.com/watch?v=0HxZn6CzOIo' + assert results['2'][0]['id']['videoId'] == 'BorYwGi2SJc' + assert results['2'][0]['snippet']['title'] == 'System of a Down: Toxicity' + assert results['2'][0]['full_url'] == 'https://www.youtube.com/watch?v=BorYwGi2SJc' + + +def test_can_send_multiple_queries_at_once_from_funwkhale(mocker, db, client): + mocker.patch( + 'funkwhale_api.providers.youtube.client._do_search', + return_value=api_data.search['8 bit adventure']) + queries = OrderedDict() + queries['1'] = { + 'q': '8 bit adventure', + } + + expected = { + "id": "0HxZn6CzOIo", + "url": "https://www.youtube.com/watch?v=0HxZn6CzOIo", + "type": "youtube#video", + "description": "Make sure to apply adhesive evenly before use. GET IT HERE: http://adhesivewombat.bandcamp.com/album/marsupial-madness Facebook: ...", + "channelId": "UCps63j3krzAG4OyXeEyuhFw", + "title": "AdhesiveWombat - 8 Bit Adventure", + "channelTitle": "AdhesiveWombat", + "publishedAt": "2012-08-22T18:41:03.000Z", + "cover": "https://i.ytimg.com/vi/0HxZn6CzOIo/hqdefault.jpg" + } + + url = reverse('api:v1:providers:youtube:searchs') + response = client.post( + url, json.dumps(queries), content_type='application/json') + + assert expected == json.loads(response.content.decode('utf-8'))['1'][0] diff --git a/api/funkwhale_api/music/tests/__init__.py b/api/tests/users/__init__.py similarity index 100% rename from api/funkwhale_api/music/tests/__init__.py rename to api/tests/users/__init__.py diff --git a/api/tests/users/test_admin.py b/api/tests/users/test_admin.py new file mode 100644 index 0000000000000000000000000000000000000000..7645a02953b1caa44b6002a741fadf48c406449f --- /dev/null +++ b/api/tests/users/test_admin.py @@ -0,0 +1,35 @@ +from funkwhale_api.users.admin import MyUserCreationForm + + +def test_clean_username_success(db): + # Instantiate the form with a new username + form = MyUserCreationForm({ + 'username': 'alamode', + 'password1': '123456', + 'password2': '123456', + }) + # Run is_valid() to trigger the validation + valid = form.is_valid() + assert valid + + # Run the actual clean_username method + username = form.clean_username() + assert 'alamode' == username + + +def test_clean_username_false(factories): + user = factories['users.User']() + # Instantiate the form with the same username as self.user + form = MyUserCreationForm({ + 'username': user.username, + 'password1': '123456', + 'password2': '123456', + }) + # Run is_valid() to trigger the validation, which is going to fail + # because the username is already taken + valid = form.is_valid() + assert not valid + + # The form.errors dict should contain a single error called 'username' + assert len(form.errors) == 1 + assert 'username' in form.errors diff --git a/api/tests/users/test_models.py b/api/tests/users/test_models.py new file mode 100644 index 0000000000000000000000000000000000000000..57793f494bcc59a6994d2f014cf9ae7090495cd6 --- /dev/null +++ b/api/tests/users/test_models.py @@ -0,0 +1,4 @@ + +def test__str__(factories): + user = factories['users.User'](username='hello') + assert user.__str__() == 'hello' diff --git a/api/tests/users/test_views.py b/api/tests/users/test_views.py new file mode 100644 index 0000000000000000000000000000000000000000..42be77b7c18a2cd4b25829b1b567d044b0d9c6b9 --- /dev/null +++ b/api/tests/users/test_views.py @@ -0,0 +1,64 @@ +import json + +from django.test import RequestFactory +from django.urls import reverse + +from funkwhale_api.users.models import User + + +def test_can_create_user_via_api(settings, client, db): + url = reverse('rest_register') + data = { + 'username': 'test1', + 'email': 'test1@test.com', + 'password1': 'testtest', + 'password2': 'testtest', + } + settings.REGISTRATION_MODE = "public" + response = client.post(url, data) + assert response.status_code == 201 + + u = User.objects.get(email='test1@test.com') + assert u.username == 'test1' + + +def test_can_disable_registration_view(settings, client, db): + url = reverse('rest_register') + data = { + 'username': 'test1', + 'email': 'test1@test.com', + 'password1': 'testtest', + 'password2': 'testtest', + } + settings.REGISTRATION_MODE = "disabled" + response = client.post(url, data) + assert response.status_code == 403 + + +def test_can_fetch_data_from_api(client, factories): + url = reverse('api:v1:users:users-me') + response = client.get(url) + # login required + assert response.status_code == 401 + + user = factories['users.User']( + is_staff=True, + perms=[ + 'music.add_importbatch', + 'dynamic_preferences.change_globalpreferencemodel', + ] + ) + assert user.has_perm('music.add_importbatch') + client.login(username=user.username, password='test') + response = client.get(url) + assert response.status_code == 200 + + payload = json.loads(response.content.decode('utf-8')) + + assert payload['username'] == user.username + assert payload['is_staff'] == user.is_staff + assert payload['is_superuser'] == user.is_superuser + assert payload['email'] == user.email + assert payload['name'] == user.name + assert payload['permissions']['import.launch']['status'] + assert payload['permissions']['settings.change']['status']