PLINT-1088 Including internal_type in errors' data member

This commit is contained in:
Martin Grzymski
2021-10-20 07:52:40 +00:00
parent 761598de54
commit 5daa386f6e
14 changed files with 179 additions and 95 deletions

View File

@@ -1,6 +1,6 @@
import asyncio
from collections import namedtuple
from collections.abc import Iterable
from collections.abc import Iterable, Mapping
import logging
import inspect
import json
@@ -16,7 +16,12 @@ class JsonRpcError(Exception):
def __init__(self, code, message, data=None):
self.code = code
self.message = str(message)
self.data = data
self.data = {}
if data is not None:
if not isinstance(data, Mapping):
raise TypeError(f"Data parameter should be a mapping, got this instead: {data}")
self.data = data
self.data.update({"internal_type": type(self).__name__})
super().__init__()
def __eq__(self, other):
@@ -25,43 +30,41 @@ class JsonRpcError(Exception):
def json(self):
obj = {
"code": self.code,
"message": self.message
"message": self.message,
"data": self.data
}
if self.data is not None:
obj["data"] = self.data
return obj
class ParseError(JsonRpcError):
def __init__(self, message="Parse error"):
super().__init__(-32700, message)
def __init__(self, message="Parse error", data=None):
super().__init__(-32700, message, data)
class InvalidRequest(JsonRpcError):
def __init__(self, message="Invalid Request"):
super().__init__(-32600, message)
def __init__(self, message="Invalid Request", data=None):
super().__init__(-32600, message, data)
class MethodNotFound(JsonRpcError):
def __init__(self, message="Method not found"):
super().__init__(-32601, message)
def __init__(self, message="Method not found", data=None):
super().__init__(-32601, message, data)
class InvalidParams(JsonRpcError):
def __init__(self, message="Invalid params"):
super().__init__(-32602, message)
def __init__(self, message="Invalid params", data=None):
super().__init__(-32602, message, data)
class Timeout(JsonRpcError):
def __init__(self, message="Method timed out"):
super().__init__(-32000, message)
def __init__(self, message="Method timed out", data=None):
super().__init__(-32000, message, data)
class Aborted(JsonRpcError):
def __init__(self, message="Method aborted"):
super().__init__(-32001, message)
def __init__(self, message="Method aborted", data=None):
super().__init__(-32001, message, data)
class ApplicationError(JsonRpcError):

View File

