diff --git a/galaxy/api/errors.py b/galaxy/api/errors.py new file mode 100644 index 0000000..d918fc7 --- /dev/null +++ b/galaxy/api/errors.py @@ -0,0 +1,73 @@ +from galaxy.api.jsonrpc import ApplicationError + +class UnknownError(ApplicationError): + def __init__(self, data=None): + super().__init__(0, "Unknown error", data) + +class InvalidCredentials(ApplicationError): + def __init__(self, data=None): + super().__init__(100, "Invalid credentials", data) + +class NetworkError(ApplicationError): + def __init__(self, data=None): + super().__init__(101, "Network error", data) + +class LoggedInElsewhere(ApplicationError): + def __init__(self, data=None): + super().__init__(102, "Logged in elsewhere", data) + +class ProtocolError(ApplicationError): + def __init__(self, data=None): + super().__init__(103, "Protocol error", data) + +class BackendNotAvailable(ApplicationError): + def __init__(self, data=None): + super().__init__(104, "Backend not available", data) + +class BackendTimeout(ApplicationError): + def __init__(self, data=None): + super().__init__(105, "Backend timed out", data) + +class BackendError(ApplicationError): + def __init__(self, data=None): + super().__init__(106, "Backend error", data) + +class TemporaryBlocked(ApplicationError): + def __init__(self, data=None): + super().__init__(107, "Temporary blocked", data) + +class Banned(ApplicationError): + def __init__(self, data=None): + super().__init__(108, "Banned", data) + +class AccessDenied(ApplicationError): + def __init__(self, data=None): + super().__init__(109, "Access denied", data) + +class ParentalControlBlock(ApplicationError): + def __init__(self, data=None): + super().__init__(110, "Parental control block", data) + +class DeviceBlocked(ApplicationError): + def __init__(self, data=None): + super().__init__(111, "Device blocked", data) + +class RegionBlocked(ApplicationError): + def __init__(self, data=None): + super().__init__(112, "Region blocked", data) + +class FailedParsingManifest(ApplicationError): + def __init__(self, data=None): + super().__init__(200, "Failed parsing manifest", data) + +class TooManyMessagesSent(ApplicationError): + def __init__(self, data=None): + super().__init__(300, "Too many messages sent", data) + +class IncoherentLastMessage(ApplicationError): + def __init__(self, data=None): + super().__init__(400, "Different last message id on backend", data) + +class MessageNotFound(ApplicationError): + def __init__(self, data=None): + super().__init__(500, "Message not found", data) diff --git a/galaxy/api/jsonrpc.py b/galaxy/api/jsonrpc.py index f1cfe41..e7449c3 100644 --- a/galaxy/api/jsonrpc.py +++ b/galaxy/api/jsonrpc.py @@ -26,9 +26,19 @@ class InvalidParams(JsonRpcError): def __init__(self): super().__init__(-32601, "Invalid params") +class Timeout(JsonRpcError): + def __init__(self): + super().__init__(-32000, "Method timed out") + +class Aborted(JsonRpcError): + def __init__(self): + super().__init__(-32001, "Method aborted") + class ApplicationError(JsonRpcError): - def __init__(self, data): - super().__init__(-32003, "Custom error", data) + def __init__(self, code, message, data): + if code >= -32768 and code <= -32000: + raise ValueError("The error code in reserved range") + super().__init__(code, message, data) Request = namedtuple("Request", ["method", "params", "id"], defaults=[{}, None]) Method = namedtuple("Method", ["callback", "internal"]) @@ -172,10 +182,13 @@ class Server(): "id": request_id, "error": { "code": error.code, - "message": error.message, - "data": error.data + "message": error.message } } + + if error.data is not None: + response["error"]["data"] = error.data + self._send(response) class NotificationClient(): diff --git a/galaxy/api/types.py b/galaxy/api/types.py index 899f682..0b73ba8 100644 --- a/galaxy/api/types.py +++ b/galaxy/api/types.py @@ -1,7 +1,6 @@ from dataclasses import dataclass from typing import List -from galaxy.api.jsonrpc import ApplicationError from galaxy.api.consts import LocalGameState, PresenceState @dataclass @@ -9,14 +8,6 @@ class Authentication(): user_id: str user_name: str -class LoginError(ApplicationError): - def __init__(self, current_step, reason): - data = { - "current_step": current_step, - "reason": reason - } - super().__init__(data) - @dataclass class LicenseInfo(): license_type: str @@ -35,37 +26,16 @@ class Game(): dlcs: List[Dlc] license_info: LicenseInfo -class GetGamesError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class Achievement(): achievement_id: str unlock_time: int -class GetAchievementsError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class LocalGame(): game_id: str local_game_state: LocalGameState -class GetLocalGamesError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class Presence(): presence_state: PresenceState @@ -80,47 +50,12 @@ class UserInfo(): avatar_url: str presence: Presence -class GetFriendsError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - -class GetUsersError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - -class SendMessageError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - -class MarkAsReadError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class Room(): room_id: str unread_message_count: int last_message_id: str -class GetRoomsError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class Message(): message_id: str @@ -128,22 +63,8 @@ class Message(): sent_time: int message_text: str -class GetRoomHistoryError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) - @dataclass class GameTime(): game_id: str time_played: int last_played_time: int - -class GetGameTimeError(ApplicationError): - def __init__(self, reason): - data = { - "reason": reason - } - super().__init__(data) \ No newline at end of file diff --git a/tests/test_achievements.py b/tests/test_achievements.py index eb5d441..cf5b2d7 100644 --- a/tests/test_achievements.py +++ b/tests/test_achievements.py @@ -1,7 +1,8 @@ import asyncio import json -from galaxy.api.types import Achievement, GetAchievementsError +from galaxy.api.types import Achievement +from galaxy.api.errors import UnknownError def test_success(plugin, readline, write): request = { @@ -49,7 +50,7 @@ def test_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_unlocked_achievements.side_effect = GetAchievementsError("reason") + plugin.get_unlocked_achievements.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_unlocked_achievements.assert_called() response = json.loads(write.call_args[0][0]) @@ -58,11 +59,8 @@ def test_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error" } } diff --git a/tests/test_authenticate.py b/tests/test_authenticate.py index 72ad82a..6a8ca26 100644 --- a/tests/test_authenticate.py +++ b/tests/test_authenticate.py @@ -1,7 +1,14 @@ import asyncio import json -from galaxy.api.types import Authentication, LoginError +import pytest + +from galaxy.api.types import Authentication +from galaxy.api.errors import ( + UnknownError, InvalidCredentials, NetworkError, LoggedInElsewhere, ProtocolError, + BackendNotAvailable, BackendTimeout, BackendError, TemporaryBlocked, Banned, AccessDenied, + ParentalControlBlock, DeviceBlocked, RegionBlocked +) def test_success(plugin, readline, write): request = { @@ -25,7 +32,23 @@ def test_success(plugin, readline, write): } } -def test_failure(plugin, readline, write): +@pytest.mark.parametrize("error,code,message", [ + pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), + pytest.param(InvalidCredentials, 100, "Invalid credentials", id="invalid_credentials"), + pytest.param(NetworkError, 101, "Network error", id="network_error"), + pytest.param(LoggedInElsewhere, 102, "Logged in elsewhere", id="logged_elsewhere"), + pytest.param(ProtocolError, 103, "Protocol error", id="protocol_error"), + pytest.param(BackendNotAvailable, 104, "Backend not available", id="backend_not_available"), + pytest.param(BackendTimeout, 105, "Backend timed out", id="backend_timeout"), + pytest.param(BackendError, 106, "Backend error", id="backend_error"), + pytest.param(TemporaryBlocked, 107, "Temporary blocked", id="temporary_blocked"), + pytest.param(Banned, 108, "Banned", id="banned"), + pytest.param(AccessDenied, 109, "Access denied", id="access_denied"), + pytest.param(ParentalControlBlock, 110, "Parental control block", id="parental_control_clock"), + pytest.param(DeviceBlocked, 111, "Device blocked", id="device_blocked"), + pytest.param(RegionBlocked, 112, "Region blocked", id="region_blocked") +]) +def test_failure(plugin, readline, write, error, code, message): request = { "jsonrpc": "2.0", "id": "3", @@ -33,7 +56,7 @@ def test_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.authenticate.side_effect = LoginError("step", "reason") + plugin.authenticate.side_effect = error() asyncio.run(plugin.run()) plugin.authenticate.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -42,12 +65,8 @@ def test_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "current_step": "step", - "reason": "reason" - } + "code": code, + "message": message } } diff --git a/tests/test_chat.py b/tests/test_chat.py index 474955e..9493458 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -1,9 +1,10 @@ import asyncio import json -from galaxy.api.types import ( - SendMessageError, MarkAsReadError, Room, GetRoomsError, Message, GetRoomHistoryError -) +import pytest + +from galaxy.api.types import Room, Message +from galaxy.api.errors import UnknownError, TooManyMessagesSent, IncoherentLastMessage, MessageNotFound def test_send_message_success(plugin, readline, write): request = { @@ -28,7 +29,11 @@ def test_send_message_success(plugin, readline, write): "result": None } -def test_send_message_failure(plugin, readline, write): +@pytest.mark.parametrize("error,code,message", [ + pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), + pytest.param(TooManyMessagesSent, 300, "Too many messages sent", id="too_many_messages") +]) +def test_send_message_failure(plugin, readline, write, error, code, message): request = { "jsonrpc": "2.0", "id": "6", @@ -40,7 +45,7 @@ def test_send_message_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.send_message.side_effect = SendMessageError("reason") + plugin.send_message.side_effect = error() asyncio.run(plugin.run()) plugin.send_message.assert_called_with(room_id="15", message="Bye") response = json.loads(write.call_args[0][0]) @@ -49,11 +54,8 @@ def test_send_message_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "6", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": code, + "message": message } } @@ -80,7 +82,16 @@ def test_mark_as_read_success(plugin, readline, write): "result": None } -def test_mark_as_read_failure(plugin, readline, write): +@pytest.mark.parametrize("error,code,message", [ + pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), + pytest.param( + IncoherentLastMessage, + 400, + "Different last message id on backend", + id="incoherent_last_message" + ) +]) +def test_mark_as_read_failure(plugin, readline, write, error, code, message): request = { "jsonrpc": "2.0", "id": "4", @@ -92,7 +103,7 @@ def test_mark_as_read_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.mark_as_read.side_effect = MarkAsReadError("reason") + plugin.mark_as_read.side_effect = error() asyncio.run(plugin.run()) plugin.mark_as_read.assert_called_with(room_id="18", last_message_id="7") response = json.loads(write.call_args[0][0]) @@ -101,11 +112,8 @@ def test_mark_as_read_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "4", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": code, + "message": message } } @@ -151,7 +159,7 @@ def test_get_rooms_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_rooms.side_effect = GetRoomsError("reason") + plugin.get_rooms.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_rooms.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -160,11 +168,8 @@ def test_get_rooms_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "9", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error" } } @@ -209,7 +214,11 @@ def test_get_room_history_from_message_success(plugin, readline, write): } } -def test_get_room_history_from_message_failure(plugin, readline, write): +@pytest.mark.parametrize("error,code,message", [ + pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), + pytest.param(MessageNotFound, 500, "Message not found", id="message_not_found") +]) +def test_get_room_history_from_message_failure(plugin, readline, write, error, code, message): request = { "jsonrpc": "2.0", "id": "7", @@ -221,7 +230,7 @@ def test_get_room_history_from_message_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_room_history_from_message.side_effect = GetRoomHistoryError("reason") + plugin.get_room_history_from_message.side_effect = error() asyncio.run(plugin.run()) plugin.get_room_history_from_message.assert_called_with(room_id="33", message_id="88") response = json.loads(write.call_args[0][0]) @@ -230,11 +239,8 @@ def test_get_room_history_from_message_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "7", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": code, + "message": message } } @@ -287,7 +293,7 @@ def test_get_room_history_from_timestamp_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_room_history_from_timestamp.side_effect = GetRoomHistoryError("reason") + plugin.get_room_history_from_timestamp.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_room_history_from_timestamp.assert_called_with( room_id="10", @@ -299,11 +305,8 @@ def test_get_room_history_from_timestamp_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error" } } diff --git a/tests/test_game_times.py b/tests/test_game_times.py index 95559ce..3c5690a 100644 --- a/tests/test_game_times.py +++ b/tests/test_game_times.py @@ -1,7 +1,8 @@ import asyncio import json -from galaxy.api.types import GameTime, GetGameTimeError +from galaxy.api.types import GameTime +from galaxy.api.errors import UnknownError def test_success(plugin, readline, write): request = { @@ -46,7 +47,7 @@ def test_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_game_times.side_effect = GetGameTimeError("reason") + plugin.get_game_times.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_game_times.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -55,11 +56,8 @@ def test_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error", } } diff --git a/tests/test_local_games.py b/tests/test_local_games.py index 4e1e28e..a243b33 100644 --- a/tests/test_local_games.py +++ b/tests/test_local_games.py @@ -1,8 +1,11 @@ import asyncio import json -from galaxy.api.types import GetLocalGamesError, LocalGame +import pytest + +from galaxy.api.types import LocalGame from galaxy.api.consts import LocalGameState +from galaxy.api.errors import UnknownError, FailedParsingManifest def test_success(plugin, readline, write): request = { @@ -38,7 +41,14 @@ def test_success(plugin, readline, write): } } -def test_failure(plugin, readline, write): +@pytest.mark.parametrize( + "error,code,message", + [ + pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"), + pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing") + ], +) +def test_failure(plugin, readline, write, error, code, message): request = { "jsonrpc": "2.0", "id": "3", @@ -46,7 +56,7 @@ def test_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_local_games.side_effect = GetLocalGamesError("reason") + plugin.get_local_games.side_effect = error() asyncio.run(plugin.run()) plugin.get_local_games.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -55,11 +65,8 @@ def test_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": code, + "message": message } } diff --git a/tests/test_owned_games.py b/tests/test_owned_games.py index 0d852e1..d0c8db9 100644 --- a/tests/test_owned_games.py +++ b/tests/test_owned_games.py @@ -1,7 +1,8 @@ import asyncio import json -from galaxy.api.types import Game, Dlc, LicenseInfo, GetGamesError +from galaxy.api.types import Game, Dlc, LicenseInfo +from galaxy.api.errors import UnknownError def test_success(plugin, readline, write): request = { @@ -73,7 +74,7 @@ def test_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_owned_games.side_effect = GetGamesError("reason") + plugin.get_owned_games.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_owned_games.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -82,11 +83,8 @@ def test_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error" } } diff --git a/tests/test_users.py b/tests/test_users.py index 50c4858..f4bf775 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -1,7 +1,8 @@ import asyncio import json -from galaxy.api.types import UserInfo, Presence, GetFriendsError, GetUsersError +from galaxy.api.types import UserInfo, Presence +from galaxy.api.errors import UnknownError from galaxy.api.consts import PresenceState def test_get_friends_success(plugin, readline, write): @@ -73,7 +74,7 @@ def test_get_friends_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_friends.side_effect = GetFriendsError("reason") + plugin.get_friends.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_friends.assert_called_with() response = json.loads(write.call_args[0][0]) @@ -82,11 +83,8 @@ def test_get_friends_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "3", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error", } } @@ -202,7 +200,7 @@ def test_get_users_failure(plugin, readline, write): } readline.side_effect = [json.dumps(request), ""] - plugin.get_users.side_effect = GetUsersError("reason") + plugin.get_users.side_effect = UnknownError() asyncio.run(plugin.run()) plugin.get_users.assert_called_with(user_id_list=["10", "11", "12"]) response = json.loads(write.call_args[0][0]) @@ -211,10 +209,7 @@ def test_get_users_failure(plugin, readline, write): "jsonrpc": "2.0", "id": "12", "error": { - "code": -32003, - "message": "Custom error", - "data": { - "reason": "reason" - } + "code": 0, + "message": "Unknown error" } }