Esempio n. 1
0
class AniListTest(TestCase):
    @staticmethod
    def read_fixture(filename):
        with open(os.path.join(settings.TEST_DATA_DIR, filename),
                  'r',
                  encoding='utf-8') as f:
            return f.read()

    def setUp(self):
        self.anilist = AniList()

    def test_fuzzydate_to_python_datetime(self):
        self.assertEqual(
            fuzzydate_to_python_datetime({
                'year': 2017,
                'month': 12,
                'day': 25
            }), datetime(2017, 12, 25, 0, 0))
        self.assertEqual(
            fuzzydate_to_python_datetime({
                'year': 2017,
                'month': 12,
                'day': None
            }), datetime(2017, 12, 1, 0, 0))
        self.assertEqual(
            fuzzydate_to_python_datetime({
                'year': 2017,
                'month': None,
                'day': None
            }), datetime(2017, 1, 1, 0, 0))

        self.assertIsNone(
            fuzzydate_to_python_datetime({
                'year': None,
                'month': None,
                'day': 25
            }))
        self.assertIsNone(
            fuzzydate_to_python_datetime({
                'year': None,
                'month': 12,
                'day': 25
            }))
        self.assertIsNone(
            fuzzydate_to_python_datetime({
                'year': None,
                'month': 12,
                'day': None
            }))
        self.assertIsNone(
            fuzzydate_to_python_datetime({
                'year': None,
                'month': None,
                'day': None
            }))

    def test_to_anime_season(self):
        self.assertEqual(to_anime_season(datetime(2017, 1, 1, 0, 0)),
                         AniListSeason.WINTER)
        self.assertEqual(to_anime_season(datetime(2017, 4, 1, 0, 0)),
                         AniListSeason.SPRING)
        self.assertEqual(to_anime_season(datetime(2017, 7, 1, 0, 0)),
                         AniListSeason.SUMMER)
        self.assertEqual(to_anime_season(datetime(2017, 10, 1, 0, 0)),
                         AniListSeason.FALL)

    @responses.activate
    def test_api_errors(self):
        responses.add(
            responses.POST,
            self.anilist.BASE_URL,
            body=
            '{ "data": { "Media": null }, "errors": [ { "message": "Not Found.", "status": 404, "locations": [{"line": 2, "column": 3}] } ] }',
            status=404,
            content_type='application/json')

        with self.assertRaisesRegexp(AniListException,
                                     'Error 404 : Not Found.'):
            self.anilist._request(query=read_graphql_query('work-info'),
                                  variables={'id': 0})

    @responses.activate
    def test_get_work(self):
        responses.add(responses.POST,
                      self.anilist.BASE_URL,
                      body=self.read_fixture('anilist/hibike_euphonium.json'),
                      status=200,
                      content_type='application/json')

        hibike_by_id = self.anilist.get_work(search_id=20912)
        hibike_by_title = self.anilist.get_work(search_title='Hibike')
        hibike_by_id_and_title = self.anilist.get_work(search_id=20912,
                                                       search_title='Hibike')
        hibike = hibike_by_id_and_title

        self.assertEqual(hibike, hibike_by_id)
        self.assertEqual(hibike, hibike_by_title)

    @responses.activate
    def test_work_properties(self):
        responses.add(responses.POST,
                      self.anilist.BASE_URL,
                      body=self.read_fixture('anilist/hibike_euphonium.json'),
                      status=200,
                      content_type='application/json')

        hibike = self.anilist.get_work(search_id=20912)

        self.assertEqual(hibike.anilist_id, 20912)
        self.assertEqual(hibike.anilist_url, 'https://anilist.co/anime/20912')
        self.assertEqual(hibike.media_format, AniListMediaFormat.TV)

        self.assertEqual(hibike.title, 'Hibike! Euphonium')
        self.assertEqual(hibike.english_title, 'Sound! Euphonium')
        self.assertEqual(hibike.japanese_title, '響け!ユーフォニアム')
        self.assertCountEqual(hibike.synonyms, [])

        self.assertEqual(hibike.start_date, datetime(2015, 4, 8, 0, 0))
        self.assertEqual(hibike.end_date, datetime(2015, 7, 1, 0, 0))
        self.assertEqual(hibike.season, AniListSeason.SPRING)

        self.assertEqual(
            hibike.description,
            'The anime begins when Kumiko Oumae, a girl who was in the brass band club in junior high school, visits her high school\'s brass band club as a first year. Kumiko\'s classmates Hazuki and Sapphire decide to join the club, but Kumiko sees her old classmate Reina there and hesitates. She remembers an incident she had with Reina at a brass band club contest in junior high school...<br>\n<br>\n(Source: ANN)'
        )
        self.assertCountEqual(hibike.genres,
                              ['Music', 'Slice of Life', 'Drama'])
        self.assertFalse(hibike.is_nsfw)
        self.assertEqual(
            hibike.poster_url,
            'https://cdn.anilist.co/img/dir/anime/reg/20912-vpZDPyqs22Rz.jpg')

        self.assertEqual(hibike.nb_episodes, 13)
        self.assertEqual(hibike.episode_length, 24)
        self.assertIsNone(hibike.nb_chapters)

        self.assertEqual(hibike.status, AniListStatus.FINISHED)
        self.assertEqual(hibike.studio, 'Kyoto Animation')

        self.assertCountEqual(
            hibike.external_links, {
                'Official Site': 'http://anime-eupho.com/',
                'Crunchyroll': 'http://www.crunchyroll.com/sound-euphonium',
                'Twitter': 'https://twitter.com/anime_eupho'
            })

        self.assertCountEqual(hibike.tags, [{
            'anilist_tag_id': 110,
            'name': 'Band',
            'spoiler': False,
            'votes': 100
        }, {
            'anilist_tag_id': 46,
            'name': 'School',
            'spoiler': False,
            'votes': 79
        }, {
            'anilist_tag_id': 98,
            'name': 'Female Protagonist',
            'spoiler': False,
            'votes': 79
        }, {
            'anilist_tag_id': 84,
            'name': 'School Club',
            'spoiler': False,
            'votes': 73
        }, {
            'anilist_tag_id': 50,
            'name': 'Seinen',
            'spoiler': False,
            'votes': 33
        }])

        self.assertEqual(len(hibike.staff), 13)

        self.assertCountEqual(hibike.relations, [
            AniListRelation(related_id=86133,
                            relation_type=AniListRelationType.ADAPTATION),
            AniListRelation(related_id=21255,
                            relation_type=AniListRelationType.SIDE_STORY),
            AniListRelation(related_id=21376,
                            relation_type=AniListRelationType.SIDE_STORY),
            AniListRelation(related_id=21460,
                            relation_type=AniListRelationType.SEQUEL),
            AniListRelation(related_id=21638,
                            relation_type=AniListRelationType.SUMMARY),
            AniListRelation(related_id=100178,
                            relation_type=AniListRelationType.SIDE_STORY)
        ])

    @responses.activate
    def test_get_seasonal_anime(self):
        responses.add(responses.POST,
                      self.anilist.BASE_URL,
                      body=self.read_fixture('anilist/airing_fall_2017.json'),
                      status=200,
                      content_type='application/json')

        airing_animes = list(
            self.anilist.list_seasonal_animes(year=2017,
                                              season=AniListSeason.SUMMER))
        self.assertEqual(len(airing_animes), 36)

    @responses.activate
    def test_get_animelist(self):
        responses.add(
            responses.POST,
            self.anilist.BASE_URL,
            body=self.read_fixture('anilist/mrsalixor_anilist_animelist.json'),
            status=200,
            content_type='application/json')

        anime_list = list(
            self.anilist.get_user_list(AniListWorkType.ANIME, 'mrsalixor'))
        self.assertEqual(len(anime_list), 450)

    @responses.activate
    def test_get_mangalist(self):
        responses.add(
            responses.POST,
            self.anilist.BASE_URL,
            body=self.read_fixture('anilist/mrsalixor_anilist_mangalist.json'),
            status=200,
            content_type='application/json')

        anime_list = list(
            self.anilist.get_user_list(AniListWorkType.MANGA, 'mrsalixor'))
        self.assertEqual(len(anime_list), 100)

    @responses.activate
    def test_insert_into_database(self):
        artist = Artist(name='Ishihara Tatsuya').save()

        # Test insert AniListEntry into database
        responses.add(responses.POST,
                      self.anilist.BASE_URL,
                      body=self.read_fixture('anilist/hibike_euphonium.json'),
                      status=200,
                      content_type='application/json')

        hibike_entry = self.anilist.get_work(search_id=20912)
        hibike = insert_work_into_database_from_anilist(hibike_entry,
                                                        build_related=False)

        titles_hibike = WorkTitle.objects.filter(work=hibike).values_list(
            'title', flat=True)
        genres_hibike = hibike.genre.values_list('title', flat=True)
        related_hibike = RelatedWork.objects.filter(parent_work=hibike)
        staff_hibike = Work.objects.get(
            pk=hibike.pk).staff_set.all().values_list('artist__name',
                                                      flat=True)

        self.assertEqual(hibike.studio.title, 'Kyoto Animation')
        self.assertCountEqual(
            titles_hibike,
            ['Hibike! Euphonium', 'Sound! Euphonium', '響け!ユーフォニアム'])
        self.assertCountEqual(genres_hibike,
                              ['Slice of Life', 'Music', 'Drama'])
        self.assertCountEqual(
            staff_hibike,
            ['Ishihara Tatsuya', 'Matsuda Akito', 'Takeda Ayano'])

        # Check for no artist duplication
        artist = Artist.objects.filter(name='Ishihara Tatsuya')
        self.assertEqual(artist.count(), 1)
        self.assertEqual(artist.first().anilist_creator_id, 100055)

        # Try adding this work to the DB again
        hibike_again = insert_work_into_database_from_anilist(
            hibike_entry, build_related=False)
        self.assertEqual(hibike, hibike_again)

    @responses.activate
    def test_update_work(self):
        fake_studio = Studio.objects.create(title='Fake Studio')
        hibike_outdated = Work.objects.create(
            category=Category.objects.get(slug='anime'),
            title='Sound! Euphonium',
            studio=fake_studio)
        hibike_outdated.genre.add(Genre.objects.create(title='Fake genre'))

        responses.add(responses.POST,
                      self.anilist.BASE_URL,
                      body=self.read_fixture('anilist/hibike_euphonium.json'),
                      status=200,
                      content_type='application/json')

        hibike_entry = self.anilist.get_work(search_id=20912)
        # FIXME: properly mock the insertion of related works
        insert_work_into_database_from_anilist(hibike_entry,
                                               build_related=False)

        hibike_updated = Work.objects.get(title='Hibike! Euphonium')

        titles_hibike = WorkTitle.objects.filter(
            work=hibike_updated).values_list('title', flat=True)
        genres_hibike = hibike_updated.genre.values_list('title', flat=True)
        related_hibike = RelatedWork.objects.filter(parent_work=hibike_updated)
        staff_hibike = Work.objects.get(
            pk=hibike_updated.pk).staff_set.all().values_list('artist__name',
                                                              flat=True)

        self.assertEqual(hibike_updated.studio.title, 'Kyoto Animation')
        self.assertCountEqual(
            titles_hibike,
            ['Hibike! Euphonium', 'Sound! Euphonium', '響け!ユーフォニアム'])
        self.assertCountEqual(genres_hibike,
                              ['Slice of Life', 'Music', 'Drama'])
        self.assertCountEqual(
            staff_hibike,
            ['Ishihara Tatsuya', 'Matsuda Akito', 'Takeda Ayano'])