@@ -79,11 +79,11 @@ async def test_get_unlocked_achievements_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_unlocked_achievements_error(exception, code, message, plugin, read, write):
async def test_get_unlocked_achievements_error(exception, code, message, internal_type, plugin, read, write):
plugin.prepare_achievements_context.return_value = async_return_value(None)
request = {
"jsonrpc": "2.0",
@@ -113,7 +113,8 @@ async def test_get_unlocked_achievements_error(exception, code, message, plugin,
"game_id": "14",
"error": {
"code": code,
"message": message
"message": message,
"data": {"internal_type" : internal_type}
}
}
},
@@ -145,7 +146,8 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
"id": "3",
"error": {
"code": 4,
"message": "Backend error"
"message": "Backend error",
"data": {"internal_type": "BackendError"}
}
}
]
@@ -192,7 +194,8 @@ async def test_import_in_progress(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in messages

View File

@@ -43,19 +43,19 @@ async def test_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("error,code,message", [
pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"),
pytest.param(BackendNotAvailable, 2, "Backend not available", id="backend_not_available"),
pytest.param(BackendTimeout, 3, "Backend timed out", id="backend_timeout"),
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
pytest.param(InvalidCredentials, 100, "Invalid credentials", id="invalid_credentials"),
pytest.param(NetworkError, 101, "Network error", id="network_error"),
pytest.param(ProtocolError, 103, "Protocol error", id="protocol_error"),
pytest.param(TemporaryBlocked, 104, "Temporary blocked", id="temporary_blocked"),
pytest.param(Banned, 105, "Banned", id="banned"),
pytest.param(AccessDenied, 106, "Access denied", id="access_denied"),
@pytest.mark.parametrize("error,code,message, internal_type", [
pytest.param(UnknownError, 0, "Unknown error", "UnknownError"),
pytest.param(BackendNotAvailable, 2, "Backend not available", "BackendNotAvailable"),
pytest.param(BackendTimeout, 3, "Backend timed out", "BackendTimeout"),
pytest.param(BackendError, 4, "Backend error", "BackendError"),
pytest.param(InvalidCredentials, 100, "Invalid credentials", "InvalidCredentials"),
pytest.param(NetworkError, 101, "Network error", "NetworkError"),
pytest.param(ProtocolError, 103, "Protocol error", "ProtocolError"),
pytest.param(TemporaryBlocked, 104, "Temporary blocked", "TemporaryBlocked"),
pytest.param(Banned, 105, "Banned", "Banned"),
pytest.param(AccessDenied, 106, "Access denied", "AccessDenied"),
])
async def test_failure(plugin, read, write, error, code, message):
async def test_failure(plugin, read, write, error, code, message, internal_type):
request = {
"jsonrpc": "2.0",
"id": "3",
@@ -73,7 +73,8 @@ async def test_failure(plugin, read, write, error, code, message):
"id": "3",
"error": {
"code": code,
"message": message
"message": message,
"data" : {"internal_type" : internal_type}
}
}
]

View File

@@ -3,6 +3,50 @@ import galaxy.api.errors as errors
import galaxy.api.jsonrpc as jsonrpc
@pytest.mark.parametrize("data", [
{"key1": "value", "key2": "value2"},
{},
{"key1": ["list", "of", "things"], "key2": None},
{"key1": ("tuple", Exception)},
])
def test_valid_error_data(data):
test_message = "Test error message"
test_code = 1
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message, data=data)
data.update({"internal_type": "JsonRpcError"})
expected_json = {"code": 1, "data": data, "message": "Test error message"}
assert err_obj.json() == expected_json
def test_error_default_data():
test_message = "Test error message"
test_code = 1
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message)
expected_json = {"code": test_code, "data": {"internal_type": "JsonRpcError"}, "message": test_message}
assert err_obj.json() == expected_json
@pytest.mark.parametrize("data", [
123,
["not", "a", "mapping"],
"nor is this"
])
def test_invalid_error_data(data):
test_message = "Test error message"
test_code = 1
with pytest.raises(TypeError):
jsonrpc.JsonRpcError(code=test_code, message=test_message, data=data)
def test_error_override_internal_type():
test_message = "Test error message"
test_code = 1
test_data = {"internal_type": "SomeUserProvidedType", "details": "some more data"}
err_obj = jsonrpc.JsonRpcError(code=test_code, message=test_message, data=test_data)
expected_json = {"code": test_code, "data": {"details": "some more data", "internal_type": "JsonRpcError"}, "message": test_message}
assert err_obj.json() == expected_json
@pytest.mark.parametrize("error, expected_error_msg", [
(errors.AuthenticationRequired, "Authentication required"),
(errors.BackendNotAvailable, "Backend not available"),

View File

@@ -61,6 +61,7 @@ async def test_get_friends_failure(plugin, read, write):
"error": {
"code": 0,
"message": "Unknown error",
"data": {"internal_type": "UnknownError"}
}
}
]

View File

@@ -79,11 +79,11 @@ async def test_get_library_settings_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_game_library_settings_error(exception, code, message, plugin, read, write):
async def test_get_game_library_settings_error(exception, code, message, internal_type, plugin, read, write):
plugin.prepare_game_library_settings_context.return_value = async_return_value(None)
request = {
"jsonrpc": "2.0",
@@ -112,7 +112,8 @@ async def test_get_game_library_settings_error(exception, code, message, plugin,
"game_id": "6",
"error": {
"code": code,
"message": message
"message": message,
"data": {"internal_type": internal_type}
}
}
},
@@ -144,7 +145,8 @@ async def test_prepare_get_game_library_settings_context_error(plugin, read, wri
"id": "3",
"error": {
"code": 4,
"message": "Backend error"
"message": "Backend error",
"data": {"internal_type": "BackendError"}
}
}
]
@@ -190,7 +192,8 @@ async def test_import_in_progress(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in messages

View File

@@ -79,11 +79,11 @@ async def test_get_game_time_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message, internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_game_time_error(exception, code, message, plugin, read, write):
async def test_get_game_time_error(exception, code, message, internal_type, plugin, read, write):
plugin.prepare_game_times_context.return_value = async_return_value(None)
request = {
"jsonrpc": "2.0",
@@ -112,7 +112,8 @@ async def test_get_game_time_error(exception, code, message, plugin, read, write
"game_id": "6",
"error": {
"code": code,
"message": message
"message": message,
"data" : {"internal_type" : internal_type}
}
}
},
@@ -144,7 +145,8 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
"id": "3",
"error": {
"code": 4,
"message": "Backend error"
"message": "Backend error",
"data": {"internal_type": "BackendError"}
}
}
]
@@ -190,7 +192,8 @@ async def test_import_in_progress(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in messages

View File

@@ -51,13 +51,13 @@ async def test_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"error,code,message",
"error,code,message, internal_type",
[
pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"),
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing")
pytest.param(UnknownError, 0, "Unknown error", "UnknownError", id="unknown_error"),
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", "FailedParsingManifest", id="failed_parsing")
],
)
async def test_failure(plugin, read, write, error, code, message):
async def test_failure(plugin, read, write, error, code, message, internal_type):
request = {
"jsonrpc": "2.0",
"id": "3",
@@ -74,7 +74,8 @@ async def test_failure(plugin, read, write, error, code, message):
"id": "3",
"error": {
"code": code,
"message": message
"message": message,
"data" : {"internal_type" : internal_type}
}
}
]

