def test_invalid_constructor_parameters(self): """ Tests using invalid parameter types with the constructor :return: None """ source = Id({IdType.MYANIMELIST: 1}) dest = Id({IdType.MYANIMELIST: 2}) for parameters in [ (None, MediaType.ANIME, dest, MediaType.ANIME, RelationType.SEQUEL), (source, MediaType.ANIME, None, MediaType.ANIME, RelationType.SEQUEL), (source, MediaType.ANIME, dest, MediaType.ANIME, None), (1, MediaType.ANIME, 2, MediaType.ANIME, RelationType.SEQUEL), (True, MediaType.ANIME, False, MediaType.ANIME, RelationType.SEQUEL), (source, MediaType.ANIME, dest, MediaType.ANIME, "SEQUEL"), (source, "ANIME", dest, MediaType.ANIME, RelationType.SEQUEL), (source, MediaType.ANIME, dest, "MANGA", RelationType.SEQUEL) ]: try: Relation(*parameters) self.fail() except TypeError: pass
def test_equality(self): """ Tests that the equality of the objects is handled correctly :return: None """ id_one = Id({IdType.MYANIMELIST: 1}) id_two = Id({IdType.MYANIMELIST: 2}) id_three = Id({IdType.MYANIMELIST: 3}) one = Relation(id_one, MediaType.ANIME, id_two, MediaType.ANIME, RelationType.SEQUEL) two = Relation(id_one, MediaType.ANIME, id_two, MediaType.ANIME, RelationType.SEQUEL) three = Relation(id_one, MediaType.ANIME, id_three, MediaType.ANIME, RelationType.SEQUEL) four = Relation(id_one, MediaType.ANIME, id_two, MediaType.ANIME, RelationType.ADAPTATION) five = Relation(id_one, MediaType.ANIME, id_two, MediaType.MANGA, RelationType.SEQUEL) self.assertNotEqual(one, "Test") self.assertEqual(one, two) self.assertNotEqual(two, three) self.assertNotEqual(two, four) self.assertNotEqual(three, four) self.assertNotEqual(one, five)
def __resolve_query_id(self, media_type: MediaType, _id: Id, allow_mal: bool) -> Optional[Tuple[int, IdType]]: """ Calculates the ID value to use in a query :param media_type: The media type of the ID :param _id: The ID :param allow_mal: If True, may return a Myanimelist ID. This will be signified by the second return value being IdType.MYANIMELIST :return: A tuple consisting of the ID and the IDs type """ mal_id = _id.get(IdType.MYANIMELIST) anilist_id = _id.get(IdType.ANILIST) id_type = IdType.ANILIST if anilist_id is None: query_id = mal_id if allow_mal: id_type = IdType.MYANIMELIST else: query_id = self.get_anilist_id_from_mal_id( media_type, query_id) else: query_id = anilist_id if query_id is None: return None else: return query_id, id_type
def test_retrieving_non_existant_list_entry(self): """ Tests retrieving entries that are not in a user's list Uses very badly rated entries to make sure that they never get added to the list, breaking the test :return: None """ for media_type, _id in { MediaType.ANIME: Id({ IdType.KITSU: 378, # Mars of Destruction IdType.MYANIMELIST: 413, IdType.ANILIST: 413 }), MediaType.MANGA: Id({ IdType.KITSU: 45257, # Treasure Hunter Kukai IdType.MYANIMELIST: 539, IdType.ANILIST: 75257 }), }.items(): self.assertIsNone(self.api.get_list_entry( media_type, _id, self.username )) self.assertIsNone(self.api.get_user_data( media_type, _id, self.username ))
def test_generating_anime_media_list_entry(self): """ Tests generating an anime media list entry object :return: None """ data = self.generate_sample_anime_entry() self.assertEqual(data.id, Id({IdType.MYANIMELIST: 1})) self.assertEqual(data.title, Title({TitleType.ROMAJI: "Test"})) self.assertEqual(data.relations, [ Relation(Id({IdType.MYANIMELIST: 1}), MediaType.ANIME, Id({IdType.MYANIMELIST: 2}), MediaType.MANGA, RelationType.SEQUEL) ]) self.assertEqual(data.releasing_status, ReleasingStatus.FINISHED) self.assertEqual(data.releasing_start, Date(2018, 1, 1)) self.assertEqual(data.releasing_end, Date(2018, 4, 4)) self.assertEqual(data.episode_count, 12) self.assertEqual(data.episode_duration, 25) self.assertEqual(data.cover_url, "https://example.com/image.png") self.assertEqual(data.username, "namboy94") self.assertEqual(data.score, Score(55, ScoreType.PERCENTAGE)) self.assertEqual(data.consuming_status, ConsumingStatus.COMPLETED) self.assertEqual(data.episode_progress, 12) self.assertEqual(data.consuming_start, Date(2018, 1, 1)) self.assertEqual(data.consuming_end, Date(2018, 4, 4)) self.assertEqual(data.media_type, MediaType.ANIME)
def test_serialization(self): """ Tests serializing an ID object :return: None """ ob = Id({IdType.MYANIMELIST: 1, IdType.ANILIST: 2}) data = ob.serialize() self.assertEqual(data, {"MYANIMELIST": 1, "ANILIST": 2, "KITSU": None})
def test_string_representation(self): """ Tests that the string representation is correct :return: None """ _id = Id({IdType.MYANIMELIST: 1, IdType.ANILIST: 2}) representation = str(_id) serialised = json.loads(representation) self.assertEqual(_id, Id.deserialize(serialised))
def test_unfilled_entries(self): """ Tests that unfilled ID entries lead to Null values for the respective IDs :return: None """ _id = Id({IdType.MYANIMELIST: 1}) self.assertEqual(1, _id.get(IdType.MYANIMELIST)) self.assertEqual(None, _id.get(IdType.ANILIST)) self.assertEqual(None, _id.get(IdType.KITSU))
def test_fetching_different_id_types(self): """ Tests generating an ID using the constructor and fetching the different IDs :return: None """ _id = Id({IdType.MYANIMELIST: 1, IdType.ANILIST: 2, IdType.KITSU: 3}) self.assertEqual(1, _id.get(IdType.MYANIMELIST)) self.assertEqual(2, _id.get(IdType.ANILIST)) self.assertEqual(3, _id.get(IdType.KITSU))
def test_string_representation(self): """ Tests that the string representation is correct :return: None """ source = Id({IdType.MYANIMELIST: 1}) dest = Id({IdType.MYANIMELIST: 2}) relation = Relation(source, MediaType.ANIME, dest, MediaType.ANIME, RelationType.SEQUEL) representation = str(relation) serialised = json.loads(representation) self.assertEqual(relation, Relation.deserialize(serialised))
def generate_sample_manga_data() -> MangaData: """ Generates a generic MangaData object :return: The generated manga data object """ return MangaData(Id( {IdType.MYANIMELIST: 1}), Title({TitleType.ROMAJI: "Test"}), [ Relation(Id({IdType.MYANIMELIST: 1}), MediaType.MANGA, Id({IdType.MYANIMELIST: 2}), MediaType.MANGA, RelationType.SEQUEL) ], ReleasingStatus.FINISHED, Date(2018, 1, 1), Date(2018, 4, 4), "https://example.com/image.png", 100, 10)
def test_deserialization(self): """ Tests deserializing an ID object :return: None """ self.assertEqual(Id.deserialize({ "MYANIMELIST": 1, "ANILIST": 2 }), Id({ IdType.MYANIMELIST: 1, IdType.ANILIST: 2 }))
def test_equality(self): """ Tests that the equality of the objects is handled correctly :return: None """ one = Id({IdType.MYANIMELIST: 1, IdType.ANILIST: 2}) two = Id({IdType.MYANIMELIST: 1, IdType.ANILIST: 2}) three = Id({IdType.MYANIMELIST: 1}) self.assertEqual(one, two) self.assertNotEqual(two, three) self.assertNotEqual(one, "Test")
def test_caching_anime(self): """ Tests that the caching works correctly for media data :return: None """ self.api.cache = Cache(self.cache.cache_location) for media_type, english, _id in [ (MediaType.ANIME, "Steins;Gate", Id({ IdType.KITSU: 5646, IdType.MYANIMELIST: 9253, IdType.ANILIST: 9253 })), (MediaType.MANGA, "Spice & Wolf", Id({ IdType.MYANIMELIST: 9115, IdType.KITSU: 18471, IdType.ANILIST: 39115 })) ]: fetched_entry = self.api.get_list_entry( media_type, _id, self.username ) fetched_data = self.api.get_data(media_type, _id) cached_entry = self.api.cache.get_media_list_entry( self.api.id_type, media_type, _id, self.username ) cached_data = self.api.cache.get_media_data( self.api.id_type, media_type, _id ) self.assertEqual( fetched_data.title.get(TitleType.ENGLISH), english ) self.assertEqual( fetched_entry.title.get(TitleType.ENGLISH), english ) self.assertEqual(fetched_data, cached_data) self.assertEqual(fetched_entry, cached_entry) def raise_value_error(): raise ValueError() # Makes sure that cached value is used from now on with mock.patch("requests.post", new=raise_value_error): with mock.patch("requests.get", new=raise_value_error): new_fetched_data = self.api.get_data(media_type, _id) new_fetched_entry = self.api.get_list_entry( media_type, _id, self.username ) self.assertEqual(new_fetched_data, cached_data) self.assertEqual(new_fetched_entry, cached_entry)
def test_setting_invalid_ids(self): """ Tests setting ids that are invalid types :return: None """ _id = Id({IdType.MYANIMELIST: 1}) for value in [None, "100", 100.0, True]: try: # noinspection PyTypeChecker _id.set(value, IdType.ANILIST) self.fail() except TypeError: pass
def test_equality(self): """ Tests that the equality of the objects is handled correctly :return: None """ one = self.generate_sample_anime_entry() two = self.generate_sample_anime_entry() self.assertEqual(one, two) two.id = Id({IdType.KITSU: 1}) self.assertNotEqual(one, two) two = self.generate_sample_anime_entry() two.releasing_start = None self.assertNotEqual(one, two) two = self.generate_sample_anime_entry() two.username = "******" self.assertNotEqual(one, two) two = self.generate_sample_anime_entry() two.consuming_end = None self.assertNotEqual(one, two) self.assertNotEqual(one, self.generate_sample_manga_entry())
def _get_common_deserialized_components( cls, data: Dict[str, Optional[str or int or float or bool or Dict or List or Tuple or Set]]) \ -> Dict[str, Optional[str or int or float or bool or Dict or List or Tuple or Set]]: """ Deserializes the common child components of the data dictionary :param data: The data to deserialize :return: The deserialized dictionary """ deserialized = { "media_type": MediaType[data["media_type"]], "id": Id.deserialize(data["id"]), "title": Title.deserialize(data["title"]), "relations": list(map(lambda x: Relation.deserialize(x), data["relations"])), "releasing_status": ReleasingStatus[data["releasing_status"]], "cover_url": data["cover_url"] } for date in ["releasing_start", "releasing_end"]: date_data = data[date] if date_data is not None: deserialized[date] = Date.deserialize(date_data) else: deserialized[date] = None return deserialized
def _get_common_deserialized_components( cls, data: Dict[str, Optional[str or int or float or bool or Dict or List or Tuple or Set]]) \ -> Dict[str, Optional[str or int or float or bool or Dict or List or Tuple or Set]]: """ Deserializes the common child components of the data dictionary :param data: The data to deserialize :return: The deserialized dictionary """ deserialized = { "media_id": Id.deserialize(data["media_id"]), "media_type": MediaType[data["media_type"]], "username": data["username"], "score": Score.deserialize(data["score"]), "consuming_status": ConsumingStatus[data["consuming_status"]] } for date in ["consuming_start", "consuming_end"]: if data[date] is not None: deserialized[date] = Date.deserialize(data[date]) else: deserialized[date] = None return deserialized
def test_invalid_deserialization(self): """ Tests that invalid serialized data raises ValueErrors when deserialized :return: None """ def attempt_deserialize(media_cls: type(MediaType), data: dict): try: media_cls.deserialize(data) self.fail() except (ValueError, TypeError): pass for media_class, sample in [ (AnimeData, self.generate_sample_serialized_anime_data()), (MangaData, self.generate_sample_serialized_manga_data()) ]: for key, value in sample.items(): for faux_value in [2000, "Hello", Id({IdType.KITSU: 1})]: if type(faux_value) != type(value): copy = deepcopy(sample) copy[key] = faux_value attempt_deserialize(media_class, copy) copy = deepcopy(sample) copy.pop(key) attempt_deserialize(media_class, copy)
def test_loading_and_retrieving_cache(self): """ Tests writing some data into the cache and then reloading it in a different Cache object :return: None """ entry = TestMediaListEntry.generate_sample_anime_entry() one = entry.id two = Id({IdType.MYANIMELIST: 2}) user = entry.username self.cache.add(IdType.MYANIMELIST, entry.get_media_data()) self.cache.add(IdType.MYANIMELIST, entry.get_user_data()) entry.id = two self.cache.add(IdType.MYANIMELIST, entry) self.cache.write() new_cache = Cache("testdir/.cache") for cache in [self.cache, new_cache]: for _id in [one, two]: entry.id = _id self.assertEqual( cache.get_media_data(IdType.MYANIMELIST, MediaType.ANIME, _id), entry.get_media_data()) self.assertEqual( cache.get_media_user_data(IdType.MYANIMELIST, MediaType.ANIME, _id, user), entry.get_user_data()) self.assertEqual( cache.get_media_list_entry(IdType.MYANIMELIST, MediaType.ANIME, _id, user), entry)
def generate_sample_manga_user_data() -> MangaUserData: """ Generates a sample MangaUserData object :return: The generated MangaUserData object """ return MangaUserData(Id({IdType.MYANIMELIST: 1}), "namboy94", Score(55, ScoreType.PERCENTAGE), ConsumingStatus.COMPLETED, Date(2018, 1, 1), Date(2018, 4, 4), 100, 10)
def __generate_id_obj(self, _id: int or Id) -> Id: """ Generates an Id object if the given ID is an integer :param _id: The ID to make sure is an Id object :return: The generated Id object """ if isinstance(_id, int): _id = Id({self.id_type: _id}) return _id
def test_invalid_deserialization(self): """ Tests that invalid serialized data raises ValueErrors when deserialized :return: None """ for data in [{ "A": 1 }, {}, { "ANILIST": "1" }, { "Anilist": 1 }, { "ANILIST": None }, []]: try: Id.deserialize(data) self.fail() except (TypeError, ValueError): pass
def _deserialize(cls, data: Dict[ str, Optional[str or int or float or bool or Dict or List or Tuple or Set]]): """ Deserializes a dictionary into an object of this type :param data: The data to deserialize :return: The deserialized object :raises TypeError: If a type error occurred :raises ValueError: If the data could not be deserialized """ source = Id.deserialize(data["source"]) source_type = MediaType[data["source_type"]] dest = Id.deserialize(data["dest"]) dest_type = MediaType[data["dest_type"]] relation_type = RelationType[data["type"]] generated = cls(source, source_type, dest, dest_type, relation_type) # type: Relation return generated
def test_retrieving_data(self): """ Tests retrieving a data, user data and list entries for both anime and manga :return: None """ for media_type, english, _id in [ (MediaType.ANIME, "Steins;Gate", Id({ IdType.KITSU: 5646, IdType.MYANIMELIST: 9253, IdType.ANILIST: 9253 })), (MediaType.MANGA, "Spice & Wolf", Id({ IdType.MYANIMELIST: 9115, IdType.KITSU: 18471, IdType.ANILIST: 39115 })) ]: media_data = self.api.get_data(media_type, _id) user_data = self.api.get_user_data(media_type, _id, self.username) entry = self.api.get_list_entry(media_type, _id, self.username) self.assertEqual( media_data.id.get(self.api.id_type), _id.get(self.api.id_type) ) self.assertEqual( user_data.id.get(self.api.id_type), _id.get(self.api.id_type) ) self.assertEqual( entry.id.get(self.api.id_type), _id.get(self.api.id_type) ) self.assertEqual(media_data.title.get(TitleType.ENGLISH), english) self.assertEqual(entry.title.get(TitleType.ENGLISH), english) self.assertEqual(media_data, entry.get_media_data()) self.assertEqual(user_data, entry.get_user_data())
def test_generating_manga_data(self): """ Tests generating a manga data object :return: None """ data = self.generate_sample_manga_data() self.assertEqual(data.id, Id({IdType.MYANIMELIST: 1})) self.assertEqual(data.title, Title({TitleType.ROMAJI: "Test"})) self.assertEqual(data.relations, [ Relation(Id({IdType.MYANIMELIST: 1}), MediaType.MANGA, Id({IdType.MYANIMELIST: 2}), MediaType.MANGA, RelationType.SEQUEL) ]) self.assertEqual(data.releasing_status, ReleasingStatus.FINISHED) self.assertEqual(data.releasing_start, Date(2018, 1, 1)) self.assertEqual(data.releasing_end, Date(2018, 4, 4)) self.assertEqual(data.chapter_count, 100) self.assertEqual(data.volume_count, 10) self.assertEqual(data.cover_url, "https://example.com/image.png") self.assertEqual(data.media_type, MediaType.MANGA)
def test_invalid_constructor_parameters(self): """ Tests using invalid parameter types with the constructor :return: None """ for parameters in [([], ), (100, )]: try: Id(*parameters) self.fail() except TypeError: pass
def test_no_entries(self): """ Tests that the constructor raises a ValueError when no ID at all is provided :return: None """ try: Id({}) self.fail() except ValueError: pass
def test_no_valid_entries(self): """ Tests that providing None values as the only IDs still result in a ValueError :return: None """ try: # noinspection PyTypeChecker Id({IdType.MYANIMELIST: None}) self.fail() except ValueError: pass
def test_getting_fresh_data(self): """ Tests retrieving fresh data :return: None """ self.api.cache.expiration = 6000 _id = Id({ IdType.KITSU: 5646, IdType.MYANIMELIST: 9253, IdType.ANILIST: 9253 }) media_data = self.api.get_anime_data(_id) user_data = self.api.get_anime_user_data(_id, self.username) original_media = deepcopy(media_data) original_user = deepcopy(user_data) self.assertEqual( self.api.cache.get_media_data( self.api.id_type, MediaType.ANIME, _id ), media_data ) self.assertEqual( self.api.cache.get_media_user_data( self.api.id_type, MediaType.ANIME, _id, self.username ), user_data ) media_data.title.set("Test", TitleType.ENGLISH) user_data.score = Score(0, ScoreType.PERCENTAGE) self.api.cache.add(self.api.id_type, media_data) self.api.cache.add(self.api.id_type, user_data) self.assertNotEqual(original_media, media_data) self.assertNotEqual(original_user, user_data) self.assertEqual(media_data, self.api.get_anime_data(_id)) self.assertEqual( user_data, self.api.get_anime_user_data(_id, self.username) ) fresh_media = self.api.get_anime_data(_id, True) fresh_user = self.api.get_anime_user_data(_id, self.username, True) self.assertNotEqual(fresh_media, media_data) self.assertEqual(fresh_media, original_media) self.assertNotEqual(fresh_user, user_data) self.assertEqual(fresh_user, original_user)