Esempio n. 2
0
class AniListTest(TestCase):
    @staticmethod
    def read_fixture(filename):
        with open(os.path.join(settings.TEST_DATA_DIR, filename),
                  'r',
                  encoding='utf-8') as f:
            return f.read()

    @staticmethod
    def add_fake_auth():
        responses.add(
            responses.POST,
            urljoin(AniList.BASE_URL, AniList.AUTH_PATH),
            body=
            '{"access_token":"fake_token","token_type":"Bearer","expires_in":3600,"expires":946684800}',
            status=200,
            content_type='application/json')

    def setUp(self):
        self.anilist = AniList('test_client', 'client_secret')
        self.no_anilist = AniList()

    def test_to_python_datetime(self):
        self.assertEqual(to_python_datetime('20171225'),
                         datetime(2017, 12, 25, 0, 0))
        self.assertEqual(to_python_datetime('20171200'),
                         datetime(2017, 12, 1, 0, 0))
        self.assertEqual(to_python_datetime('20170000'),
                         datetime(2017, 1, 1, 0, 0))
        self.assertRaises(ValueError, to_python_datetime, '2017')

    def test_to_anime_season(self):
        self.assertEqual(to_anime_season(datetime(2017, 1, 1, 0, 0)), 'winter')
        self.assertEqual(to_anime_season(datetime(2017, 4, 1, 0, 0)), 'spring')
        self.assertEqual(to_anime_season(datetime(2017, 7, 1, 0, 0)), 'summer')
        self.assertEqual(to_anime_season(datetime(2017, 10, 1, 0, 0)), 'fall')

    def test_missing_client(self):
        self.assertRaises(RuntimeError, self.no_anilist._authenticate)
        self.assertFalse(self.no_anilist._is_authenticated())

    @responses.activate
    def test_authentication(self):
        self.add_fake_auth()

        self.assertFalse(self.anilist._is_authenticated())

        auth = self.anilist._authenticate()
        self.assertEqual(auth["access_token"], "fake_token")
        self.assertEqual(auth["token_type"], "Bearer")
        self.assertEqual(auth["expires_in"], 3600)
        self.assertEqual(auth["expires"], 946684800)

    @responses.activate
    def test_get_seasonal_anime(self):
        self.add_fake_auth()

        responses.add(
            responses.GET,
            urljoin(AniList.BASE_URL, 'browse/anime'),
            body=self.read_fixture('anilist/airing_summer_2017_trimmed.json'),
            status=200,
            content_type='application/json')

        for anime in self.anilist.list_seasonal_animes(year=2017,
                                                       season='summer'):
            if anime.title == 'Made in Abyss':
                self.assertEqual(anime.anilist_id, 97986)
                self.assertEqual(anime.english_title, 'Made in Abyss')
                self.assertEqual(anime.japanese_title, 'メイドインアビス')
                self.assertEqual(anime.media_type, 'TV')
                self.assertEqual(anime.start_date, datetime(2017, 7, 7))
                self.assertIsNone(anime.end_date)
                self.assertEqual(anime.synonyms, [])
                self.assertEqual(anime.genres,
                                 ['Adventure', 'Fantasy', 'Sci-Fi'])
                self.assertFalse(anime.is_nsfw)
                self.assertEqual(
                    anime.poster_url,
                    'https://cdn.anilist.co/img/dir/anime/reg/97986-ZL0DkAyNWyxG.jpg'
                )
                self.assertEqual(anime.nb_episodes, 13)
                self.assertEqual(anime.status, AniListStatus.airing)
                self.assertEqual(anime.tags[1], {
                    'anilist_tag_id': 175,
                    'name': 'Robots',
                    'spoiler': False
                })
                break

    @responses.activate
    def test_get_userlist(self):
        self.add_fake_auth()

        for work_type in AniListWorks:
            responses.add(responses.GET,
                          urljoin(
                              AniList.BASE_URL,
                              'user/mrsalixor/{}list'.format(work_type.value)),
                          body=self.read_fixture(
                              'anilist/mrsalixor_anilist_{}list.json'.format(
                                  work_type.value)),
                          status=200,
                          content_type='application/json')

        anime_list = self.anilist.get_user_list(AniListWorks.animes,
                                                'mrsalixor')
        animes = set(anime_list)
        self.assertEqual(len(animes), 52)

        manga_list = self.anilist.get_user_list(AniListWorks.mangas,
                                                'mrsalixor')
        mangas = set(manga_list)
        self.assertEqual(len(mangas), 57)