View File

@@ -69,11 +69,11 @@ async def test_get_local_size_success(plugin, read, write):
]
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(FailedParsingManifest, 200, "Failed parsing manifest"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(FailedParsingManifest, 200, "Failed parsing manifest", "FailedParsingManifest"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_local_size_error(exception, code, message, plugin, read, write):
async def test_get_local_size_error(exception, code, message, internal_type, plugin, read, write):
game_id = "6"
request_id = "55"
plugin.prepare_local_size_context.return_value = async_return_value(None)
@@ -105,7 +105,10 @@ async def test_get_local_size_error(exception, code, message, plugin, read, writ
"game_id": game_id,
"error": {
"code": code,
"message": message
"message": message,
"data": {
"internal_type": internal_type
}
}
}
},
@@ -120,7 +123,7 @@ async def test_get_local_size_error(exception, code, message, plugin, read, writ
@pytest.mark.asyncio
async def test_prepare_get_local_size_context_error(plugin, read, write):
request_id = "31415"
error_details = "Unexpected syntax"
error_details = {"Details": "Unexpected syntax"}
error_message, error_code = FailedParsingManifest().message, FailedParsingManifest().code
plugin.prepare_local_size_context.side_effect = FailedParsingManifest(data=error_details)
request = {
@@ -131,7 +134,6 @@ async def test_prepare_get_local_size_context_error(plugin, read, write):
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
await plugin.run()
assert get_messages(write) == [
{
"jsonrpc": "2.0",
@@ -139,7 +141,10 @@ async def test_prepare_get_local_size_context_error(plugin, read, write):
"error": {
"code": error_code,
"message": error_message,
"data": error_details
"data": {
"internal_type": "FailedParsingManifest",
"Details": "Unexpected syntax"
}
}
}
]
@@ -186,6 +191,7 @@ async def test_import_already_in_progress_error(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in responses

View File

@@ -71,11 +71,11 @@ async def test_get_os_compatibility_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_os_compatibility_error(exception, code, message, plugin, read, write):
async def test_get_os_compatibility_error(exception, code, message, internal_type, plugin, read, write):
game_id = "6"
request_id = "55"
plugin.prepare_os_compatibility_context.return_value = async_return_value(None)
@@ -104,7 +104,8 @@ async def test_get_os_compatibility_error(exception, code, message, plugin, read
"game_id": game_id,
"error": {
"code": code,
"message": message
"message": message,
"data": {"internal_type": internal_type}
}
}
},
@@ -135,7 +136,8 @@ async def test_prepare_get_os_compatibility_context_error(plugin, read, write):
"id": request_id,
"error": {
"code": 4,
"message": "Backend error"
"message": "Backend error",
"data": {"internal_type": "BackendError"}
}
}
]
@@ -181,7 +183,8 @@ async def test_import_already_in_progress_error(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in responses

View File

@@ -90,7 +90,8 @@ async def test_failure(plugin, read, write):
"id": "3",
"error": {
"code": 0,
"message": "Unknown error"
"message": "Unknown error",
"data": {"internal_type": "UnknownError"}
}
}
]

View File

