From f2cb1f5a68ea76c404eca74175677c1b1c0b90da Mon Sep 17 00:00:00 2001 From: FriendsOfGalaxy Date: Tue, 3 Dec 2019 12:19:15 +0100 Subject: [PATCH] version 0.26 --- .github/ISSUE_TEMPLATE/bug_report.md | 27 ---- requirements/app.txt | 2 +- requirements/dev.txt | 2 +- src/plugin.py | 62 +++----- src/psn_client.py | 55 ++++++- src/version.py | 2 +- tests/integration_test.py | 21 +-- tests/test_achievements.py | 220 +++++---------------------- tests/test_communication_id_cache.py | 20 +-- tests/test_data.py | 36 +++-- tests/test_friends.py | 3 +- 11 files changed, 151 insertions(+), 299 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index cb718f6..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - -**Before start** -- check if your issue is known: https://github.com/FriendsOfGalaxy/galaxy-integration-psn/wiki/Common-problems - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** - -Steps to reproduce the behavior, for example: -1. Log in with account that have 5k games -2. Wait 2 minutes -3. See that plugin has crashed - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Attached logs** -Check how to do it properly: https://github.com/FriendsOfGalaxy/galaxy-integration-psn/wiki/Log-files diff --git a/requirements/app.txt b/requirements/app.txt index 3dce3be..4cff786 100755 --- a/requirements/app.txt +++ b/requirements/app.txt @@ -1,4 +1,4 @@ aiohttp==3.5.4 -galaxy.plugin.api==0.40.1 +galaxy.plugin.api==0.59 urllib3==1.24.1 certifi==2019.3.9 diff --git a/requirements/dev.txt b/requirements/dev.txt index 38eb490..9450670 100755 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,7 +1,7 @@ -r app.txt aioresponses invoke==1.2.0 -pytest==4.2.0 +pytest==5.2.2 pytest-flakes==4.0.0 pytest-pythonpath==0.7.3 pytest-asyncio==0.10.0 diff --git a/src/plugin.py b/src/plugin.py index 4e3713d..4a2b3b7 100755 --- a/src/plugin.py +++ b/src/plugin.py @@ -7,10 +7,10 @@ from collections import defaultdict from galaxy.api.plugin import Plugin, create_and_run_plugin -from galaxy.api.types import Authentication, NextStep, Achievement +from galaxy.api.types import Authentication, NextStep, Achievement, UserPresence, PresenceState from galaxy.api.consts import Platform +from galaxy.api.errors import ApplicationError, InvalidCredentials, UnknownError from galaxy.api.jsonrpc import InvalidParams -from galaxy.api.errors import ApplicationError, InvalidCredentials, UnknownError, AuthenticationRequired import serialization from cache import Cache @@ -19,7 +19,7 @@ CommunicationId, TitleId, TrophyTitles, UnixTimestamp, PSNClient, MAX_TITLE_IDS_PER_REQUEST ) -from typing import Dict, List, Set, Iterable, Tuple, Optional +from typing import Dict, List, Set, Iterable, Tuple, Optional, Any from version import __version__ from http_client import OAUTH_LOGIN_URL, OAUTH_LOGIN_REDIRECT_URL @@ -122,31 +122,17 @@ async def filter_games(titles): await self._psn_client.async_get_owned_games() ) - # TODO: backward compatibility. remove when GLX handles batch imports - async def get_unlocked_achievements(self, game_id: TitleId): - async def get_game_comm_id(): - comm_ids: List[CommunicationId] = (await self.get_game_communication_ids([game_id]))[game_id] - if not self._is_game(comm_ids): - raise InvalidParams() - return comm_ids[0] + async def get_unlocked_achievements(self, game_id: str, context: Any) -> List[Achievement]: + if not context: + return [] + comm_ids: List[CommunicationId] = (await self.get_game_communication_ids([game_id]))[game_id] + if not self._is_game(comm_ids): + raise InvalidParams() + return self._get_game_trophies_from_cache(comm_ids, context)[0] - return await self._psn_client.async_get_earned_trophies( - await get_game_comm_id() - ) - - async def start_achievements_import(self, game_ids: List[TitleId]): - if not self._http_client.is_authenticated: - raise AuthenticationRequired - await super().start_achievements_import(game_ids) - - async def import_games_achievements(self, game_ids: Iterable[TitleId]): - try: - games_cids = await self.get_game_communication_ids(game_ids) - trophy_titles = await self._psn_client.get_trophy_titles() - except ApplicationError as error: - for title_id in game_ids: - self.game_achievements_import_failure(title_id, error) - return + async def prepare_achievements_context(self, game_ids: List[str]) -> Any: + games_cids = await self.get_game_communication_ids(game_ids) + trophy_titles = await self._psn_client.get_trophy_titles() pending_cid_tids, pending_tid_cids, tid_trophies = self._process_trophies_cache(games_cids, trophy_titles) @@ -167,10 +153,7 @@ async def import_games_achievements(self, game_ids: Iterable[TitleId]): except (pickle.PicklingError, binascii.Error): logging.error("Can not serialize trophies cache") - # log if some games has not been processed (it shouldn't happen) - for tid in pending_tid_cids.keys(): - logging.error("Not fetched all trophies for game %s", tid) - self.game_achievements_import_failure(tid, UnknownError()) + return trophy_titles def _process_trophies_cache( self, @@ -182,9 +165,6 @@ def _process_trophies_cache( tid_trophies: _TID_TROPHIES_DICT = {} for title_id, comm_ids in games_cids.items(): - if not self._is_game(comm_ids): - self.game_achievements_import_failure(title_id, InvalidParams()) - continue game_trophies, pending_comm_ids = self._get_game_trophies_from_cache(comm_ids, trophy_titles) @@ -193,9 +173,6 @@ def _process_trophies_cache( pending_cid_tids[comm_id].add(title_id) pending_tid_cids[title_id].update(pending_comm_ids) tid_trophies[title_id] = game_trophies - else: - # all trophies fetched from cache - self.game_achievements_import_success(title_id, game_trophies) return pending_cid_tids, pending_tid_cids, tid_trophies @@ -225,7 +202,6 @@ async def _import_trophies( def handle_error(error_): for tid_ in pending_tids: del pending_tid_cids[tid_] - self.game_achievements_import_failure(tid_, error_) try: trophies: List[Achievement] = await self._psn_client.async_get_earned_trophies(comm_id) @@ -238,7 +214,6 @@ def handle_error(error_): pending_comm_ids.remove(comm_id) if not pending_comm_ids: # the game has already all comm ids processed - self.game_achievements_import_success(tid, game_trophies) del pending_tid_cids[tid] except ApplicationError as error: handle_error(error) @@ -246,6 +221,15 @@ def handle_error(error_): logging.exception("Unhandled exception. Please report it to the plugin developers") handle_error(UnknownError()) + async def prepare_user_presence_context(self, user_ids: List[str]) -> Any: + return await self._psn_client.async_get_friends_presences() + + async def get_user_presence(self, user_id: str, context: Any) -> UserPresence: + for user in context: + if user_id in user: + return user[user_id] + return UserPresence(PresenceState.Unknown) + async def get_friends(self): return await self._psn_client.async_get_friends() diff --git a/src/psn_client.py b/src/psn_client.py index e4e6a19..58d5c0d 100755 --- a/src/psn_client.py +++ b/src/psn_client.py @@ -5,7 +5,7 @@ from typing import Dict, List, NewType, Tuple from galaxy.api.errors import UnknownBackendResponse -from galaxy.api.types import Achievement, Game, LicenseInfo, FriendInfo +from galaxy.api.types import Achievement, Game, LicenseInfo, UserInfo, UserPresence, PresenceState from galaxy.api.consts import LicenseType from http_client import paginate_url @@ -37,8 +37,13 @@ USER_INFO_URL = "https://pl-prof.np.community.playstation.net/userProfile/v1/users/{user_id}/profile2" \ "?fields=accountId,onlineId" +DEFAULT_AVATAR_SIZE = "l" FRIENDS_URL = "https://us-prof.np.community.playstation.net/userProfile/v1/users/{user_id}/friends/profiles2" \ - "?fields=accountId,onlineId" + "?fields=accountId,onlineId,avatarUrls&avatarSizes={avatar_size_list}" + +FRIENDS_WITH_PRESENCE_URL = "https://us-prof.np.community.playstation.net/userProfile/v1/users/{user_id}/friends/profiles2" \ + "?fields=accountId,onlineId,primaryOnlineStatus,presences(@titleInfo,lastOnlineDate)" + DEFAULT_LIMIT = 100 MAX_TITLE_IDS_PER_REQUEST = 5 @@ -195,18 +200,58 @@ def trophies_parser(response) -> List[Achievement]: async def async_get_friends(self): def friend_info_parser(profile): - return FriendInfo( + + avatar_url = None + for avatar in profile["avatarUrls"]: + avatar_url = avatar["avatarUrl"] + + return UserInfo( user_id=str(profile["accountId"]), - user_name=str(profile["onlineId"]) + user_name=str(profile["onlineId"]), + avatar_url=avatar_url, + profile_url=f"https://my.playstation.com/profile/{str(profile['onlineId'])}" ) def friend_list_parser(response): + logging.info(response) return [ friend_info_parser(profile) for profile in response.get("profiles", []) ] if response else [] return await self.fetch_paginated_data( friend_list_parser, - FRIENDS_URL.format(user_id="me"), + FRIENDS_URL.format(user_id="me", avatar_size_list=DEFAULT_AVATAR_SIZE), + "totalResults" + ) + + async def async_get_friends_presences(self): + def friend_info_parser(profile): + + if profile["primaryOnlineStatus"] == "online": + presence_state = PresenceState.Online + else: + presence_state = PresenceState.Offline + + game_title = game_id = None + + if "presences" in profile: + for presence in profile["presences"]: + try: + if presence["onlineStatus"] == "online" and presence["platform"] == "PS4": + game_title = presence["titleName"] + game_id = presence["npTitleId"] + except: + continue + + return {profile['accountId']: UserPresence(presence_state, game_id, game_title)} + + def friends_with_presence_parser(response): + return [ + friend_info_parser(profile) for profile in response.get("profiles", []) + ] if response else [] + + return await self.fetch_paginated_data( + friends_with_presence_parser, + FRIENDS_WITH_PRESENCE_URL.format(user_id="me"), "totalResults" ) diff --git a/src/version.py b/src/version.py index 820250e..93e925f 100755 --- a/src/version.py +++ b/src/version.py @@ -1 +1 @@ -__version__ = "0.24.1" +__version__ = "0.26" diff --git a/tests/integration_test.py b/tests/integration_test.py index 56f970b..c0fbe71 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -42,19 +42,14 @@ def test_integration(): plugin_socket.sendall((json.dumps(request)+"\n").encode("utf-8")) response = json.loads(plugin_socket.recv(4096)) print(response) - assert response == { - "id": "3", - "jsonrpc": "2.0", - "result": { - "platform_name": "psn", - "features": [ - "ImportOwnedGames", - "ImportAchievements", - "ImportFriends" - ], - "token": token - } - }, "Response differs from expected" + assert response["result"]["platform_name"] == "psn" + assert set(response["result"]["features"]) == set([ + 'ImportAchievements', + 'ImportOwnedGames', + 'ImportUserPresence', + 'ImportFriends' + ]) + assert response["result"]["token"] == token plugin_socket.close() result.wait(TIMEOUT) diff --git a/tests/test_achievements.py b/tests/test_achievements.py index 9cb1663..71d3739 100644 --- a/tests/test_achievements.py +++ b/tests/test_achievements.py @@ -3,12 +3,12 @@ from galaxy.api.errors import AuthenticationRequired, UnknownBackendResponse from psn_client import EARNED_TROPHIES_PAGE from tests.async_mock import AsyncMock -from tests.test_data import BACKEND_TROPHIES, COMMUNICATION_ID, GAMES, TITLE_TO_COMMUNICATION_ID, UNLOCKED_ACHIEVEMENTS -from unittest.mock import call -from itertools import chain +from unittest.mock import MagicMock +from tests.test_data import COMMUNICATION_ID, GAMES, TITLE_TO_COMMUNICATION_ID, UNLOCKED_ACHIEVEMENTS, CONTEXT, TROPHIES_CACHE, BACKEND_TROPHIES GET_ALL_TROPHIES_URL = EARNED_TROPHIES_PAGE.format(communication_id=COMMUNICATION_ID, trophy_group_id="all") -GAME_ID = "CUSA07917_00" + +GAME_ID = "CUSA07320_00" GAME_IDS = [game.game_id for game in GAMES] async def _wait_for_comm_ids_and_import_start(): @@ -34,7 +34,6 @@ async def mock_get_game_communication_id(mocker): async def mock_get_game_communication_ids(mocker): mocked = _mock_get_game_communication_ids(mocker, TITLE_TO_COMMUNICATION_ID) yield mocked - mocked.assert_called_once_with(GAME_IDS) @pytest.fixture @@ -62,198 +61,30 @@ def mock_import_achievements_failure(mocker): @pytest.mark.asyncio async def test_not_authenticated(psn_plugin): with pytest.raises(AuthenticationRequired): - await psn_plugin.start_achievements_import([GAME_ID]) + await psn_plugin.prepare_achievements_context([GAME_ID]) await asyncio.sleep(0) @pytest.mark.asyncio async def test_get_unlocked_achievements( authenticated_plugin, - mock_get_game_communication_id, - mock_async_get_earned_trophies -): - mock_async_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS - assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID) - mock_async_get_earned_trophies.assert_called_once_with(COMMUNICATION_ID) - - -@pytest.mark.asyncio -async def test_get_unlocked_achievements_error( - authenticated_plugin, - mock_get_game_communication_id, - mock_async_get_earned_trophies -): - mock_async_get_earned_trophies.side_effect = UnknownBackendResponse() - - with pytest.raises(UnknownBackendResponse): - await authenticated_plugin.get_unlocked_achievements(GAME_ID) - - -@pytest.mark.asyncio -async def test_import_games_achievements( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success -): - mock_async_get_game_communication_id_map.return_value = {GAME_ID: [COMMUNICATION_ID]} - mock_get_trophy_titles.return_value = {COMMUNICATION_ID: 1388308713} - mock_async_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS - - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - mock_async_get_earned_trophies.assert_called_once_with(COMMUNICATION_ID) - mock_import_achievements_success.assert_called_once_with(GAME_ID, UNLOCKED_ACHIEVEMENTS) - - -@pytest.mark.asyncio -async def test_import_multiple_games_achievements( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success -): - mock_async_get_game_communication_id_map.return_value = TITLE_TO_COMMUNICATION_ID - mock_get_trophy_titles.return_value = { - comm_id: 1559744411 for comm_id in chain.from_iterable(TITLE_TO_COMMUNICATION_ID.values()) - } - mock_async_get_earned_trophies.return_value = [] - await authenticated_plugin.start_achievements_import(GAME_IDS) - await _wait_for_comm_ids_and_import_start() - - mock_async_get_earned_trophies.assert_has_calls( - [call(comm_id) for comm_id in chain.from_iterable(TITLE_TO_COMMUNICATION_ID.values())], - any_order=True - ) - -@pytest.mark.asyncio -async def test_import_game_achievements_multiple_comm_ids( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success -): - mock_async_get_game_communication_id_map.return_value = { - GAME_ID: ["NPWR12784_00", "NPWR10584_00"] - } - mock_get_trophy_titles.return_value = { - "NPWR12784_00": 1559744411, - "NPWR10584_00": 1559744411 - } - mock_async_get_earned_trophies.side_effect = [ - [UNLOCKED_ACHIEVEMENTS[0]], - [UNLOCKED_ACHIEVEMENTS[1]] - ] - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - mock_async_get_earned_trophies.assert_has_calls( - [call("NPWR12784_00"), call("NPWR10584_00")], - any_order=True - ) - mock_import_achievements_success.assert_called_once_with(GAME_ID, UNLOCKED_ACHIEVEMENTS[0:2]) - -@pytest.mark.asyncio -async def test_import_game_achievements_same_comm_id( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success -): - mock_async_get_game_communication_id_map.return_value = { - "CUSA07917_00": ["NPWR12784_00"], - "CUSA02000_00": ["NPWR12784_00"] - } - mock_get_trophy_titles.return_value = { - "NPWR12784_00": 1559744411 - } - mock_async_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS - await authenticated_plugin.start_achievements_import(["CUSA07917_00", "CUSA02000_00"]) - await _wait_for_comm_ids_and_import_start() - - mock_async_get_earned_trophies.assert_called_once_with("NPWR12784_00") - mock_import_achievements_success.assert_has_calls( - [ - call("CUSA07917_00", UNLOCKED_ACHIEVEMENTS), - call("CUSA02000_00", UNLOCKED_ACHIEVEMENTS) - ], - any_order=True - ) - -@pytest.mark.asyncio -async def test_import_achievements_valid_cache( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success + mock_get_game_communication_ids, ): - mock_async_get_game_communication_id_map.return_value = {GAME_ID: [COMMUNICATION_ID]} - mock_get_trophy_titles.return_value = {COMMUNICATION_ID: 1388308713} - mock_async_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS - - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - assert mock_get_trophy_titles.call_count == 1 - assert mock_async_get_earned_trophies.call_count == 1 - assert mock_import_achievements_success.call_count == 1 - - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - assert mock_get_trophy_titles.call_count == 2 - assert mock_async_get_earned_trophies.call_count == 1 - assert mock_import_achievements_success.call_count == 2 + mock_get_game_communication_ids.return_value = TITLE_TO_COMMUNICATION_ID + authenticated_plugin._trophies_cache = TROPHIES_CACHE + assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID, CONTEXT) @pytest.mark.asyncio -async def test_import_achievements_invalid_cache( +async def test_get_unlocked_achievements_trophies_cache_called( authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_success + mock_get_game_communication_ids ): - mock_async_get_game_communication_id_map.return_value = {GAME_ID: [COMMUNICATION_ID]} - mock_get_trophy_titles.return_value = {COMMUNICATION_ID: 1388308713} - mock_async_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS + mock_get_game_communication_ids.return_value = TITLE_TO_COMMUNICATION_ID + authenticated_plugin._get_game_trophies_from_cache = MagicMock() - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() + await authenticated_plugin.get_unlocked_achievements(GAME_ID, CONTEXT) - assert mock_get_trophy_titles.call_count == 1 - assert mock_async_get_earned_trophies.call_count == 1 - assert mock_import_achievements_success.call_count == 1 - - mock_get_trophy_titles.return_value[COMMUNICATION_ID] += 100 - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - assert mock_get_trophy_titles.call_count == 2 - assert mock_async_get_earned_trophies.call_count == 2 - assert mock_import_achievements_success.call_count == 2 - -@pytest.mark.asyncio -async def test_import_games_achievements_error( - authenticated_plugin, - mock_async_get_game_communication_id_map, - mock_get_trophy_titles, - mock_async_get_earned_trophies, - mock_import_achievements_failure -): - mock_async_get_game_communication_id_map.return_value = {GAME_ID: [COMMUNICATION_ID]} - mock_get_trophy_titles.return_value = {COMMUNICATION_ID: 1559744411} - mock_async_get_earned_trophies.side_effect = UnknownBackendResponse() - - await authenticated_plugin.start_achievements_import([GAME_ID]) - await _wait_for_comm_ids_and_import_start() - - mock_import_achievements_failure.assert_called_once_with(GAME_ID, UnknownBackendResponse()) + authenticated_plugin._get_game_trophies_from_cache.assert_called_once() @pytest.mark.asyncio @pytest.mark.parametrize("backend_response, trophies", [ @@ -278,6 +109,27 @@ async def test_async_get_earned_trophies( http_get.assert_called_once_with(GET_ALL_TROPHIES_URL) +@pytest.mark.asyncio +async def test_prepare_achievements_context_error( + authenticated_plugin, + mock_get_game_communication_ids, + mock_async_get_earned_trophies +): + mock_get_game_communication_ids.side_effect = UnknownBackendResponse() + + with pytest.raises(UnknownBackendResponse): + await authenticated_plugin.prepare_achievements_context([GAME_ID]) + + +@pytest.mark.asyncio +async def test_get_unlocked_achievements_no_context( + authenticated_plugin, + mock_get_game_communication_ids +): + mock_get_game_communication_ids.return_value = TITLE_TO_COMMUNICATION_ID + authenticated_plugin._trophies_cache = TROPHIES_CACHE + assert [] == await authenticated_plugin.get_unlocked_achievements(GAME_ID, None) + @pytest.mark.asyncio @pytest.mark.parametrize("backend_response", [ {"trophies": "bad_format"}, diff --git a/tests/test_communication_id_cache.py b/tests/test_communication_id_cache.py index ffe72a4..8695507 100644 --- a/tests/test_communication_id_cache.py +++ b/tests/test_communication_id_cache.py @@ -7,9 +7,9 @@ from plugin import COMMUNICATION_IDS_CACHE_KEY from psn_client import GAME_DETAILS_URL from tests.async_mock import AsyncMock -from tests.test_data import GAMES, TITLE_TO_COMMUNICATION_ID, TITLES, UNLOCKED_ACHIEVEMENTS +from tests.test_data import GAMES, TITLE_TO_COMMUNICATION_ID, TITLES, UNLOCKED_ACHIEVEMENTS, CONTEXT, TROPHIES_CACHE -GAME_ID = GAMES[0].game_id +GAME_ID = GAMES[7].game_id @pytest.fixture @@ -127,14 +127,13 @@ async def test_cache_miss_on_dlc_achievements_retrieval( assert COMMUNICATION_IDS_CACHE_KEY not in authenticated_plugin.persistent_cache with pytest.raises(InvalidParams): - await authenticated_plugin.get_unlocked_achievements(dlc_id) + await authenticated_plugin.get_unlocked_achievements(dlc_id, CONTEXT) assert mapping == authenticated_plugin.persistent_cache[COMMUNICATION_IDS_CACHE_KEY] assert not mock_client_get_earned_trophies.called mock_get_game_communication_id_map.assert_called_once_with([dlc_id]) - @pytest.mark.asyncio async def test_cache_miss_on_game_achievements_retrieval( authenticated_plugin, @@ -145,17 +144,16 @@ async def test_cache_miss_on_game_achievements_retrieval( mapping = {GAME_ID: comm_ids} mock_get_game_communication_id_map.return_value = mapping mock_client_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS + authenticated_plugin._trophies_cache = TROPHIES_CACHE assert "communication_ids" not in authenticated_plugin.persistent_cache - assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID) + assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID, CONTEXT) assert mapping == authenticated_plugin.persistent_cache[COMMUNICATION_IDS_CACHE_KEY] - mock_client_get_earned_trophies.assert_called_once_with(comm_ids[0]) mock_get_game_communication_id_map.assert_called_once_with([GAME_ID]) - @pytest.mark.asyncio async def test_cached_on_dlc_achievements_retrieval( authenticated_plugin, @@ -168,7 +166,7 @@ async def test_cached_on_dlc_achievements_retrieval( mock_persistent_cache.return_value = {COMMUNICATION_IDS_CACHE_KEY: mapping} with pytest.raises(InvalidParams): - await authenticated_plugin.get_unlocked_achievements(dlc_id) + await authenticated_plugin.get_unlocked_achievements(dlc_id, CONTEXT) assert mapping == authenticated_plugin.persistent_cache[COMMUNICATION_IDS_CACHE_KEY] @@ -179,22 +177,20 @@ async def test_cached_on_dlc_achievements_retrieval( @pytest.mark.asyncio async def test_cached_on_game_achievements_retrieval( authenticated_plugin, - mock_client_get_earned_trophies, mock_get_game_communication_id_map, mock_persistent_cache ): comm_ids = TITLE_TO_COMMUNICATION_ID[GAME_ID] mapping = {GAME_ID: comm_ids} - mock_client_get_earned_trophies.return_value = UNLOCKED_ACHIEVEMENTS + authenticated_plugin._trophies_cache = TROPHIES_CACHE mock_persistent_cache.return_value = {COMMUNICATION_IDS_CACHE_KEY: mapping.copy()} - assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID) + assert UNLOCKED_ACHIEVEMENTS == await authenticated_plugin.get_unlocked_achievements(GAME_ID, CONTEXT) assert not mock_get_game_communication_id_map.called assert mapping == authenticated_plugin.persistent_cache[COMMUNICATION_IDS_CACHE_KEY] - mock_client_get_earned_trophies.assert_called_once_with(comm_ids[0]) @pytest.mark.asyncio diff --git a/tests/test_data.py b/tests/test_data.py index 392bc13..a84e6d1 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,7 +1,8 @@ from galaxy.api.consts import LicenseType -from galaxy.api.types import Achievement, FriendInfo, Game, LicenseInfo +from galaxy.api.types import Achievement, UserInfo, Game, LicenseInfo +from cache import Cache, CacheEntry -COMMUNICATION_ID = "NPWR12784_00" +COMMUNICATION_ID = "NPWR11556_00" DEFAULT_LICENSE = LicenseInfo(LicenseType.SinglePurchase, None) @@ -97,7 +98,7 @@ }, { "trophyId": 3, - "fromUser": {"onlineId": "user-id", "earned": True, "earnedDate": "2013-12-29T09:18:33Z"}, + "fromUser": {"onlineId": "user-id", "earned": False, "earnedDate": "2013-12-29T09:18:33Z"}, "trophyName": "achievement 3", "groupId": "001" }, @@ -109,7 +110,7 @@ }, { "trophyId": 5, - "fromUser": {"onlineId": "user-id", "earned": True, "earnedDate": "1987-02-07T10:14:42Z"}, + "fromUser": {"onlineId": "user-id", "earned": False, "earnedDate": "1987-02-07T10:14:42Z"}, "trophyName": "achievement 5", "groupId": "022" }, @@ -117,12 +118,14 @@ } UNLOCKED_ACHIEVEMENTS = [ - Achievement(achievement_id="NPWR12784_00_1", achievement_name="achievement 1", unlock_time=538304493), - Achievement(achievement_id="NPWR12784_00_2", achievement_name="achievement 2", unlock_time=1318782798), - Achievement(achievement_id="NPWR12784_00_3", achievement_name="achievement 3", unlock_time=1388308713), - Achievement(achievement_id="NPWR12784_00_5", achievement_name="achievement 5", unlock_time=539691282), + Achievement(achievement_id="NPWR11556_00_1", achievement_name="achievement 1", unlock_time=538304493.0,), + Achievement(achievement_id="NPWR11556_00_2", achievement_name="achievement 2", unlock_time=1318782798.0) ] +TROPHIES_CACHE = Cache() +TROPHIES_CACHE._entries ={"NPWR11556_00": CacheEntry(value=UNLOCKED_ACHIEVEMENTS, timestamp=1490374318.0)} + + BACKEND_USER_PROFILES = { "start": 0, "size": 7, @@ -200,11 +203,14 @@ } FRIEND_INFO_LIST = [ - FriendInfo(user_id="1", user_name="veryopenperson"), - FriendInfo(user_id="2", user_name="ImTestingSth1"), - FriendInfo(user_id="3", user_name="venom6683"), - FriendInfo(user_id="4", user_name="l_Touwa_l"), - FriendInfo(user_id="5", user_name="Resilb"), - FriendInfo(user_id="6", user_name="Di_PL"), - FriendInfo(user_id="7", user_name="nyash") + UserInfo(user_id="1", user_name="veryopenperson", avatar_url="http://playstation.net/avatar/DefaultAvatar_m.png", profile_url="https://my.playstation.com/profile/veryopenperson"), + UserInfo(user_id="2", user_name="ImTestingSth1", avatar_url="http://playstation.net/avatar/DefaultAvatar_m.png", profile_url="https://my.playstation.com/profile/ImTestingSth1"), + UserInfo(user_id="3", user_name="venom6683", avatar_url="http://playstation.net/avatar/WWS_E/E0007_m.png", profile_url="https://my.playstation.com/profile/venom6683"), + UserInfo(user_id="4", user_name="l_Touwa_l", avatar_url="http://playstation.net/avatar/DefaultAvatar_m.png", profile_url="https://my.playstation.com/profile/l_Touwa_l"), + UserInfo(user_id="5", user_name="Resilb", avatar_url="http://playstation.net/avatar/DefaultAvatar_m.png", profile_url="https://my.playstation.com/profile/Resilb"), + UserInfo(user_id="6", user_name="Di_PL", avatar_url="http://playstation.net/avatar/DefaultAvatar_m.png", profile_url="https://my.playstation.com/profile/Di_PL"), + UserInfo(user_id="7", user_name="nyash", avatar_url="http://playstation.net/avatar/3RD/E9A0BB8BC40BBF9B_M.png", profile_url="https://my.playstation.com/profile/nyash") ] + +CONTEXT = {'NPWR16617_00': 1569108306.0, 'NPWR11453_00': 1567944392.0, 'NPWR16532_00': 1567545710.0, 'NPWR10526_00': 1566592417.0, 'NPWR15355_00': 1554141081.0, 'NPWR16687_00': 1553463882.0, 'NPWR13157_00': 1552250990.0, 'NPWR09412_00': 1549056652.0, 'NPWR12518_00': 1546473026.0, 'NPWR14695_00': 1546127236.0, 'NPWR07028_00': 1546030925.0, 'NPWR14376_00': 1545849865.0, 'NPWR13320_00': 1544914476.0, 'NPWR11631_00': 1544911126.0, 'NPWR06302_00': 1544888234.0, 'NPWR06685_00': 1544881604.0, 'NPWR13650_00': 1544828007.0, 'NPWR12662_00': 1544733951.0, 'NPWR15453_00': 1544646241.0, 'NPWR08260_00': 1544561891.0, 'NPWR13648_00': 1544555969.0, 'NPWR14065_00': 1543755146.0, 'NPWR07897_00': 1528635032.0, 'NPWR11469_00': 1528580350.0, 'NPWR08899_00': 1528349310.0, 'NPWR14963_00': 1528225627.0, 'NPWR11704_00': 1528053359.0, 'NPWR10261_00': 1526112707.0, 'NPWR14513_00': 1525182963.0, 'NPWR13970_00': 1524598278.0, 'NPWR05424_00': 1523813120.0, 'NPWR11920_00': 1523381863.0, 'NPWR07942_00': 1523299564.0, 'NPWR09187_00': 1523137528.0, 'NPWR14858_00': 1522962802.0, 'NPWR08864_00': 1522791973.0, 'NPWR09600_00': 1520813610.0, 'NPWR13161_00': 1519857527.0, 'NPWR09694_00': 1518818903.0, 'NPWR10810_00': 1518302892.0, 'NPWR09071_00': 1516049483.0, 'NPWR11866_00': 1514765179.0, 'NPWR08840_00': 1514647655.0, 'NPWR09304_00': 1514640325.0, 'NPWR08844_00': 1514401356.0, 'NPWR13263_00': 1514250188.0, 'NPWR06221_00': 1513543300.0, 'NPWR11659_00': 1510523951.0, 'NPWR10569_00': 1509043324.0, 'NPWR13412_00': 1508862631.0, 'NPWR04915_00': 1505244423.0, 'NPWR12845_00': 1504894257.0, 'NPWR12042_00': 1504214093.0, 'NPWR08935_00': 1502561238.0, 'NPWR06040_00': 1500916492.0, 'NPWR10876_00': 1500591963.0, 'NPWR08268_00': 1499886156.0, 'NPWR09350_00': 1499705028.0, 'NPWR08661_00': 1498935916.0, 'NPWR05818_00': 1494083469.0, 'NPWR08983_00': 1490903238.0, 'NPWR11556_00': 1490374318.0, 'NPWR04914_00': 1489329259.0} + diff --git a/tests/test_friends.py b/tests/test_friends.py index f13e872..8376d88 100644 --- a/tests/test_friends.py +++ b/tests/test_friends.py @@ -4,7 +4,8 @@ from http_client import paginate_url from tests.test_data import BACKEND_USER_PROFILES, FRIEND_INFO_LIST -GET_ALL_FRIENDS_URL = paginate_url(FRIENDS_URL.format(user_id="me"), limit=DEFAULT_LIMIT) +DEFAULT_AVATAR_SIZE = "l" +GET_ALL_FRIENDS_URL = paginate_url(FRIENDS_URL.format(user_id="me", avatar_size_list=DEFAULT_AVATAR_SIZE), limit=DEFAULT_LIMIT) def test_not_authenticated(psn_plugin, event_loop):