Esempio n. 3
0
class AniListTest(TestCase):
    @staticmethod
    def read_fixture(filename):
        with open(os.path.join(settings.TEST_DATA_DIR, filename),
                  'r',
                  encoding='utf-8') as f:
            return f.read()

    @staticmethod
    def add_fake_auth():
        responses.add(
            responses.POST,
            urljoin(AniList.BASE_URL, AniList.AUTH_PATH),
            body=
            '{"access_token":"fake_token","token_type":"Bearer","expires_in":3600,"expires":946684800}',
            status=200,
            content_type='application/json')

    def setUp(self):
        self.anilist = AniList('test_client', 'client_secret')
        self.no_anilist = AniList()

    def test_to_python_datetime(self):
        self.assertEqual(to_python_datetime('20171225'),
                         datetime(2017, 12, 25, 0, 0))
        self.assertEqual(to_python_datetime('20171200'),
                         datetime(2017, 12, 1, 0, 0))
        self.assertEqual(to_python_datetime('20170000'),
                         datetime(2017, 1, 1, 0, 0))
        self.assertRaises(ValueError, to_python_datetime, '2017')

    def test_to_anime_season(self):
        self.assertEqual(to_anime_season(datetime(2017, 1, 1, 0, 0)), 'winter')
        self.assertEqual(to_anime_season(datetime(2017, 4, 1, 0, 0)), 'spring')
        self.assertEqual(to_anime_season(datetime(2017, 7, 1, 0, 0)), 'summer')
        self.assertEqual(to_anime_season(datetime(2017, 10, 1, 0, 0)), 'fall')

    def test_missing_client(self):
        self.assertRaises(RuntimeError, self.no_anilist._authenticate)
        self.assertFalse(self.no_anilist._is_authenticated())

    @responses.activate
    def test_authentication(self):
        self.add_fake_auth()

        self.assertFalse(self.anilist._is_authenticated())

        auth = self.anilist._authenticate()
        self.assertEqual(auth["access_token"], "fake_token")
        self.assertEqual(auth["token_type"], "Bearer")
        self.assertEqual(auth["expires_in"], 3600)
        self.assertEqual(auth["expires"], 946684800)

    @responses.activate
    def test_api_errors(self):
        self.add_fake_auth()

        error_tests = [{
            'route':
            'unknown_route',
            'status':
            404,
            'exception':
            '"unknown_route" API route does not exist',
            'body':
            '{"error":{"status":404,"messages":["API route not found."]}}'
        }, {
            'route':
            'token_expired',
            'status':
            200,
            'exception':
            'token no longer valid or not found',
            'body':
            '{"error":"access_denied","error_description":"The resource owner or authorization server denied the request."}'
        }, {
            'route':
            'token_missing',
            'status':
            401,
            'exception':
            'token no longer valid or not found',
            'body':
            '{"status":401,"error":"unauthorized","error_message":"Access token is missing"}'
        }, {
            'route':
            'other_error',
            'status':
            404,
            'exception':
            'unknown_error - handle too',
            'body':
            '{"status":404,"error":{"unknown_error":"handle too"}}'
        }]

        for error_test in error_tests:
            with self.subTest(error_test['route'],
                              exception=error_test['exception']):
                responses.add(responses.GET,
                              urljoin(AniList.BASE_URL, error_test['route']),
                              body=error_test['body'],
                              status=error_test['status'],
                              content_type='application/json')

                with self.assertRaisesRegexp(AniListException,
                                             error_test['exception']):
                    self.anilist._request(error_test['route'])

    @responses.activate
    def test_get_seasonal_anime(self):
        self.add_fake_auth()

        responses.add(
            responses.GET,
            urljoin(AniList.BASE_URL, 'browse/anime'),
            body=self.read_fixture('anilist/airing_summer_2017_trimmed.json'),
            status=200,
            content_type='application/json')

        for anime in self.anilist.list_seasonal_animes(year=2017,
                                                       season='summer'):
            if anime.title == 'Made in Abyss':
                self.assertEqual(anime.anilist_id, 97986)
                self.assertEqual(anime.english_title, 'Made in Abyss')
                self.assertEqual(anime.japanese_title, 'メイドインアビス')
                self.assertEqual(anime.media_type, 'TV')
                self.assertEqual(anime.start_date, datetime(2017, 7, 7))
                self.assertIsNone(anime.end_date)
                self.assertIsNone(anime.description)
                self.assertEqual(anime.synonyms, [])
                self.assertEqual(anime.genres,
                                 ['Adventure', 'Fantasy', 'Sci-Fi'])
                self.assertFalse(anime.is_nsfw)
                self.assertEqual(
                    anime.poster_url,
                    'https://cdn.anilist.co/img/dir/anime/reg/97986-ZL0DkAyNWyxG.jpg'
                )
                self.assertEqual(anime.nb_episodes, 13)
                self.assertEqual(anime.status, AniListStatus.airing)
                self.assertEqual(
                    anime.tags[1], {
                        'anilist_tag_id': 175,
                        'name': 'Robots',
                        'spoiler': False,
                        'votes': 53
                    })
                break

    @responses.activate
    def test_get_work_by_id(self):
        self.add_fake_auth()

        responses.add(responses.GET,
                      urljoin(AniList.BASE_URL, 'anime/20912/page'),
                      body=self.read_fixture('anilist/hibike_euphonium.json'),
                      status=200,
                      content_type='application/json')

        hibike = self.anilist.get_work_by_id(AniListWorks.animes, 20912)

        self.assertEqual(hibike.english_title, 'Sound! Euphonium')
        self.assertEqual(hibike.japanese_title, '響け!ユーフォニアム')
        self.assertEqual(hibike.studio, 'Kyoto Animation')
        self.assertEqual(hibike.episode_length, 24)

        self.assertEqual(hibike.youtube_url,
                         'https://www.youtube.com/watch?v=r_Kk9xhVkB8')
        self.assertEqual(hibike.crunchyroll_url,
                         'http://www.crunchyroll.com/sound-euphonium')
        self.assertEqual(hibike.twitter_url, 'https://twitter.com/anime_eupho')
        self.assertEqual(hibike.official_url, 'http://anime-eupho.com/')

        responses.add(
            responses.GET,
            urljoin(AniList.BASE_URL, 'anime/99999999999/page'),
            body=
            '{"error":{"status":404,"messages":["No query results for model [App/AniList/v1/Series/Series] 99999999999"]}}',
            status=404,
            content_type='application/json')

        inexistant_work = self.anilist.get_work_by_id(AniListWorks.animes,
                                                      99999999999)
        self.assertIsNone(inexistant_work)

    @responses.activate
    def test_get_work_by_title(self):
        self.add_fake_auth()

        responses.add(
            responses.GET,
            urljoin(AniList.BASE_URL, 'anime/search/Hibike!'),
            body=self.read_fixture('anilist/hibike_euphonium_search.json'),
            status=200,
            content_type='application/json')

        hibike = self.anilist.get_work_by_title(AniListWorks.animes, 'Hibike!')

        self.assertEqual(hibike.english_title, 'Sound! Euphonium')
        self.assertEqual(hibike.japanese_title, '響け!ユーフォニアム')

        responses.add(
            responses.GET,
            urljoin(AniList.BASE_URL, 'anime/search/no%20such%20anime'),
            body='{"error":{"status":200,"messages":["No Results."]}}',
            status=200,
            content_type='application/json')

        inexistant_work = self.anilist.get_work_by_title(
            AniListWorks.animes, 'no such anime')
        self.assertIsNone(inexistant_work)

    @responses.activate
    def test_get_userlist(self):
        self.add_fake_auth()

        for work_type in AniListWorks:
            responses.add(responses.GET,
                          urljoin(
                              AniList.BASE_URL,
                              'user/mrsalixor/{}list'.format(work_type.value)),
                          body=self.read_fixture(
                              'anilist/mrsalixor_anilist_{}list.json'.format(
                                  work_type.value)),
                          status=200,
                          content_type='application/json')

        anime_list = self.anilist.get_user_list(AniListWorks.animes,
                                                'mrsalixor')
        animes = set(anime_list)
        self.assertEqual(len(animes), 52)

        manga_list = self.anilist.get_user_list(AniListWorks.mangas,
                                                'mrsalixor')
        mangas = set(manga_list)
        self.assertEqual(len(mangas), 57)

        for work_type in AniListWorks:
            responses.add(
                responses.GET,
                urljoin(AniList.BASE_URL,
                        'user/aaaaaaaaaaaaa/{}list'.format(work_type.value)),
                body=
                '{"error":{"status":404,"messages":["No query results for model [App/AniList/v1/User/User] aaaaaaaaaaaaa"]}}',
                status=404,
                content_type='application/json')

        inexistant_user_animelist = list(
            self.anilist.get_user_list(AniListWorks.animes, 'aaaaaaaaaaaaa'))
        inexistant_user_mangalist = list(
            self.anilist.get_user_list(AniListWorks.mangas, 'aaaaaaaaaaaaa'))
        self.assertCountEqual(inexistant_user_animelist, [])
        self.assertCountEqual(inexistant_user_mangalist, [])