@@ -7,6 +7,8 @@ from galaxy.api.errors import (
BackendNotAvailable, BackendTimeout, BackendError, InvalidCredentials, NetworkError, AccessDenied, UnknownError
)
from galaxy.api.jsonrpc import JsonRpcError
@pytest.mark.asyncio
async def test_refresh_credentials_success(plugin, read, write):
@@ -58,7 +60,8 @@ async def test_refresh_credentials_failure(exception, plugin, read, write):
with pytest.raises(JsonRpcError) as e:
await plugin.refresh_credentials({}, False)
assert error == e.value
# Go back to comparing error == e.value, after fixing current always raising JsonRpcError when handling a response with an error
assert error.code == e.value.code
assert get_messages(write) == [
{
"jsonrpc": "2.0",

View File

@@ -53,13 +53,13 @@ async def test_get_subscriptions_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize(
"error,code,message",
"error,code,message,internal_type",
[
pytest.param(UnknownError, 0, "Unknown error", id="unknown_error"),
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing")
pytest.param(UnknownError, 0, "Unknown error", "UnknownError", id="unknown_error"),
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", "FailedParsingManifest", id="failed_parsing")
],
)
async def test_get_subscriptions_failure_generic(plugin, read, write, error, code, message):
async def test_get_subscriptions_failure_generic(plugin, read, write, error, code, message, internal_type):
request = {
"jsonrpc": "2.0",
"id": "3",
@@ -76,6 +76,7 @@ async def test_get_subscriptions_failure_generic(plugin, read, write, error, cod
"id": "3",
"error": {
"code": code,
"data": {"internal_type": internal_type},
"message": message
}
}
@@ -212,11 +213,11 @@ async def test_get_subscription_games_success_empty(plugin, read, write):
]
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_subscription_games_error(exception, code, message, plugin, read, write):
async def test_get_subscription_games_error(exception, code, message, internal_type, plugin, read, write):
plugin.prepare_subscription_games_context.return_value = async_return_value(None)
request = {
"jsonrpc": "2.0",
@@ -246,7 +247,8 @@ async def test_get_subscription_games_error(exception, code, message, plugin, re
"subscription_name": "sub_a",
"error": {
"code": code,
"message": message
"message": message,
"data": {"internal_type": internal_type}
}
}
},
@@ -269,7 +271,7 @@ async def test_get_subscription_games_error(exception, code, message, plugin, re
@pytest.mark.asyncio
async def test_prepare_get_subscription_games_context_error(plugin, read, write):
request_id = "31415"
error_details = "Unexpected backend error"
error_details = {"Details": "Unexpected backend error"}
error_message, error_code = BackendError().message, BackendError().code
plugin.prepare_subscription_games_context.side_effect = BackendError(data=error_details)
request = {
@@ -280,7 +282,6 @@ async def test_prepare_get_subscription_games_context_error(plugin, read, write)
}
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
await plugin.run()
assert get_messages(write) == [
{
"jsonrpc": "2.0",
@@ -288,7 +289,10 @@ async def test_prepare_get_subscription_games_context_error(plugin, read, write)
"error": {
"code": error_code,
"message": error_message,
"data": error_details
"data": {
"internal_type": "BackendError",
"Details": "Unexpected backend error"
}
}
}
]
@@ -334,7 +338,8 @@ async def test_import_already_in_progress_error(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in responses

View File

@@ -139,11 +139,11 @@ async def test_get_user_presence_success(plugin, read, write):
@pytest.mark.asyncio
@pytest.mark.parametrize("exception,code,message", [
(BackendError, 4, "Backend error"),
(KeyError, 0, "Unknown error")
@pytest.mark.parametrize("exception,code,message,internal_type", [
(BackendError, 4, "Backend error", "BackendError"),
(KeyError, 0, "Unknown error", "UnknownError")
])
async def test_get_user_presence_error(exception, code, message, plugin, read, write):
async def test_get_user_presence_error(exception, code, message, internal_type, plugin, read, write):
user_id = "69"
request_id = "55"
plugin.prepare_user_presence_context.return_value = async_return_value(None)
@@ -172,7 +172,10 @@ async def test_get_user_presence_error(exception, code, message, plugin, read, w
"user_id": user_id,
"error": {
"code": code,
"message": message
"message": message,
"data": {
"internal_type": internal_type
}
}
}
},
@@ -203,7 +206,10 @@ async def test_prepare_get_user_presence_context_error(plugin, read, write):
"id": request_id,
"error": {
"code": 4,
"message": "Backend error"
"message": "Backend error",
"data": {
"internal_type": "BackendError"
}
}
}
]
@@ -249,7 +255,8 @@ async def test_import_already_in_progress_error(plugin, read, write):
"id": "4",
"error": {
"code": 600,
"message": "Import already in progress"
"message": "Import already in progress",
"data": {"internal_type": "ImportInProgress"}
}
} in responses