From d310628cb525d73e77d5244e29efc088f5f00b0c Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Sun, 25 Mar 2018 22:27:38 +0200 Subject: [PATCH] refactored tests, no have __init__ everywhere --- api/tests/activity/__init__.py | 0 api/tests/channels/__init__.py | 0 api/tests/common/__init__.py | 0 api/tests/favorites/__init__.py | 0 api/tests/instance/__init__.py | 0 api/tests/music/conftest.py | 566 ++++++++++++++++++ api/tests/music/cover.py | 1 - api/tests/music/data.py | 502 ---------------- api/tests/music/mocking/lyricswiki.py | 32 - api/tests/music/test_api.py | 119 ++-- api/tests/music/test_import.py | 8 +- api/tests/music/test_lyrics.py | 23 +- api/tests/music/test_music.py | 45 +- api/tests/music/test_tasks.py | 19 +- api/tests/music/test_works.py | 16 +- .../musicbrainz/{data.py => conftest.py} | 42 +- api/tests/musicbrainz/test_api.py | 64 +- api/tests/playlists/__init__.py | 0 api/tests/radios/__init__.py | 0 dev.yml | 1 + 20 files changed, 751 insertions(+), 687 deletions(-) create mode 100644 api/tests/activity/__init__.py create mode 100644 api/tests/channels/__init__.py create mode 100644 api/tests/common/__init__.py create mode 100644 api/tests/favorites/__init__.py create mode 100644 api/tests/instance/__init__.py create mode 100644 api/tests/music/conftest.py delete mode 100644 api/tests/music/cover.py delete mode 100644 api/tests/music/data.py delete mode 100644 api/tests/music/mocking/lyricswiki.py rename api/tests/musicbrainz/{data.py => conftest.py} (96%) create mode 100644 api/tests/playlists/__init__.py create mode 100644 api/tests/radios/__init__.py diff --git a/api/tests/activity/__init__.py b/api/tests/activity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/channels/__init__.py b/api/tests/channels/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/common/__init__.py b/api/tests/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/favorites/__init__.py b/api/tests/favorites/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/instance/__init__.py b/api/tests/instance/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/music/conftest.py b/api/tests/music/conftest.py new file mode 100644 index 000000000..1d0fa4e38 --- /dev/null +++ b/api/tests/music/conftest.py @@ -0,0 +1,566 @@ +import pytest + + +_artists = {'search': {}, 'get': {}} + +_artists['search']['adhesive_wombat'] = { + 'artist-list': [ + { + 'type': 'Person', + 'ext:score': '100', + 'id': '62c3befb-6366-4585-b256-809472333801', + 'disambiguation': 'George Shaw', + 'gender': 'male', + 'area': {'sort-name': 'Raleigh', 'id': '3f8828b9-ba93-4604-9b92-1f616fa1abd1', 'name': 'Raleigh'}, + 'sort-name': 'Wombat, Adhesive', + 'life-span': {'ended': 'false'}, + 'name': 'Adhesive Wombat' + }, + { + 'country': 'SE', + 'type': 'Group', + 'ext:score': '42', + 'id': '61b34e69-7573-4208-bc89-7061bca5a8fc', + 'area': {'sort-name': 'Sweden', 'id': '23d10872-f5ae-3f0c-bf55-332788a16ecb', 'name': 'Sweden'}, + 'sort-name': 'Adhesive', + 'life-span': {'end': '2002-07-12', 'begin': '1994', 'ended': 'true'}, + 'name': 'Adhesive', + 'begin-area': { + 'sort-name': 'Katrineholm', + 'id': '02390d96-b5a3-4282-a38f-e64a95d08b7f', + 'name': 'Katrineholm' + }, + }, + ] +} +_artists['get']['adhesive_wombat'] = {'artist': _artists['search']['adhesive_wombat']['artist-list'][0]} + +_artists['get']['soad'] = { + 'artist': { + 'country': 'US', + 'isni-list': ['0000000121055332'], + 'type': 'Group', + 'area': { + 'iso-3166-1-code-list': ['US'], + 'sort-name': 'United States', + 'id': '489ce91b-6658-3307-9877-795b68554c98', + 'name': 'United States' + }, + 'begin-area': { + 'sort-name': 'Glendale', + 'id': '6db2e45d-d7f3-43da-ac0b-7ba5ca627373', + 'name': 'Glendale' + }, + 'id': 'cc0b7089-c08d-4c10-b6b0-873582c17fd6', + 'life-span': {'begin': '1994'}, + 'sort-name': 'System of a Down', + 'name': 'System of a Down' + } +} + +_albums = {'search': {}, 'get': {}, 'get_with_includes': {}} +_albums['search']['hypnotize'] = { + 'release-list': [ + { + "artist-credit": [ + { + "artist": { + "alias-list": [ + { + "alias": "SoaD", + "sort-name": "SoaD", + "type": "Search hint" + }, + { + "alias": "S.O.A.D.", + "sort-name": "S.O.A.D.", + "type": "Search hint" + }, + { + "alias": "System Of Down", + "sort-name": "System Of Down", + "type": "Search hint" + } + ], + "id": "cc0b7089-c08d-4c10-b6b0-873582c17fd6", + "name": "System of a Down", + "sort-name": "System of a Down" + } + } + ], + "artist-credit-phrase": "System of a Down", + "barcode": "", + "country": "US", + "date": "2005", + "ext:score": "100", + "id": "47ae093f-1607-49a3-be11-a15d335ccc94", + "label-info-list": [ + { + "catalog-number": "8-2796-93871-2", + "label": { + "id": "f5be9cfe-e1af-405c-a074-caeaed6797c0", + "name": "American Recordings" + } + }, + { + "catalog-number": "D162990", + "label": { + "id": "9a7d39a4-a887-40f3-a645-a9a136d1f13f", + "name": "BMG Direct Marketing, Inc." + } + } + ], + "medium-count": 1, + "medium-list": [ + { + "disc-count": 1, + "disc-list": [], + "format": "CD", + "track-count": 12, + "track-list": [] + } + ], + "medium-track-count": 12, + "packaging": "Digipak", + "release-event-list": [ + { + "area": { + "id": "489ce91b-6658-3307-9877-795b68554c98", + "iso-3166-1-code-list": [ + "US" + ], + "name": "United States", + "sort-name": "United States" + }, + "date": "2005" + } + ], + "release-group": { + "id": "72035143-d6ec-308b-8ee5-070b8703902a", + "primary-type": "Album", + "type": "Album" + }, + "status": "Official", + "text-representation": { + "language": "eng", + "script": "Latn" + }, + "title": "Hypnotize" + }, + { + "artist-credit": [ + { + "artist": { + "alias-list": [ + { + "alias": "SoaD", + "sort-name": "SoaD", + "type": "Search hint" + }, + { + "alias": "S.O.A.D.", + "sort-name": "S.O.A.D.", + "type": "Search hint" + }, + { + "alias": "System Of Down", + "sort-name": "System Of Down", + "type": "Search hint" + } + ], + "id": "cc0b7089-c08d-4c10-b6b0-873582c17fd6", + "name": "System of a Down", + "sort-name": "System of a Down" + } + } + ], + "artist-credit-phrase": "System of a Down", + "asin": "B000C6NRY8", + "barcode": "827969387115", + "country": "US", + "date": "2005-12-20", + "ext:score": "100", + "id": "8a4034a9-7834-3b7e-a6f0-d0791e3731fb", + "medium-count": 1, + "medium-list": [ + { + "disc-count": 0, + "disc-list": [], + "format": "Vinyl", + "track-count": 12, + "track-list": [] + } + ], + "medium-track-count": 12, + "release-event-list": [ + { + "area": { + "id": "489ce91b-6658-3307-9877-795b68554c98", + "iso-3166-1-code-list": [ + "US" + ], + "name": "United States", + "sort-name": "United States" + }, + "date": "2005-12-20" + } + ], + "release-group": { + "id": "72035143-d6ec-308b-8ee5-070b8703902a", + "primary-type": "Album", + "type": "Album" + }, + "status": "Official", + "text-representation": { + "language": "eng", + "script": "Latn" + }, + "title": "Hypnotize" + }, + ] +} +_albums['get']['hypnotize'] = {'release': _albums['search']['hypnotize']['release-list'][0]} +_albums['get_with_includes']['hypnotize'] = { + 'release': { + 'artist-credit': [ + {'artist': {'id': 'cc0b7089-c08d-4c10-b6b0-873582c17fd6', + 'name': 'System of a Down', + 'sort-name': 'System of a Down'}}], + 'artist-credit-phrase': 'System of a Down', + 'barcode': '', + 'country': 'US', + 'cover-art-archive': {'artwork': 'true', + 'back': 'false', + 'count': '1', + 'front': 'true'}, + 'date': '2005', + 'id': '47ae093f-1607-49a3-be11-a15d335ccc94', + 'medium-count': 1, + 'medium-list': [{'format': 'CD', + 'position': '1', + 'track-count': 12, + 'track-list': [{'id': '59f5cf9a-75b2-3aa3-abda-6807a87107b3', + 'length': '186000', + 'number': '1', + 'position': '1', + 'recording': {'id': '76d03fc5-758c-48d0-a354-a67de086cc68', + 'length': '186000', + 'title': 'Attack'}, + 'track_or_recording_length': '186000'}, + {'id': '3aaa28c1-12b1-3c2a-b90a-82e09e355608', + 'length': '239000', + 'number': '2', + 'position': '2', + 'recording': {'id': '327543b0-9193-48c5-83c9-01c7b36c8c0a', + 'length': '239000', + 'title': 'Dreaming'}, + 'track_or_recording_length': '239000'}, + {'id': 'a34fef19-e637-3436-b7eb-276ff2814d6f', + 'length': '147000', + 'number': '3', + 'position': '3', + 'recording': {'id': '6e27866c-07a1-425d-bb4f-9d9e728db344', + 'length': '147000', + 'title': 'Kill Rock ’n Roll'}, + 'track_or_recording_length': '147000'}, + {'id': '72a4e5c0-c150-3ba1-9ceb-3ab82648af25', + 'length': '189000', + 'number': '4', + 'position': '4', + 'recording': {'id': '7ff8a67d-c8e2-4b3a-a045-7ad3561d0605', + 'length': '189000', + 'title': 'Hypnotize'}, + 'track_or_recording_length': '189000'}, + {'id': 'a748fa6e-b3b7-3b22-89fb-a038ec92ac32', + 'length': '178000', + 'number': '5', + 'position': '5', + 'recording': {'id': '19b6eb6a-0e76-4ef7-b63f-959339dbd5d2', + 'length': '178000', + 'title': 'Stealing Society'}, + 'track_or_recording_length': '178000'}, + {'id': '5c5a8d4e-e21a-317e-a719-6e2dbdefa5d2', + 'length': '216000', + 'number': '6', + 'position': '6', + 'recording': {'id': 'c3c2afe1-ee9a-47cb-b3c6-ff8100bc19d5', + 'length': '216000', + 'title': 'Tentative'}, + 'track_or_recording_length': '216000'}, + {'id': '265718ba-787f-3193-947b-3b6fa69ffe96', + 'length': '175000', + 'number': '7', + 'position': '7', + 'recording': {'id': '96f804e1-f600-4faa-95a6-ce597e7db120', + 'length': '175000', + 'title': 'U‐Fig'}, + 'title': 'U-Fig', + 'track_or_recording_length': '175000'}, + {'id': 'cdcf8572-3060-31ca-a72c-1ded81ca1f7a', + 'length': '328000', + 'number': '8', + 'position': '8', + 'recording': {'id': '26ba38f0-b26b-48b7-8e77-226b22a55f79', + 'length': '328000', + 'title': 'Holy Mountains'}, + 'track_or_recording_length': '328000'}, + {'id': 'f9f00cb0-5635-3217-a2a0-bd61917eb0df', + 'length': '171000', + 'number': '9', + 'position': '9', + 'recording': {'id': '039f3379-3a69-4e75-a882-df1c4e1608aa', + 'length': '171000', + 'title': 'Vicinity of Obscenity'}, + 'track_or_recording_length': '171000'}, + {'id': 'cdd45914-6741-353e-bbb5-d281048ff24f', + 'length': '164000', + 'number': '10', + 'position': '10', + 'recording': {'id': 'c24d541a-a9a8-4a22-84c6-5e6419459cf8', + 'length': '164000', + 'title': 'She’s Like Heroin'}, + 'track_or_recording_length': '164000'}, + {'id': 'cfcf12ac-6831-3dd6-a2eb-9d0bfeee3f6d', + 'length': '167000', + 'number': '11', + 'position': '11', + 'recording': {'id': '0aff4799-849f-4f83-84f4-22cabbba2378', + 'length': '167000', + 'title': 'Lonely Day'}, + 'track_or_recording_length': '167000'}, + {'id': '7e38bb38-ff62-3e41-a670-b7d77f578a1f', + 'length': '220000', + 'number': '12', + 'position': '12', + 'recording': {'id': 'e1b4d90f-2f44-4fe6-a826-362d4e3d9b88', + 'length': '220000', + 'title': 'Soldier Side'}, + 'track_or_recording_length': '220000'}]}], + 'packaging': 'Digipak', + 'quality': 'normal', + 'release-event-count': 1, + 'release-event-list': [{'area': {'id': '489ce91b-6658-3307-9877-795b68554c98', + 'iso-3166-1-code-list': ['US'], + 'name': 'United States', + 'sort-name': 'United States'}, + 'date': '2005'}], + 'status': 'Official', + 'text-representation': {'language': 'eng', 'script': 'Latn'}, + 'title': 'Hypnotize'}} + +_albums['get']['marsupial'] = { + 'release': { + "artist-credit": [ + { + "artist": { + "disambiguation": "George Shaw", + "id": "62c3befb-6366-4585-b256-809472333801", + "name": "Adhesive Wombat", + "sort-name": "Wombat, Adhesive" + } + } + ], + "artist-credit-phrase": "Adhesive Wombat", + "country": "XW", + "cover-art-archive": { + "artwork": "true", + "back": "false", + "count": "1", + "front": "true" + }, + "date": "2013-06-05", + "id": "a50d2a81-2a50-484d-9cb4-b9f6833f583e", + "packaging": "None", + "quality": "normal", + "release-event-count": 1, + "release-event-list": [ + { + "area": { + "id": "525d4e18-3d00-31b9-a58b-a146a916de8f", + "iso-3166-1-code-list": [ + "XW" + ], + "name": "[Worldwide]", + "sort-name": "[Worldwide]" + }, + "date": "2013-06-05" + } + ], + "status": "Official", + "text-representation": { + "language": "eng", + "script": "Latn" + }, + "title": "Marsupial Madness" + } +} + +_tracks = {'search': {}, 'get': {}} + +_tracks['search']['8bitadventures'] = { + 'recording-list': [ + { + "artist-credit": [ + { + "artist": { + "disambiguation": "George Shaw", + "id": "62c3befb-6366-4585-b256-809472333801", + "name": "Adhesive Wombat", + "sort-name": "Wombat, Adhesive" + } + } + ], + "artist-credit-phrase": "Adhesive Wombat", + "ext:score": "100", + "id": "9968a9d6-8d92-4051-8f76-674e157b6eed", + "length": "271000", + "release-list": [ + { + "country": "XW", + "date": "2013-06-05", + "id": "a50d2a81-2a50-484d-9cb4-b9f6833f583e", + "medium-list": [ + { + "format": "Digital Media", + "position": "1", + "track-count": 11, + "track-list": [ + { + "id": "64d43604-c1ee-4f45-a02c-030672d2fe27", + "length": "271000", + "number": "1", + "title": "8-Bit Adventure", + "track_or_recording_length": "271000" + } + ] + } + ], + "medium-track-count": 11, + "release-event-list": [ + { + "area": { + "id": "525d4e18-3d00-31b9-a58b-a146a916de8f", + "iso-3166-1-code-list": [ + "XW" + ], + "name": "[Worldwide]", + "sort-name": "[Worldwide]" + }, + "date": "2013-06-05" + } + ], + "release-group": { + "id": "447b4979-2178-405c-bfe6-46bf0b09e6c7", + "primary-type": "Album", + "type": "Album" + }, + "status": "Official", + "title": "Marsupial Madness" + } + ], + "title": "8-Bit Adventure", + "tag-list": [ + { + "count": "2", + "name": "techno" + }, + { + "count": "2", + "name": "good-music" + }, + ], + }, + ] +} + +_tracks['get']['8bitadventures'] = {'recording': _tracks['search']['8bitadventures']['recording-list'][0]} +_tracks['get']['chop_suey'] = { + 'recording': { + 'id': '46c7368a-013a-47b6-97cc-e55e7ab25213', + 'length': '210240', + 'title': 'Chop Suey!', + 'work-relation-list': [{'target': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5', + 'type': 'performance', + 'type-id': 'a3005666-a872-32c3-ad06-98af558e99b0', + 'work': {'id': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5', + 'language': 'eng', + 'title': 'Chop Suey!'}}]}} + +_works = {'search': {}, 'get': {}} +_works['get']['chop_suey'] = {'work': {'id': 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5', + 'language': 'eng', + 'recording-relation-list': [{'direction': 'backward', + 'recording': {'disambiguation': 'edit', + 'id': '07ca77cf-f513-4e9c-b190-d7e24bbad448', + 'length': '170893', + 'title': 'Chop Suey!'}, + 'target': '07ca77cf-f513-4e9c-b190-d7e24bbad448', + 'type': 'performance', + 'type-id': 'a3005666-a872-32c3-ad06-98af558e99b0'}, + ], + 'title': 'Chop Suey!', + 'type': 'Song', + 'url-relation-list': [{'direction': 'backward', + 'target': 'http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!', + 'type': 'lyrics', + 'type-id': 'e38e65aa-75e0-42ba-ace0-072aeb91a538'}]}} + + +@pytest.fixture() +def artists(): + return _artists + + +@pytest.fixture() +def albums(): + return _albums + + +@pytest.fixture() +def tracks(): + return _tracks + + +@pytest.fixture() +def works(): + return _works + + +@pytest.fixture() +def lyricswiki_content(): + return """ + + + + + + + + + + + + + + + + + + + + + + +System Of A Down:Chop Suey! Lyrics - LyricWikia - Wikia + + +
+We're rolling "Suicide".

Wake up (wake up)
Grab a brush and put on a little makeup
Hide the scars to fade away the shakeup (hide the scars to fade away the)
Why'd you leave the keys upon the table?
Here you go, create another fable

You wanted to
Grab a brush and put a little makeup
You wanted to
Hide the scars to fade away the shakeup
You wanted to
Why'd you leave the keys upon the table?
You wanted to

I don't think you trust
In my self-righteous suicide
I cry when angels deserve to die

Wake up (wake up)
Grab a brush and put on a little makeup
Hide the scars to fade away the (hide the scars to fade away the)
Why'd you leave the keys upon the table?
Here you go, create another fable

You wanted to
Grab a brush and put a little makeup
You wanted to
Hide the scars to fade away the shakeup
You wanted to
Why'd you leave the keys upon the table?
You wanted to

I don't think you trust
In my self-righteous suicide
I cry when angels deserve to die
In my self-righteous suicide
I cry when angels deserve to die

Father (father)
Father (father)
Father (father)
Father (father)
Father, into your hands I commit my spirit
Father, into your hands

Why have you forsaken me?
In your eyes forsaken me
In your thoughts forsaken me
In your heart forsaken me, oh

Trust in my self-righteous suicide
I cry when angels deserve to die
In my self-righteous suicide
I cry when angels deserve to die +
+ +""" + + +@pytest.fixture() +def binary_cover(): + return b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x02\x01\x00H\x00H\x00\x00\xff\xed\x08\xaePhotoshop 3.0\x008BIM\x03\xe9\x00\x00\x00\x00\x00x\x00\x03\x00\x00\x00H\x00H\x00\x00\x00\x00\x02\xd8\x02(\xff\xe1\xff\xe2\x02\xf9\x02F\x03G\x05(\x03\xfc\x00\x02\x00\x00\x00H\x00H\x00\x00\x00\x00\x02\xd8\x02(\x00\x01\x00\x00\x00d\x00\x00\x00\x01\x00\x03\x03\x03\x00\x00\x00\x01\'\x0f\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x08\x00\x19\x01\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008BIM\x03\xed\x00\x00\x00\x00\x00\x10\x00H\x00\x00\x00\x01\x00\x01\x00H\x00\x00\x00\x01\x00\x018BIM\x03\xf3\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x008BIM\x04\n\x00\x00\x00\x00\x00\x01\x00\x008BIM\'\x10\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x028BIM\x03\xf5\x00\x00\x00\x00\x00H\x00/ff\x00\x01\x00lff\x00\x06\x00\x00\x00\x00\x00\x01\x00/ff\x00\x01\x00\xa1\x99\x9a\x00\x06\x00\x00\x00\x00\x00\x01\x002\x00\x00\x00\x01\x00Z\x00\x00\x00\x06\x00\x00\x00\x00\x00\x01\x005\x00\x00\x00\x01\x00-\x00\x00\x00\x06\x00\x00\x00\x00\x00\x018BIM\x03\xf8\x00\x00\x00\x00\x00p\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xe8\x00\x008BIM\x04\x00\x00\x00\x00\x00\x00\x02\x00\x018BIM\x04\x02\x00\x00\x00\x00\x00\x04\x00\x00\x00\x008BIM\x04\x08\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x02@\x00\x00\x02@\x00\x00\x00\x008BIM\x04\t\x00\x00\x00\x00\x06\x9b\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x80\x00\x00\x01\x80\x00\x00\xc0\x00\x00\x00\x06\x7f\x00\x18\x00\x01\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x02\x01\x00H\x00H\x00\x00\xff\xfe\x00\'File written by Adobe Photoshop\xa8 4.0\x00\xff\xee\x00\x0eAdobe\x00d\x80\x00\x00\x00\x01\xff\xdb\x00\x84\x00\x0c\x08\x08\x08\t\x08\x0c\t\t\x0c\x11\x0b\n\x0b\x11\x15\x0f\x0c\x0c\x0f\x15\x18\x13\x13\x15\x13\x13\x18\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\r\x0b\x0b\r\x0e\r\x10\x0e\x0e\x10\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x00\x80\x00\x80\x03\x01"\x00\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x08\xff\xc4\x01?\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x01\x02\x04\x05\x06\x07\x08\t\n\x0b\x01\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x10\x00\x01\x04\x01\x03\x02\x04\x02\x05\x07\x06\x08\x05\x03\x0c3\x01\x00\x02\x11\x03\x04!\x121\x05AQa\x13"q\x812\x06\x14\x91\xa1\xb1B#$\x15R\xc1b34r\x82\xd1C\x07%\x92S\xf0\xe1\xf1cs5\x16\xa2\xb2\x83&D\x93TdE\xc2\xa3t6\x17\xd2U\xe2e\xf2\xb3\x84\xc3\xd3u\xe3\xf3F\'\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf7\x11\x00\x02\x02\x01\x02\x04\x04\x03\x04\x05\x06\x07\x07\x06\x055\x01\x00\x02\x11\x03!1\x12\x04AQaq"\x13\x052\x81\x91\x14\xa1\xb1B#\xc1R\xd1\xf03$b\xe1r\x82\x92CS\x15cs4\xf1%\x06\x16\xa2\xb2\x83\x07&5\xc2\xd2D\x93T\xa3\x17dEU6te\xe2\xf2\xb3\x84\xc3\xd3u\xe3\xf3F\x94\xa4\x85\xb4\x95\xc4\xd4\xe4\xf4\xa5\xb5\xc5\xd5\xe5\xf5Vfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6\'7GWgw\x87\x97\xa7\xb7\xc7\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\xf5T\x92I%)$\x92IJI$\x92R\x92I$\x94\xa4\x92I%)$\x92IJI$\x92R\x92I$\x94\xff\x00\xff\xd0\xf5T\x92I%)$\x92IJI%\xe7\xff\x00Z\x7f\xc6\xbf\xfc\xde\xeb\xb9]\x1f\xf6_\xda~\xcd\xe9\xfe\x9b\xed\x1e\x9e\xefR\xba\xef\xfeo\xec\xf6\xed\xdb\xea\xec\xfeq%>\x80\x92\xf2\xaf\xfc}?\xf3I\xff\x00\xb3_\xfb\xe8\x97\xfe>\x9f\xf9\xa4\xff\x00\xd9\xaf\xfd\xf4IO\xaa\xa4\xbc\xab\xff\x00\x1fO\xfc\xd2\x7f\xec\xd7\xfe\xfa%\xff\x00\x8f\xa7\xfei?\xf6k\xff\x00}\x12S\xea\xa9.+\xeaW\xf8\xc8\xff\x00\x9d}V\xde\x9d\xfb;\xec~\x96;\xb2=O[\xd5\x9d\xaf\xaa\xad\x9b=\n\x7f\xd3}-\xeb\xb5IJI$\x92R\x92I$\x94\xff\x00\xff\xd1\xf5T\x92I%)$\x97\x9f\xff\x00\x8d\x7f\xad=w\xea\xf7\xec\xbf\xd8\xf9_f\xfbO\xda=o\xd1\xd7f\xefO\xec\xfe\x9f\xf3\xf5\xdb\xb7o\xabg\xd0IO\xa0/\x9f\xff\x00\xc6\x97\xfe.\xfa\x9f\xfdc\xff\x00m\xf1\xd2\xff\x00\xc7K\xeb\xdf\xfeY\xff\x00\xe0\x18\xff\x00\xfb\xce\xb9\xfe\xa9\xd53\xfa\xbe}\xbdG\xa8\xdb\xeb\xe5\xdf\xb7\xd4\xb3kY;\x1a\xda\x99\xec\xa9\xac\xaf\xf9\xb63\xf3\x12SU$\x92IJI$\x92S\xdf\xff\x00\x89O\xfcUe\x7f\xe1\x0b?\xf3\xf6*\xf6\xb5\xf3/D\xeb\xfd[\xa0\xe5?3\xa4\xdf\xf6l\x8b+59\xfb\x18\xf9a-\xb1\xcd\xdb{-g\xd3\xa9\x8bk\xff\x00\x1d/\xaf\x7f\xf9g\xff\x00\x80c\xff\x00\xef:J~\x80Iq\xff\x00\xe2\xbf\xaf\xf5n\xbd\xd023:\xb5\xff\x00i\xc8\xaf-\xf55\xfb\x18\xc8`\xae\x8b\x1a\xdd\xb42\xa6};^\xbb\x04\x94\xa4\x92I%?\xff\xd2\xf5T\x92I%)yW\xf8\xf4\xff\x00\xbcO\xfd\n\xff\x00\xddE\xea\xab\xca\xbf\xc7\xa7\xfd\xe2\x7f\xe8W\xfe\xea$\xa7\xca\x92I$\x94\xa4\x92I%)$\x92IJI$\x92S\xed_\xe2S\xff\x00\x12\xb9_\xf8~\xcf\xfc\xf3\x8a\xbd\x01y\xff\x00\xf8\x94\xff\x00\xc4\xaeW\xfe\x1f\xb3\xff\x00<\xe2\xaf@IJI$\x92S\xff\xd3\xf5T\x92I%)yW\xf8\xf4\xff\x00\xbcO\xfd\n\xff\x00\xddE\xea\xab\xca\xbf\xc7\xa7\xfd\xe2\x7f\xe8W\xfe\xea$\xa7\xca\x92I$\x94\xa4\x92I%)$\x92IJI$\x92S\xed_\xe2S\xff\x00\x12\xb9_\xf8~\xcf\xfc\xf3\x8a\xbd\x01y\xff\x00\xf8\x94\xff\x00\xc4\xaeW\xfe\x1f\xb3\xff\x00<\xe2\xaf@IJI$\x92S\xff\xd4\xf5T\x92I%)q_\xe3#\xeaWU\xfa\xd7\xfb;\xf6u\xb8\xf5}\x8f\xd6\xf5>\xd0\xe7\xb6}_Cf\xcfJ\xab\xbf\xd0\xbfr\xedRIO\x8a\x7f\xe3)\xf5\xab\xfe\xe5`\x7f\xdb\x97\x7f\xef*\xe4:\xff\x00D\xca\xe8=Z\xfe\x93\x98\xfa\xec\xc8\xc6\xd9\xbd\xd5\x12Xw\xb1\x97\xb7k\xacmO\xfa\x16\xfe\xe2\xfai|\xff\x00\xfe4\xbf\xf1w\xd4\xff\x00\xeb\x1f\xfbo\x8e\x92\x9eU$\x92IJI$\x92S\xb1\xf5_\xea\xbfP\xfa\xd1\xd4,\xc0\xc0\xb2\x9a\xad\xaa\x93{\x9dys[\xb5\xae\xae\xa2\x01\xaa\xbb\x9d\xbfu\xcd\xfc\xd5\xd3\xff\x00\xe3)\xf5\xab\xfe\xe5`\x7f\xdb\x97\x7f\xef*_\xe2S\xff\x00\x15Y_\xf8B\xcf\xfc\xfd\x8a\xbd\xad%<\xbf\xf8\xbc\xfa\xaf\xd4>\xab\xf4[\xb03\xec\xa6\xdbm\xc9u\xedu\x05\xcen\xd7WM@\x13mt\xbb~\xea]\xf9\xab\xa8I$\x94\xa4\x92I%?\xff\xd5\xf5T\x92I%)$\x92IJ\\\x7f_\xff\x00\x15\xfd\x03\xafuk\xfa\xb6fF]y\x19;7\xb6\xa7\xd6\x1861\x947kl\xa2\xd7\xfd\n\xbf}v\t$\xa7\xcf\xff\x00\xf1\x94\xfa\xab\xff\x00r\xb3\xff\x00\xed\xca\x7f\xf7\x95/\xfce>\xaa\xff\x00\xdc\xac\xff\x00\xfbr\x9f\xfd\xe5^\x80\x92J|\xff\x00\xff\x00\x19O\xaa\xbf\xf7+?\xfe\xdc\xa7\xff\x00yR\xff\x00\xc6S\xea\xaf\xfd\xca\xcf\xff\x00\xb7)\xff\x00\xdeU\xe8\t$\xa7\x97\xfa\xaf\xfe/:/\xd5~\xa1f~\x05\xd96\xdbm&\x876\xf7V\xe6\xeds\xab\xb4\x90*\xa6\x97o\xddK\x7f9u\t$\x92\x94\x92I$\xa5$\x92I)\xff\xd6\xf5T\x92I%)$\x92IJI$\x92R\x92I$\x94\xa4\x92I%)$\x92IJI$\x92R\x92I$\x94\xff\x00\xff\xd9\x008BIM\x04\x06\x00\x00\x00\x00\x00\x07\x00\x03\x00\x00\x00\x01\x01\x00\xff\xfe\x00\'File written by Adobe Photoshop\xa8 4.0\x00\xff\xee\x00\x0eAdobe\x00d\x00\x00\x00\x00\x01\xff\xdb\x00\x84\x00\n\x07\x07\x07\x08\x07\n\x08\x08\n\x0f\n\x08\n\x0f\x12\r\n\n\r\x12\x14\x10\x10\x12\x10\x10\x14\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0b\x0c\x0c\x15\x13\x15"\x18\x18"\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x00\t\x00\t\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x02\xff\xc4\x01\xa2\x00\x00\x00\x07\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x04\x05\x03\x02\x06\x01\x00\x07\x08\t\n\x0b\x01\x00\x02\x02\x03\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x10\x00\x02\x01\x03\x03\x02\x04\x02\x06\x07\x03\x04\x02\x06\x02s\x01\x02\x03\x11\x04\x00\x05!\x121AQ\x06\x13a"q\x81\x142\x91\xa1\x07\x15\xb1B#\xc1R\xd1\xe13\x16b\xf0$r\x82\xf1%C4S\x92\xa2\xb2cs\xc25D\'\x93\xa3\xb36\x17Tdt\xc3\xd2\xe2\x08&\x83\t\n\x18\x19\x84\x94EF\xa4\xb4V\xd3U(\x1a\xf2\xe3\xf3\xc4\xd4\xe4\xf4eu\x85\x95\xa5\xb5\xc5\xd5\xe5\xf5fv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf78HXhx\x88\x98\xa8\xb8\xc8\xd8\xe8\xf8)9IYiy\x89\x99\xa9\xb9\xc9\xd9\xe9\xf9*:JZjz\x8a\x9a\xaa\xba\xca\xda\xea\xfa\x11\x00\x02\x02\x01\x02\x03\x05\x05\x04\x05\x06\x04\x08\x03\x03m\x01\x00\x02\x11\x03\x04!\x121A\x05Q\x13a"\x06q\x81\x912\xa1\xb1\xf0\x14\xc1\xd1\xe1#B\x15Rbr\xf13$4C\x82\x16\x92S%\xa2c\xb2\xc2\x07s\xd25\xe2D\x83\x17T\x93\x08\t\n\x18\x19&6E\x1a\'dtU7\xf2\xa3\xb3\xc3()\xd3\xe3\xf3\x84\x94\xa4\xb4\xc4\xd4\xe4\xf4eu\x85\x95\xa5\xb5\xc5\xd5\xe5\xf5FVfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf78HXhx\x88\x98\xa8\xb8\xc8\xd8\xe8\xf89IYiy\x89\x99\xa9\xb9\xc9\xd9\xe9\xf9*:JZjz\x8a\x9a\xaa\xba\xca\xda\xea\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\x91\xea\xfa\xbf\xe6D_\x99\x16\x96\x16\x16\x8c\xdeWf\x84;\x88U\xa1hY\x7f\xd3\'\x9e\xf3\xedCq\x0bz\xfe\x94^\xbc?\xdc\xdb\xff\x00\xa3\xcd\xeb\x7f\xa4\xaa\xf4\x80\x92\xf2\xaf\xfc}?\xf3I\xff\x00\xb3_\xfb\xe8\x97\xfe>\x9f\xf9\xa4\xff\x00\xd9\xaf\xfd\xf4IO\xaa\xa4\xbc\xab\xff\x00\x1fO\xfc\xd2\x7f\xec\xd7\xfe\xfa%\xff\x00\x8f\xa7\xfei?\xf6k\xff\x00}\x12S\xea\xa9.+\xeaW\xf8\xc8\xff\x00\x9d}V\xde\x9d\xfb;\xec~\x96;\xb2=O[\xd5\x9d\xaf\xaa\xad\x9b=\n\x7f\xd3}-\xeb\xb5IJI$\x92R\x92I$\x94\xff\x00\xff\xd1\xf5T\x92I%)$\x97\x9f\xff\x00\x8d\x7f\xad=w\xea\xf7\xec\xbf\xd8\xf9_f\xfbO\xda=o\xd1\xd7f\xefO\xec\xfe\x9f\xf3\xf5\xdb\xb7o\xabg\xd0IO\xa0/\x9f\xff\x00\xc6\x97\xfe.\xfa\x9f\xfdc\xff\x00m\xf1\xd2\xff\x00\xc7K\xeb\xdf\xfeY\xff\x00\xe0\x18\xff\x00\xfb\xce\xb9\xfe\xa9\xd53\xfa\xbe}\xbdG\xa8\xdb\xeb\xe5\xdf\xb7\xd4\xb3kY;\x1a\xda\x99\xec\xa9\xac\xaf\xf9\xb63\xf3\x12SU$\x92IJI$\x92S\xdf\xff\x00\x89O\xfcUe\x7f\xe1\x0b?\xf3\xf6*\xf6\xb5\xf3/D\xeb\xfd[\xa0\xe5?3\xa4\xdf\xf6l\x8b+59\xfb\x18\xf9a-\xb1\xcd\xdb{-g\xd3\xa9\x8bk\xff\x00\x1d/\xaf\x7f\xf9g\xff\x00\x80c\xff\x00\xef:J~\x80Iq\xff\x00\xe2\xbf\xaf\xf5n\xbd\xd023:\xb5\xff\x00i\xc8\xaf-\xf55\xfb\x18\xc8`\xae\x8b\x1a\xdd\xb42\xa6};^\xbb\x04\x94\xa4\x92I%?\xff\xd2\xf5T\x92I%)yW\xf8\xf4\xff\x00\xbcO\xfd\n\xff\x00\xddE\xea\xab\xca\xbf\xc7\xa7\xfd\xe2\x7f\xe8W\xfe\xea$\xa7\xca\x92I$\x94\xa4\x92I%)$\x92IJI$\x92S\xed_\xe2S\xff\x00\x12\xb9_\xf8~\xcf\xfc\xf3\x8a\xbd\x01y\xff\x00\xf8\x94\xff\x00\xc4\xaeW\xfe\x1f\xb3\xff\x00<\xe2\xaf@IJI$\x92S\xff\xd3\xf5T\x92I%)yW\xf8\xf4\xff\x00\xbcO\xfd\n\xff\x00\xddE\xea\xab\xca\xbf\xc7\xa7\xfd\xe2\x7f\xe8W\xfe\xea$\xa7\xca\x92I$\x94\xa4\x92I%)$\x92IJI$\x92S\xed_\xe2S\xff\x00\x12\xb9_\xf8~\xcf\xfc\xf3\x8a\xbd\x01y\xff\x00\xf8\x94\xff\x00\xc4\xaeW\xfe\x1f\xb3\xff\x00<\xe2\xaf@IJI$\x92S\xff\xd4\xf5T\x92I%)q_\xe3#\xeaWU\xfa\xd7\xfb;\xf6u\xb8\xf5}\x8f\xd6\xf5>\xd0\xe7\xb6}_Cf\xcfJ\xab\xbf\xd0\xbfr\xedRIO\x8a\x7f\xe3)\xf5\xab\xfe\xe5`\x7f\xdb\x97\x7f\xef*\xe4:\xff\x00D\xca\xe8=Z\xfe\x93\x98\xfa\xec\xc8\xc6\xd9\xbd\xd5\x12Xw\xb1\x97\xb7k\xacmO\xfa\x16\xfe\xe2\xfai|\xff\x00\xfe4\xbf\xf1w\xd4\xff\x00\xeb\x1f\xfbo\x8e\x92\x9eU$\x92IJI$\x92S\xb1\xf5_\xea\xbfP\xfa\xd1\xd4,\xc0\xc0\xb2\x9a\xad\xaa\x93{\x9dys[\xb5\xae\xae\xa2\x01\xaa\xbb\x9d\xbfu\xcd\xfc\xd5\xd3\xff\x00\xe3)\xf5\xab\xfe\xe5`\x7f\xdb\x97\x7f\xef*_\xe2S\xff\x00\x15Y_\xf8B\xcf\xfc\xfd\x8a\xbd\xad%<\xbf\xf8\xbc\xfa\xaf\xd4>\xab\xf4[\xb03\xec\xa6\xdbm\xc9u\xedu\x05\xcen\xd7WM@\x13mt\xbb~\xea]\xf9\xab\xa8I$\x94\xa4\x92I%?\xff\xd5\xf5T\x92I%)$\x92IJ\\\x7f_\xff\x00\x15\xfd\x03\xafuk\xfa\xb6fF]y\x19;7\xb6\xa7\xd6\x1861\x947kl\xa2\xd7\xfd\n\xbf}v\t$\xa7\xcf\xff\x00\xf1\x94\xfa\xab\xff\x00r\xb3\xff\x00\xed\xca\x7f\xf7\x95/\xfce>\xaa\xff\x00\xdc\xac\xff\x00\xfbr\x9f\xfd\xe5^\x80\x92J|\xff\x00\xff\x00\x19O\xaa\xbf\xf7+?\xfe\xdc\xa7\xff\x00yR\xff\x00\xc6S\xea\xaf\xfd\xca\xcf\xff\x00\xb7)\xff\x00\xdeU\xe8\t$\xa7\x97\xfa\xaf\xfe/:/\xd5~\xa1f~\x05\xd96\xdbm&\x876\xf7V\xe6\xeds\xab\xb4\x90*\xa6\x97o\xddK\x7f9u\t$\x92\x94\x92I$\xa5$\x92I)\xff\xd6\xf5T\x92I%)$\x92IJI$\x92R\x92I$\x94\xa4\x92I%)$\x92IJI$\x92R\x92I$\x94\xff\x00\xff\xd9\x008BIM\x04\x06\x00\x00\x00\x00\x00\x07\x00\x03\x00\x00\x00\x01\x01\x00\xff\xfe\x00\'File written by Adobe Photoshop\xa8 4.0\x00\xff\xee\x00\x0eAdobe\x00d\x00\x00\x00\x00\x01\xff\xdb\x00\x84\x00\n\x07\x07\x07\x08\x07\n\x08\x08\n\x0f\n\x08\n\x0f\x12\r\n\n\r\x12\x14\x10\x10\x12\x10\x10\x14\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x01\x0b\x0c\x0c\x15\x13\x15"\x18\x18"\x14\x0e\x0e\x0e\x14\x14\x0e\x0e\x0e\x0e\x14\x11\x0c\x0c\x0c\x0c\x0c\x11\x11\x0c\x0c\x0c\x0c\x0c\x0c\x11\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\xff\xc0\x00\x11\x08\x00\t\x00\t\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01\xff\xdd\x00\x04\x00\x02\xff\xc4\x01\xa2\x00\x00\x00\x07\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x04\x05\x03\x02\x06\x01\x00\x07\x08\t\n\x0b\x01\x00\x02\x02\x03\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x10\x00\x02\x01\x03\x03\x02\x04\x02\x06\x07\x03\x04\x02\x06\x02s\x01\x02\x03\x11\x04\x00\x05!\x121AQ\x06\x13a"q\x81\x142\x91\xa1\x07\x15\xb1B#\xc1R\xd1\xe13\x16b\xf0$r\x82\xf1%C4S\x92\xa2\xb2cs\xc25D\'\x93\xa3\xb36\x17Tdt\xc3\xd2\xe2\x08&\x83\t\n\x18\x19\x84\x94EF\xa4\xb4V\xd3U(\x1a\xf2\xe3\xf3\xc4\xd4\xe4\xf4eu\x85\x95\xa5\xb5\xc5\xd5\xe5\xf5fv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf67GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf78HXhx\x88\x98\xa8\xb8\xc8\xd8\xe8\xf8)9IYiy\x89\x99\xa9\xb9\xc9\xd9\xe9\xf9*:JZjz\x8a\x9a\xaa\xba\xca\xda\xea\xfa\x11\x00\x02\x02\x01\x02\x03\x05\x05\x04\x05\x06\x04\x08\x03\x03m\x01\x00\x02\x11\x03\x04!\x121A\x05Q\x13a"\x06q\x81\x912\xa1\xb1\xf0\x14\xc1\xd1\xe1#B\x15Rbr\xf13$4C\x82\x16\x92S%\xa2c\xb2\xc2\x07s\xd25\xe2D\x83\x17T\x93\x08\t\n\x18\x19&6E\x1a\'dtU7\xf2\xa3\xb3\xc3()\xd3\xe3\xf3\x84\x94\xa4\xb4\xc4\xd4\xe4\xf4eu\x85\x95\xa5\xb5\xc5\xd5\xe5\xf5FVfv\x86\x96\xa6\xb6\xc6\xd6\xe6\xf6GWgw\x87\x97\xa7\xb7\xc7\xd7\xe7\xf78HXhx\x88\x98\xa8\xb8\xc8\xd8\xe8\xf89IYiy\x89\x99\xa9\xb9\xc9\xd9\xe9\xf9*:JZjz\x8a\x9a\xaa\xba\xca\xda\xea\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00?\x00\x91\xea\xfa\xbf\xe6D_\x99\x16\x96\x16\x16\x8c\xdeWf\x84;\x88U\xa1hY\x7f\xd3\'\x9e\xf3\xedCq\x0bz\xfe\x94^\xbc?\xdc\xdb\xff\x00\xa3\xcd\xeb\x7f\xa4\xaa\xf4 - - - - - - - - - - - - - - - - - - - - - - -System Of A Down:Chop Suey! Lyrics - LyricWikia - Wikia - - -
-We're rolling "Suicide".

Wake up (wake up)
Grab a brush and put on a little makeup
Hide the scars to fade away the shakeup (hide the scars to fade away the)
Why'd you leave the keys upon the table?
Here you go, create another fable

You wanted to
Grab a brush and put a little makeup
You wanted to
Hide the scars to fade away the shakeup
You wanted to
Why'd you leave the keys upon the table?
You wanted to

I don't think you trust
In my self-righteous suicide
I cry when angels deserve to die

Wake up (wake up)
Grab a brush and put on a little makeup
Hide the scars to fade away the (hide the scars to fade away the)
Why'd you leave the keys upon the table?
Here you go, create another fable

You wanted to
Grab a brush and put a little makeup
You wanted to
Hide the scars to fade away the shakeup
You wanted to
Why'd you leave the keys upon the table?
You wanted to

I don't think you trust
In my self-righteous suicide
I cry when angels deserve to die
In my self-righteous suicide
I cry when angels deserve to die

Father (father)
Father (father)
Father (father)
Father (father)
Father, into your hands I commit my spirit
Father, into your hands

Why have you forsaken me?
In your eyes forsaken me
In your thoughts forsaken me
In your heart forsaken me, oh

Trust in my self-righteous suicide
I cry when angels deserve to die
In my self-righteous suicide
I cry when angels deserve to die -
- - -""" diff --git a/api/tests/music/test_api.py b/api/tests/music/test_api.py index 8196d3c09..625bf9d2b 100644 --- a/api/tests/music/test_api.py +++ b/api/tests/music/test_api.py @@ -8,21 +8,21 @@ from funkwhale_api.musicbrainz import api from funkwhale_api.music import serializers from funkwhale_api.music import tasks -from . import data as api_data DATA_DIR = os.path.dirname(os.path.abspath(__file__)) -def test_can_submit_youtube_url_for_track_import(mocker, superuser_client): +def test_can_submit_youtube_url_for_track_import( + artists, albums, tracks, mocker, superuser_client): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['8bitadventures']) + return_value=tracks['get']['8bitadventures']) mocker.patch( 'funkwhale_api.music.models.TrackFile.download_file', return_value=None) @@ -58,17 +58,18 @@ def test_import_creates_an_import_with_correct_data(mocker, superuser_client): assert job.source == 'https://www.youtube.com/watch?v={0}'.format(video_id) -def test_can_import_whole_album(mocker, superuser_client): +def test_can_import_whole_album( + artists, albums, mocker, superuser_client): mocker.patch('funkwhale_api.music.tasks.import_job_run') mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['soad']) + return_value=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']) + return_value=albums['get_with_includes']['hypnotize']) payload = { 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', 'tracks': [ @@ -97,7 +98,7 @@ def test_can_import_whole_album(mocker, superuser_client): 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] + medium_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']: @@ -113,17 +114,18 @@ def test_can_import_whole_album(mocker, superuser_client): assert job.source == row['source'] -def test_can_import_whole_artist(mocker, superuser_client): +def test_can_import_whole_artist( + artists, albums, mocker, superuser_client): mocker.patch('funkwhale_api.music.tasks.import_job_run') mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['soad']) + return_value=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']) + return_value=albums['get_with_includes']['hypnotize']) payload = { 'artistId': 'mbid', 'albums': [ @@ -157,7 +159,7 @@ def test_can_import_whole_artist(mocker, superuser_client): 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] + medium_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']: @@ -173,55 +175,57 @@ def test_can_import_whole_artist(mocker, superuser_client): 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) +def test_user_can_query_api_for_his_own_batches( + superuser_api_client, factories): + factories['music.ImportJob']() + job = factories['music.ImportJob']( + batch__submitted_by=superuser_api_client.user) 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')) + response = superuser_api_client.get(url) + results = response.data assert results['count'] == 1 assert results['results'][0]['jobs'][0]['mbid'] == job.mbid -def test_user_can_create_an_empty_batch(client, factories): - user = factories['users.SuperUser']() +def test_user_cannnot_access_other_batches( + superuser_api_client, factories): + factories['music.ImportJob']() + job = factories['music.ImportJob']() url = reverse('api:v1:import-batches-list') - client.login(username=user.username, password='test') - response = client.post(url) + + response = superuser_api_client.get(url) + results = response.data + assert results['count'] == 0 + + +def test_user_can_create_an_empty_batch(superuser_api_client, factories): + url = reverse('api:v1:import-batches-list') + response = superuser_api_client.post(url) assert response.status_code == 201 - batch = user.imports.latest('id') + batch = superuser_api_client.user.imports.latest('id') - assert batch.submitted_by == user + assert batch.submitted_by == superuser_api_client.user assert batch.source == 'api' -def test_user_can_create_import_job_with_file(client, factories, mocker): +def test_user_can_create_import_job_with_file( + superuser_api_client, factories, mocker): path = os.path.join(DATA_DIR, 'test.ogg') m = mocker.patch('funkwhale_api.common.utils.on_commit') - user = factories['users.SuperUser']() - batch = factories['music.ImportBatch'](submitted_by=user) + batch = factories['music.ImportBatch']( + submitted_by=superuser_api_client.user) url = reverse('api:v1:import-jobs-list') - client.login(username=user.username, password='test') with open(path, 'rb') as f: content = f.read() f.seek(0) - response = client.post(url, { + response = superuser_api_client.post(url, { 'batch': batch.pk, 'audio_file': f, 'source': 'file://' - }, format='multipart') + }) assert response.status_code == 201 @@ -237,16 +241,16 @@ def test_user_can_create_import_job_with_file(client, factories, mocker): import_job_id=job.pk) -def test_can_search_artist(factories, client): +def test_can_search_artist(factories, logged_in_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 + response = logged_in_client.get(url, {'query': artist1.name}) + assert response.data == expected -def test_can_search_artist_by_name_start(factories, client): +def test_can_search_artist_by_name_start(factories, logged_in_client): artist1 = factories['music.Artist'](name='alpha') artist2 = factories['music.Artist'](name='beta') expected = { @@ -256,20 +260,20 @@ def test_can_search_artist_by_name_start(factories, client): 'results': [serializers.ArtistSerializerNested(artist1).data] } url = reverse('api:v1:artists-list') - response = client.get(url, {'name__startswith': 'a'}) + response = logged_in_client.get(url, {'name__startswith': 'a'}) - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_search_tracks(factories, client): +def test_can_search_tracks(factories, logged_in_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}) + response = logged_in_client.get(url, {'query': query}) - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data @pytest.mark.parametrize('route,method', [ @@ -278,24 +282,31 @@ def test_can_search_tracks(factories, client): ('api:v1:artists-list', 'get'), ('api:v1:albums-list', 'get'), ]) -def test_can_restrict_api_views_to_authenticated_users(db, route, method, settings, client): +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): +def test_track_file_url_is_restricted_to_authenticated_users( + api_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) + response = api_client.get(url) assert response.status_code == 401 - user = factories['users.SuperUser']() - client.login(username=user.username, password='test') - response = client.get(url) + +def test_track_file_url_is_accessible_to_authenticated_users( + logged_in_api_client, factories, settings): + settings.API_AUTHENTICATION_REQUIRED = True + f = factories['music.TrackFile']() + assert f.audio_file is not None + url = f.path + response = logged_in_api_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_import.py b/api/tests/music/test_import.py index f2ca1abbd..0f709e81f 100644 --- a/api/tests/music/test_import.py +++ b/api/tests/music/test_import.py @@ -2,23 +2,21 @@ import json from django.urls import reverse -from . import data as api_data - def test_create_import_can_bind_to_request( - mocker, factories, superuser_api_client): + artists, albums, mocker, factories, superuser_api_client): request = factories['requests.ImportRequest']() mocker.patch('funkwhale_api.music.tasks.import_job_run') mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['soad']) + return_value=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']) + return_value=albums['get_with_includes']['hypnotize']) payload = { 'releaseId': '47ae093f-1607-49a3-be11-a15d335ccc94', 'importRequest': request.pk, diff --git a/api/tests/music/test_lyrics.py b/api/tests/music/test_lyrics.py index d10d113d7..3aee368c0 100644 --- a/api/tests/music/test_lyrics.py +++ b/api/tests/music/test_lyrics.py @@ -7,15 +7,12 @@ from funkwhale_api.music import serializers from funkwhale_api.music import tasks 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): +def test_works_import_lyrics_if_any( + lyricswiki_content, mocker, factories): mocker.patch( 'funkwhale_api.music.lyrics._get_html', - return_value=lyricswiki.content) + return_value=lyricswiki_content) lyrics = factories['music.Lyrics']( url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') @@ -48,16 +45,22 @@ Is it me you're looking for?""" assert expected == l.content_rendered -def test_works_import_lyrics_if_any(mocker, factories, logged_in_client): +def test_works_import_lyrics_if_any( + lyricswiki_content, + works, + tracks, + mocker, + factories, + logged_in_client): mocker.patch( 'funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) + return_value=works['get']['chop_suey']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['chop_suey']) + return_value=tracks['get']['chop_suey']) mocker.patch( 'funkwhale_api.music.lyrics._get_html', - return_value=lyricswiki.content) + return_value=lyricswiki_content) track = factories['music.Track']( work=None, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') diff --git a/api/tests/music/test_music.py b/api/tests/music/test_music.py index 076ad2bd0..4162912e4 100644 --- a/api/tests/music/test_music.py +++ b/api/tests/music/test_music.py @@ -2,14 +2,11 @@ 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): +def test_can_create_artist_from_api(artists, mocker, db): mocker.patch( 'musicbrainzngs.search_artists', - return_value=api_data.artists['search']['adhesive_wombat']) + return_value=artists['search']['adhesive_wombat']) artist = models.Artist.create_from_api(query="Adhesive wombat") data = models.Artist.api.search(query='Adhesive wombat')['artist-list'][0] @@ -19,13 +16,13 @@ def test_can_create_artist_from_api(mocker, db): assert artist.name, 'Adhesive Wombat' -def test_can_create_album_from_api(mocker, db): +def test_can_create_album_from_api(artists, albums, mocker, db): mocker.patch( 'funkwhale_api.musicbrainz.api.releases.search', - return_value=api_data.albums['search']['hypnotize']) + return_value=albums['search']['hypnotize']) mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['soad']) + return_value=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] @@ -38,16 +35,16 @@ def test_can_create_album_from_api(mocker, db): assert album.artist.mbid, data['artist-credit'][0]['artist']['id'] -def test_can_create_track_from_api(mocker, db): +def test_can_create_track_from_api(artists, albums, tracks, mocker, db): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.tracks['search']['8bitadventures']) + return_value=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 @@ -60,16 +57,17 @@ def test_can_create_track_from_api(mocker, db): assert track.album.title == 'Marsupial Madness' -def test_can_create_track_from_api_with_corresponding_tags(mocker, db): +def test_can_create_track_from_api_with_corresponding_tags( + artists, albums, tracks, mocker, db): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['8bitadventures']) + return_value=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()] @@ -77,16 +75,17 @@ def test_can_create_track_from_api_with_corresponding_tags(mocker, db): assert tag in track_tags -def test_can_get_or_create_track_from_api(mocker, db): +def test_can_get_or_create_track_from_api( + artists, albums, tracks, mocker, db): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.tracks['search']['8bitadventures']) + return_value=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 @@ -126,13 +125,13 @@ def test_artist_tags_deduced_from_album_tags(factories, django_assert_num_querie assert tag in artist.tags -def test_can_download_image_file_for_album(mocker, factories): +def test_can_download_image_file_for_album(binary_cover, mocker, factories): mocker.patch( 'funkwhale_api.musicbrainz.api.images.get_front', - return_value=binary_data) + return_value=binary_cover) # 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 + assert album.cover.file.read() == binary_cover diff --git a/api/tests/music/test_tasks.py b/api/tests/music/test_tasks.py index 5ecf9b9e4..ddbc4ba9a 100644 --- a/api/tests/music/test_tasks.py +++ b/api/tests/music/test_tasks.py @@ -4,8 +4,6 @@ import pytest from funkwhale_api.providers.acoustid import get_acoustid_client from funkwhale_api.music import tasks -from . import data as api_data - DATA_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -50,7 +48,7 @@ def test_set_acoustid_on_track_file_required_high_score(factories, mocker): def test_import_job_can_run_with_file_and_acoustid( - preferences, factories, mocker): + artists, albums, tracks, preferences, factories, mocker): preferences['providers_acoustid__api_key'] = 'test' path = os.path.join(DATA_DIR, 'test.ogg') mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' @@ -66,13 +64,13 @@ def test_import_job_can_run_with_file_and_acoustid( } mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.tracks['search']['8bitadventures']) + return_value=tracks['search']['8bitadventures']) mocker.patch('acoustid.match', return_value=acoustid_payload) job = factories['music.FileImportJob'](audio_file__path=path) @@ -129,7 +127,8 @@ def test__do_import_skipping_accoustid_if_no_key( m.assert_called_once_with(p) -def test_import_job_can_be_skipped(factories, mocker, preferences): +def test_import_job_can_be_skipped( + artists, albums, tracks, factories, mocker, preferences): preferences['providers_acoustid__api_key'] = 'test' path = os.path.join(DATA_DIR, 'test.ogg') mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' @@ -146,13 +145,13 @@ def test_import_job_can_be_skipped(factories, mocker, preferences): } mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['adhesive_wombat']) + return_value=artists['get']['adhesive_wombat']) mocker.patch( 'funkwhale_api.musicbrainz.api.releases.get', - return_value=api_data.albums['get']['marsupial']) + return_value=albums['get']['marsupial']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.tracks['search']['8bitadventures']) + return_value=tracks['search']['8bitadventures']) mocker.patch('acoustid.match', return_value=acoustid_payload) job = factories['music.FileImportJob'](audio_file__path=path) diff --git a/api/tests/music/test_works.py b/api/tests/music/test_works.py index 9b72768ad..13f6447be 100644 --- a/api/tests/music/test_works.py +++ b/api/tests/music/test_works.py @@ -5,13 +5,11 @@ 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): +def test_can_import_work(factories, mocker, works): mocker.patch( 'funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) + return_value=works['get']['chop_suey']) recording = factories['music.Track']( mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' @@ -28,13 +26,13 @@ def test_can_import_work(factories, mocker): assert recording.work == work -def test_can_get_work_from_recording(factories, mocker): +def test_can_get_work_from_recording(factories, mocker, works, tracks): mocker.patch( 'funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) + return_value=works['get']['chop_suey']) mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.get', - return_value=api_data.tracks['get']['chop_suey']) + return_value=tracks['get']['chop_suey']) recording = factories['music.Track']( work=None, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') @@ -53,10 +51,10 @@ def test_can_get_work_from_recording(factories, mocker): assert recording.work == work -def test_works_import_lyrics_if_any(db, mocker): +def test_works_import_lyrics_if_any(db, mocker, works): mocker.patch( 'funkwhale_api.musicbrainz.api.works.get', - return_value=api_data.works['get']['chop_suey']) + return_value=works['get']['chop_suey']) mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' work = models.Work.create_from_api(id=mbid) diff --git a/api/tests/musicbrainz/data.py b/api/tests/musicbrainz/conftest.py similarity index 96% rename from api/tests/musicbrainz/data.py rename to api/tests/musicbrainz/conftest.py index 1d7b9a3de..505d6e553 100644 --- a/api/tests/musicbrainz/data.py +++ b/api/tests/musicbrainz/conftest.py @@ -1,5 +1,7 @@ -artists = {'search': {}, 'get': {}} -artists['search']['lost fingers'] = { +import pytest + +_artists = {'search': {}, 'get': {}} +_artists['search']['lost fingers'] = { 'artist-count': 696, 'artist-list': [ { @@ -21,7 +23,7 @@ artists['search']['lost fingers'] = { }, ] } -artists['get']['lost fingers'] = { +_artists['get']['lost fingers'] = { "artist": { "life-span": { "begin": "2008" @@ -102,8 +104,8 @@ artists['get']['lost fingers'] = { } -release_groups = {'browse': {}} -release_groups['browse']["lost fingers"] = { +_release_groups = {'browse': {}} +_release_groups['browse']["lost fingers"] = { "release-group-list": [ { "first-release-date": "2010", @@ -165,8 +167,8 @@ release_groups['browse']["lost fingers"] = { "release-group-count": 8 } -recordings = {'search': {}, 'get': {}} -recordings['search']['brontide matador'] = { +_recordings = {'search': {}, 'get': {}} +_recordings['search']['brontide matador'] = { "recording-count": 1044, "recording-list": [ { @@ -217,8 +219,8 @@ recordings['search']['brontide matador'] = { ] } -releases = {'search': {}, 'get': {}, 'browse': {}} -releases['search']['brontide matador'] = { +_releases = {'search': {}, 'get': {}, 'browse': {}} +_releases['search']['brontide matador'] = { "release-count": 116, "release-list": [ { "ext:score": "100", @@ -283,7 +285,7 @@ releases['search']['brontide matador'] = { ] } -releases['browse']['Lost in the 80s'] = { +_releases['browse']['Lost in the 80s'] = { "release-count": 3, "release-list": [ { @@ -476,3 +478,23 @@ releases['browse']['Lost in the 80s'] = { }, ] } + + +@pytest.fixture() +def releases(): + return _releases + + +@pytest.fixture() +def release_groups(): + return _release_groups + + +@pytest.fixture() +def artists(): + return _artists + + +@pytest.fixture() +def recordings(): + return _recordings diff --git a/api/tests/musicbrainz/test_api.py b/api/tests/musicbrainz/test_api.py index bbade3400..fdd1dbdb0 100644 --- a/api/tests/musicbrainz/test_api.py +++ b/api/tests/musicbrainz/test_api.py @@ -2,64 +2,65 @@ 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): +def test_can_search_recording_in_musicbrainz_api( + recordings, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.recordings.search', - return_value=api_data.recordings['search']['brontide matador']) + return_value=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}) + expected = recordings['search']['brontide matador'] + response = logged_in_api_client.get(url, data={'query': query}) - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_search_release_in_musicbrainz_api(db, mocker, client): +def test_can_search_release_in_musicbrainz_api(releases, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.releases.search', - return_value=api_data.releases['search']['brontide matador']) + return_value=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}) + expected = releases['search']['brontide matador'] + response = logged_in_api_client.get(url, data={'query': query}) - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_search_artists_in_musicbrainz_api(db, mocker, client): +def test_can_search_artists_in_musicbrainz_api(artists, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.search', - return_value=api_data.artists['search']['lost fingers']) + return_value=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}) + expected = artists['search']['lost fingers'] + response = logged_in_api_client.get(url, data={'query': query}) - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_get_artist_in_musicbrainz_api(db, mocker, client): +def test_can_get_artist_in_musicbrainz_api(artists, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.artists.get', - return_value=api_data.artists['get']['lost fingers']) + return_value=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'] + response = logged_in_api_client.get(url) + expected = artists['get']['lost fingers'] - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_broswe_release_group_using_musicbrainz_api(db, mocker, client): +def test_can_broswe_release_group_using_musicbrainz_api( + release_groups, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.release_groups.browse', - return_value=api_data.release_groups['browse']['lost fingers']) + return_value=release_groups['browse']['lost fingers']) uuid = 'ac16bbc0-aded-4477-a3c3-1d81693d58c9' url = reverse( 'api:v1:providers:musicbrainz:release-group-browse', @@ -67,16 +68,17 @@ def test_can_broswe_release_group_using_musicbrainz_api(db, mocker, client): 'artist_uuid': uuid, } ) - response = client.get(url) - expected = api_data.release_groups['browse']['lost fingers'] + response = logged_in_api_client.get(url) + expected = release_groups['browse']['lost fingers'] - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data -def test_can_broswe_releases_using_musicbrainz_api(db, mocker, client): +def test_can_broswe_releases_using_musicbrainz_api( + releases, db, mocker, logged_in_api_client): mocker.patch( 'funkwhale_api.musicbrainz.api.releases.browse', - return_value=api_data.releases['browse']['Lost in the 80s']) + return_value=releases['browse']['Lost in the 80s']) uuid = 'f04ed607-11b7-3843-957e-503ecdd485d1' url = reverse( 'api:v1:providers:musicbrainz:release-browse', @@ -84,7 +86,7 @@ def test_can_broswe_releases_using_musicbrainz_api(db, mocker, client): 'release_group_uuid': uuid, } ) - response = client.get(url) - expected = api_data.releases['browse']['Lost in the 80s'] + response = logged_in_api_client.get(url) + expected = releases['browse']['Lost in the 80s'] - assert expected == json.loads(response.content.decode('utf-8')) + assert expected == response.data diff --git a/api/tests/playlists/__init__.py b/api/tests/playlists/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/tests/radios/__init__.py b/api/tests/radios/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dev.yml b/dev.yml index 8d2129bef..dd3a55ddc 100644 --- a/dev.yml +++ b/dev.yml @@ -50,6 +50,7 @@ services: - ./api:/app - ./data/music:/music environment: + - "PYTHONDONTWRITEBYTECODE=true" - "DJANGO_ALLOWED_HOSTS=localhost,nginx" - "DJANGO_SETTINGS_MODULE=config.settings.local" - "DJANGO_SECRET_KEY=dev"