mirror of
https://github.com/gogcom/galaxy-integrations-python-api.git
synced 2026-01-29 09:01:22 -05:00
Compare commits
14 Commits
dependabot
...
0.71
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e483edd92 | ||
|
|
8a39496a11 | ||
|
|
2c18ac4ffa | ||
|
|
6a1d2763c3 | ||
|
|
f4ded58c28 | ||
|
|
d2f34349b8 | ||
|
|
f062387ddb | ||
|
|
5a699100d6 | ||
|
|
5daa386f6e | ||
|
|
761598de54 | ||
|
|
ab44e137c3 | ||
|
|
948bfcd971 | ||
|
|
6196e751c6 | ||
|
|
a5b2a0890e |
20
.github/workflows/ci.yml
vendored
Normal file
20
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.13
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements-dev.txt
|
||||
- name: Run tests
|
||||
run: |
|
||||
pytest
|
||||
@@ -1,4 +1,4 @@
|
||||
image: registry-gitlab.gog.com/docker/python:3.7.3
|
||||
image: registry-gitlab.gog.com/docker/python:3.13
|
||||
|
||||
stages:
|
||||
- test
|
||||
@@ -18,10 +18,10 @@ deploy_package:
|
||||
TWINE_USERNAME: $PYPI_USERNAME
|
||||
TWINE_PASSWORD: $PYPI_PASSWORD
|
||||
script:
|
||||
- pip install twine wheel
|
||||
- pip install twine wheel build
|
||||
- rm -rf dist
|
||||
- export VERSION=$(python setup.py --version)
|
||||
- python setup.py sdist --formats=gztar bdist_wheel
|
||||
- python -m build --sdist --wheel
|
||||
- twine upload dist/*
|
||||
- curl -X POST --silent --show-error --fail
|
||||
"https://gitlab.gog.com/api/v4/projects/${CI_PROJECT_ID}/repository/tags?tag_name=${VERSION}&ref=${CI_COMMIT_REF_NAME}&private_token=${PACKAGE_DEPLOYER_API_TOKEN}"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
dist: xenial # required for Python >= 3.7
|
||||
language: python
|
||||
python:
|
||||
- "3.7"
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
script:
|
||||
- pytest
|
||||
32
README.md
32
README.md
@@ -1,17 +1,19 @@
|
||||
# GOG Galaxy Integrations Python API
|
||||
# GOG GALAXY Integrations Python API
|
||||
|
||||
This Python library allows developers to easily build community integrations for various gaming platforms with GOG Galaxy 2.0.
|
||||
This Python library allows developers to easily build community integrations for various gaming platforms with GOG GALAXY **2.1**.
|
||||
|
||||
- refer to our <a href='https://galaxy-integrations-python-api.readthedocs.io'>documentation</a>
|
||||
|
||||
Note: For integrations targeting GOG GALAXY the **below 2.1.0 version**, please refer to [this version](https://github.com/gogcom/galaxy-integrations-python-api/tree/0.69).
|
||||
|
||||
## Features
|
||||
|
||||
Each integration in GOG Galaxy 2.0 comes as a separate Python script and is launched as a separate process that needs to communicate with the main instance of GOG Galaxy 2.0.
|
||||
Each integration in GOG GALAXY 2.1 comes as a separate Python script and is launched as a separate process that needs to communicate with the main instance of GOG GALAXY 2.1.
|
||||
|
||||
The provided features are:
|
||||
|
||||
- multistep authorization using a browser built into GOG Galaxy 2.0
|
||||
- support for GOG Galaxy 2.0 features:
|
||||
- multistep authorization using a browser built into GOG GALAXY 2.1
|
||||
- support for GOG GALAXY 2.1 features:
|
||||
- importing owned and detecting installed games
|
||||
- installing and launching games
|
||||
- importing achievements and game time
|
||||
@@ -28,9 +30,11 @@ Each integration can implement only one platform. Each integration must declare
|
||||
|
||||
## Basic usage
|
||||
|
||||
Each integration should inherit from the :class:`~galaxy.api.plugin.Plugin` class. Supported methods like :meth:`~galaxy.api.plugin.Plugin.get_owned_games` should be overwritten - they are called from the GOG Galaxy client at the appropriate times.
|
||||
Each of those methods can raise exceptions inherited from the :exc:`~galaxy.api.jsonrpc.ApplicationError`.
|
||||
Communication between an integration and the client is also possible with the use of notifications, for example: :meth:`~galaxy.api.plugin.Plugin.update_local_game_status`.
|
||||
Each integration should inherit from the `galaxy.api.plugin.Plugin` class. Supported methods like `galaxy.api.plugin.Plugin.get_owned_games` should be overwritten - they are called from the GOG GALAXY client at the appropriate times.
|
||||
Each of those methods can raise exceptions inherited from the `galaxy.api.jsonrpc.ApplicationError`.
|
||||
Communication between an integration and the client is also possible with the use of notifications, for example: `galaxy.api.plugin.Plugin.update_local_game_status`.
|
||||
|
||||
The minimum implementation requires to override `galaxy.api.plugin.Plugin.authenticate` and `galaxy.api.plugin.Plugin.get_owned_games` methods.
|
||||
|
||||
```python
|
||||
import sys
|
||||
@@ -72,8 +76,8 @@ if __name__ == "__main__":
|
||||
|
||||
## Deployment
|
||||
|
||||
The client has a built-in Python 3.7 interpreter, so integrations are delivered as Python modules.
|
||||
In order to be found by GOG Galaxy 2.0 an integration folder should be placed in [lookup directory](#deploy-location). Beside all the Python files, the integration folder must contain [manifest.json](#deploy-manifest) and all third-party dependencies. See an [exemplary structure](#deploy-structure-example).
|
||||
The client has a built-in Python 3.13 interpreter, so integrations are delivered as Python modules.
|
||||
In order to be found by GOG GALAXY 2.1 an integration folder should be placed in [lookup directory](#deploy-location). Beside all the Python files, the integration folder must contain [manifest.json](#deploy-manifest) and all third-party dependencies. See an [exemplary structure](#deploy-structure-example).
|
||||
|
||||
### Lookup directory
|
||||
|
||||
@@ -88,7 +92,7 @@ In order to be found by GOG Galaxy 2.0 an integration folder should be placed in
|
||||
`~/Library/Application Support/GOG.com/Galaxy/plugins/installed`
|
||||
|
||||
### Logging
|
||||
<a href='https://docs.python.org/3.7/howto/logging.html'>Root logger</a> is already setup by GOG Galaxy to store rotated log files in:
|
||||
<a href='https://docs.python.org/3.13/howto/logging.html'>Root logger</a> is already setup by GOG GALAXY to store rotated log files in:
|
||||
|
||||
- Windows:
|
||||
|
||||
@@ -128,9 +132,9 @@ Obligatory JSON file to be placed in an integration folder.
|
||||
|
||||
### Dependencies
|
||||
|
||||
All third-party packages (packages not included in the Python 3.7 standard library) should be deployed along with plugin files. Use the following command structure:
|
||||
All third-party packages (packages not included in the Python 3.13 standard library) should be deployed along with plugin files. Use the following command structure:
|
||||
|
||||
```pip install DEP --target DIR --implementation cp --python-version 37```
|
||||
```pip install DEP --target DIR --implementation cp --python-version 313```
|
||||
|
||||
For example, a plugin that uses *requests* could have the following structure:
|
||||
|
||||
@@ -149,4 +153,4 @@ installed
|
||||
|
||||
## Legal Notice
|
||||
|
||||
By integrating or attempting to integrate any applications or content with or into GOG Galaxy 2.0 you represent that such application or content is your original creation (other than any software made available by GOG) and/or that you have all necessary rights to grant such applicable rights to the relevant community integration to GOG and to GOG Galaxy 2.0 end users for the purpose of use of such community integration and that such community integration comply with any third party license and other requirements including compliance with applicable laws.
|
||||
By integrating or attempting to integrate any applications or content with or into GOG GALAXY 2.1 you represent that such application or content is your original creation (other than any software made available by GOG) and/or that you have all necessary rights to grant such applicable rights to the relevant community integration to GOG and to GOG GALAXY 2.1 end users for the purpose of use of such community integration and that such community integration comply with any third party license and other requirements including compliance with applicable laws.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Sphinx==2.0.1
|
||||
sphinx-rtd-theme==0.4.3
|
||||
sphinx-autodoc-typehints==1.6.0
|
||||
sphinxcontrib-asyncio==0.2.0
|
||||
m2r==0.2.1
|
||||
Sphinx==4.2.0
|
||||
sphinx-rtd-theme==1.0.0
|
||||
sphinx-autodoc-typehints==1.12.0
|
||||
sphinxcontrib-asyncio==0.3.0
|
||||
m2r2==0.3.1
|
||||
typing-extensions==3.10.0.2
|
||||
@@ -34,7 +34,7 @@ extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinxcontrib.asyncio',
|
||||
'sphinx_autodoc_typehints',
|
||||
'm2r' # mdinclude directive for makrdown files
|
||||
'm2r2' # mdinclude directive for makrdown files
|
||||
]
|
||||
autodoc_member_order = 'bysource'
|
||||
autodoc_inherit_docstrings = False
|
||||
@@ -70,6 +70,6 @@ html_theme_options = {
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
# html_static_path = ["_static"]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
@@ -7,7 +7,7 @@ plugin
|
||||
.. automodule:: galaxy.api.plugin
|
||||
:members:
|
||||
:undoc-members:
|
||||
:exclude-members: JSONEncoder, features, achievements_import_finished, game_times_import_finished, start_achievements_import, start_game_times_import, get_game_times, get_unlocked_achievements
|
||||
:exclude-members: JSONEncoder, features
|
||||
|
||||
types
|
||||
-----------------------
|
||||
|
||||
69
pyproject.toml
Normal file
69
pyproject.toml
Normal file
@@ -0,0 +1,69 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "galaxy.plugin.api"
|
||||
version = "0.71"
|
||||
description = "GOG Galaxy Integrations Python API"
|
||||
authors = [
|
||||
{name = "Galaxy team", email = "galaxy@gog.com"}
|
||||
]
|
||||
readme = "README.md"
|
||||
license-files = ["LICENSE"]
|
||||
requires-python = "~=3.13.0"
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dependencies = [
|
||||
"aiohttp>=3.12.15",
|
||||
"certifi>=2026.1.4",
|
||||
"psutil>=5.6.6; sys_platform == 'darwin'"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest==8.4.1",
|
||||
"pytest-asyncio==1.1.0",
|
||||
"pytest-mock==3.14.1",
|
||||
"pytest-mypy==1.0.1",
|
||||
"pytest-flakes==4.0.5",
|
||||
"types-certifi==2021.10.8.3",
|
||||
"setuptools==80.9.0",
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
package-dir = {"" = "src"}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
python_classes = ["Test*"]
|
||||
python_functions = ["test_*"]
|
||||
addopts = [
|
||||
"--strict-markers",
|
||||
"--strict-config",
|
||||
"--verbose",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.13"
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
disallow_untyped_defs = true
|
||||
disallow_incomplete_defs = true
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
no_implicit_optional = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_no_return = true
|
||||
warn_unreachable = true
|
||||
strict_equality = true
|
||||
@@ -1,7 +1,8 @@
|
||||
-r requirements.txt
|
||||
pytest==5.2.2
|
||||
pytest-asyncio==0.10.0
|
||||
pytest-mock==1.10.3
|
||||
pytest-mypy==0.4.1
|
||||
pytest-flakes==4.0.0
|
||||
types-certifi==2020.4.0
|
||||
pytest==8.4.1
|
||||
pytest-asyncio==1.1.0
|
||||
pytest-mock==3.14.1
|
||||
pytest-mypy==1.0.1
|
||||
pytest-flakes==4.0.5
|
||||
types-certifi==2021.10.8.3
|
||||
setuptools==80.9.0
|
||||
@@ -1,7 +1,7 @@
|
||||
-e .
|
||||
# Copied from setup.py because of a pip bug
|
||||
# see https://github.com/pypa/pip/issues/4780
|
||||
aiohttp==3.7.4
|
||||
certifi==2019.3.9
|
||||
aiohttp==3.12.15
|
||||
certifi==2026.1.4
|
||||
psutil==5.6.6; sys_platform == 'darwin'
|
||||
# End of copy from setup.py
|
||||
|
||||
17
setup.py
17
setup.py
@@ -1,16 +1,3 @@
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name="galaxy.plugin.api",
|
||||
version="0.68",
|
||||
description="GOG Galaxy Integrations Python API",
|
||||
author='Galaxy team',
|
||||
author_email='galaxy@gog.com',
|
||||
packages=find_packages("src"),
|
||||
package_dir={'': 'src'},
|
||||
install_requires=[
|
||||
"aiohttp>=3.5.4",
|
||||
"certifi>=2019.3.9",
|
||||
"psutil>=5.6.6; sys_platform == 'darwin'"
|
||||
]
|
||||
)
|
||||
setup()
|
||||
|
||||
@@ -6,7 +6,7 @@ sphinx:
|
||||
formats: all
|
||||
|
||||
python:
|
||||
version: 3.7
|
||||
version: 3.13
|
||||
install:
|
||||
- requirements: requirements.txt
|
||||
- requirements: docs/requirements.txt
|
||||
|
||||
@@ -1,71 +1,88 @@
|
||||
from galaxy.api.jsonrpc import ApplicationError, UnknownError
|
||||
|
||||
assert UnknownError
|
||||
|
||||
assert UnknownError is not None # UnknownError not used directly in errors.py, but we want to ensure it's defined
|
||||
|
||||
class AuthenticationRequired(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(1, "Authentication required", data)
|
||||
def __init__(self, message="Authentication required", data=None):
|
||||
super().__init__(1, message, data)
|
||||
|
||||
|
||||
class BackendNotAvailable(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(2, "Backend not available", data)
|
||||
def __init__(self, message="Backend not available", data=None):
|
||||
super().__init__(2, message, data)
|
||||
|
||||
|
||||
class BackendTimeout(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(3, "Backend timed out", data)
|
||||
def __init__(self, message="Backend timed out", data=None):
|
||||
super().__init__(3, message, data)
|
||||
|
||||
|
||||
class BackendError(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(4, "Backend error", data)
|
||||
def __init__(self, message="Backend error", data=None):
|
||||
super().__init__(4, message, data)
|
||||
|
||||
class UnknownBackendResponse(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(4, "Backend responded in unknown way", data)
|
||||
|
||||
class TooManyRequests(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(5, "Too many requests. Try again later", data)
|
||||
def __init__(self, message="Too many requests. Try again later", data=None):
|
||||
super().__init__(5, message, data)
|
||||
|
||||
|
||||
class UnknownBackendResponse(ApplicationError):
|
||||
def __init__(self, message="Backend responded in unknown way", data=None):
|
||||
super().__init__(6, message, data)
|
||||
|
||||
|
||||
class InvalidCredentials(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(100, "Invalid credentials", data)
|
||||
def __init__(self, message="Invalid credentials", data=None):
|
||||
super().__init__(100, message, data)
|
||||
|
||||
|
||||
class NetworkError(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(101, "Network error", data)
|
||||
def __init__(self, message="Network error", data=None):
|
||||
super().__init__(101, message, data)
|
||||
|
||||
|
||||
class ProtocolError(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(103, "Protocol error", data)
|
||||
def __init__(self, message="Protocol error", data=None):
|
||||
super().__init__(103, message, data)
|
||||
|
||||
|
||||
class TemporaryBlocked(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(104, "Temporary blocked", data)
|
||||
def __init__(self, message="Temporary blocked", data=None):
|
||||
super().__init__(104, message, data)
|
||||
|
||||
|
||||
class Banned(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(105, "Banned", data)
|
||||
def __init__(self, message="Banned", data=None):
|
||||
super().__init__(105, message, data)
|
||||
|
||||
|
||||
class AccessDenied(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(106, "Access denied", data)
|
||||
def __init__(self, message="Access denied", data=None):
|
||||
super().__init__(106, message, data)
|
||||
|
||||
|
||||
class FailedParsingManifest(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(200, "Failed parsing manifest", data)
|
||||
def __init__(self, message="Failed parsing manifest", data=None):
|
||||
super().__init__(200, message, data)
|
||||
|
||||
|
||||
class TooManyMessagesSent(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(300, "Too many messages sent", data)
|
||||
def __init__(self, message="Too many messages sent", data=None):
|
||||
super().__init__(300, message, data)
|
||||
|
||||
|
||||
class IncoherentLastMessage(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(400, "Different last message id on backend", data)
|
||||
def __init__(self, message="Different last message id on backend", data=None):
|
||||
super().__init__(400, message, data)
|
||||
|
||||
|
||||
class MessageNotFound(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(500, "Message not found", data)
|
||||
def __init__(self, message="Message not found", data=None):
|
||||
super().__init__(500, message, data)
|
||||
|
||||
|
||||
class ImportInProgress(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(600, "Import already in progress", data)
|
||||
def __init__(self, message="Import already in progress", data=None):
|
||||
super().__init__(600, message, data)
|
||||
|
||||
@@ -61,7 +61,7 @@ class Importer:
|
||||
context = await self._prepare_context(ids)
|
||||
self._task_manager.create_task(
|
||||
self._import_elements(ids, context),
|
||||
"{} import".format(self._name),
|
||||
f"{self._name} import",
|
||||
handle_exceptions=False
|
||||
)
|
||||
except:
|
||||
@@ -76,7 +76,7 @@ class CollectionImporter(Importer):
|
||||
|
||||
async def _import_element(self, id_, context_):
|
||||
try:
|
||||
async for element in self._get(id_, context_):
|
||||
async for element in await self._get(id_, context_):
|
||||
self._notification_success(id_, element)
|
||||
except ApplicationError as error:
|
||||
self._notification_failure(id_, error)
|
||||
|
||||
@@ -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
|
||||
@@ -15,8 +15,13 @@ logger = logging.getLogger(__name__)
|
||||
class JsonRpcError(Exception):
|
||||
def __init__(self, code, message, data=None):
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.data = data
|
||||
self.message = str(message)
|
||||
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,37 +30,42 @@ 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):
|
||||
super().__init__(-32700, "Parse error")
|
||||
def __init__(self, message="Parse error", data=None):
|
||||
super().__init__(-32700, message, data)
|
||||
|
||||
|
||||
class InvalidRequest(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32600, "Invalid Request")
|
||||
def __init__(self, message="Invalid Request", data=None):
|
||||
super().__init__(-32600, message, data)
|
||||
|
||||
|
||||
class MethodNotFound(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32601, "Method not found")
|
||||
def __init__(self, message="Method not found", data=None):
|
||||
super().__init__(-32601, message, data)
|
||||
|
||||
|
||||
class InvalidParams(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32602, "Invalid params")
|
||||
def __init__(self, message="Invalid params", data=None):
|
||||
super().__init__(-32602, message, data)
|
||||
|
||||
|
||||
class Timeout(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32000, "Method timed out")
|
||||
def __init__(self, message="Method timed out", data=None):
|
||||
super().__init__(-32000, message, data)
|
||||
|
||||
|
||||
class Aborted(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32001, "Method aborted")
|
||||
def __init__(self, message="Method aborted", data=None):
|
||||
super().__init__(-32001, message, data)
|
||||
|
||||
|
||||
class ApplicationError(JsonRpcError):
|
||||
def __init__(self, code, message, data):
|
||||
@@ -63,9 +73,11 @@ class ApplicationError(JsonRpcError):
|
||||
raise ValueError("The error code in reserved range")
|
||||
super().__init__(code, message, data)
|
||||
|
||||
|
||||
class UnknownError(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(0, "Unknown error", data)
|
||||
def __init__(self, message="Unknown error", data=None):
|
||||
super().__init__(0, message, data)
|
||||
|
||||
|
||||
Request = namedtuple("Request", ["method", "params", "id"], defaults=[{}, None])
|
||||
Response = namedtuple("Response", ["id", "result", "error"], defaults=[None, {}, {}])
|
||||
@@ -285,7 +297,7 @@ class Connection():
|
||||
@staticmethod
|
||||
def _parse_message(data):
|
||||
try:
|
||||
jsonrpc_message = json.loads(data, encoding="utf-8")
|
||||
jsonrpc_message = json.loads(data)
|
||||
if jsonrpc_message.get("jsonrpc") != "2.0":
|
||||
raise InvalidRequest()
|
||||
del jsonrpc_message["jsonrpc"]
|
||||
|
||||
@@ -299,7 +299,7 @@ class Plugin:
|
||||
return self._external_task_manager.create_task(coro, description)
|
||||
|
||||
async def _pass_control(self):
|
||||
while self._active:
|
||||
while self._active and self._connection._active:
|
||||
try:
|
||||
self.tick()
|
||||
except Exception:
|
||||
@@ -1063,7 +1063,7 @@ class Plugin:
|
||||
This method will only be used if :meth:`get_subscriptions` has been implemented.
|
||||
|
||||
:param context: the value returned from :meth:`prepare_subscription_games_context`
|
||||
:return a generator object that yields SubscriptionGames
|
||||
:return: a generator object that yields SubscriptionGames
|
||||
|
||||
.. code-block:: python
|
||||
:linenos:
|
||||
@@ -1129,8 +1129,11 @@ def create_and_run_plugin(plugin_class, argv):
|
||||
async with plugin_class(reader, writer, token) as plugin:
|
||||
await plugin.run()
|
||||
finally:
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
try:
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
except (ConnectionAbortedError, ConnectionResetError):
|
||||
pass
|
||||
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
|
||||
@@ -33,7 +33,7 @@ class Cookie:
|
||||
|
||||
@dataclass
|
||||
class NextStep:
|
||||
"""Return this from :meth:`.authenticate` or :meth:`.pass_login_credentials` to open client built-in browser with given url.
|
||||
R"""Return this from :meth:`.authenticate` or :meth:`.pass_login_credentials` to open client built-in browser with given url.
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -37,3 +37,21 @@ async def async_raise(error, loop_iterations_delay=0):
|
||||
if loop_iterations_delay > 0:
|
||||
await skip_loop(loop_iterations_delay)
|
||||
raise error
|
||||
|
||||
|
||||
def delayed_return_value(return_value, loop_iterations_delay=0):
|
||||
async def return_fn(*args, **kwargs):
|
||||
for _ in range(loop_iterations_delay):
|
||||
await asyncio.sleep(0)
|
||||
return return_value
|
||||
return return_fn
|
||||
|
||||
|
||||
def delayed_return_value_iterable(return_value, loop_iterations_delay=0):
|
||||
iterable = iter(return_value)
|
||||
async def return_fn(*args, **kwargs):
|
||||
for _ in range(loop_iterations_delay):
|
||||
await asyncio.sleep(0.001)
|
||||
last_value = next(iterable)
|
||||
return last_value
|
||||
return return_fn
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from contextlib import ExitStack
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -12,12 +12,12 @@ from galaxy.unittest.mock import async_return_value
|
||||
@pytest.fixture()
|
||||
def reader():
|
||||
stream = MagicMock(name="stream_reader")
|
||||
stream.read = MagicMock()
|
||||
stream.read = AsyncMock()
|
||||
yield stream
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def writer():
|
||||
def writer():
|
||||
stream = MagicMock(name="stream_writer")
|
||||
stream.drain.side_effect = lambda: async_return_value(None)
|
||||
yield stream
|
||||
@@ -34,7 +34,7 @@ def write(writer):
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def plugin(reader, writer):
|
||||
def plugin(reader, writer):
|
||||
"""Return plugin instance with all feature methods mocked"""
|
||||
methods = (
|
||||
"handshake_complete",
|
||||
@@ -73,13 +73,15 @@ async def plugin(reader, writer):
|
||||
"subscription_games_import_complete"
|
||||
)
|
||||
|
||||
# Patch the class methods BEFORE creating the instance
|
||||
with ExitStack() as stack:
|
||||
for method in methods:
|
||||
stack.enter_context(patch.object(Plugin, method))
|
||||
|
||||
async with Plugin(Platform.Generic, "0.1", reader, writer, "token") as plugin:
|
||||
plugin.shutdown.return_value = async_return_value(None)
|
||||
yield plugin
|
||||
|
||||
# Now create the plugin instance
|
||||
plugin = Plugin(Platform.Generic, "0.1", reader, writer, "token")
|
||||
plugin.shutdown.return_value = None
|
||||
yield plugin
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
||||
@@ -5,7 +5,7 @@ from pytest import raises
|
||||
|
||||
from galaxy.api.types import Achievement
|
||||
from galaxy.api.errors import BackendError
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_initialization_no_id_nor_name():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_unlocked_achievements_success(plugin, read, write):
|
||||
plugin.prepare_achievements_context.return_value = async_return_value(5)
|
||||
plugin.prepare_achievements_context.return_value = 5
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -31,16 +31,17 @@ async def test_get_unlocked_achievements_success(plugin, read, write):
|
||||
"game_ids": ["14"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
plugin.get_unlocked_achievements.return_value = async_return_value([
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_unlocked_achievements.return_value = [
|
||||
Achievement(achievement_id="lvl10", unlock_time=1548421241),
|
||||
Achievement(achievement_name="Got level 20", unlock_time=1548422395),
|
||||
Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633)
|
||||
])
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.prepare_achievements_context.assert_called_with(["14"])
|
||||
plugin.get_unlocked_achievements.assert_called_with("14", 5)
|
||||
plugin.achievements_import_complete.asert_called_with()
|
||||
plugin.achievements_import_complete.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -79,12 +80,12 @@ 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):
|
||||
plugin.prepare_achievements_context.return_value = async_return_value(None)
|
||||
async def test_get_unlocked_achievements_error(exception, code, message, internal_type, plugin, read, write):
|
||||
plugin.prepare_achievements_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -94,11 +95,12 @@ async def test_get_unlocked_achievements_error(exception, code, message, plugin,
|
||||
}
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_unlocked_achievements.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_unlocked_achievements.assert_called()
|
||||
plugin.achievements_import_complete.asert_called_with()
|
||||
plugin.achievements_import_complete.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -113,7 +115,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}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -135,9 +138,10 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
|
||||
"game_ids": ["14"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -145,7 +149,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"}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -153,8 +158,8 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_in_progress(plugin, read, write):
|
||||
plugin.prepare_achievements_context.return_value = async_return_value(None)
|
||||
plugin.get_unlocked_achievements.return_value = async_return_value([])
|
||||
plugin.prepare_achievements_context.return_value = None
|
||||
plugin.get_unlocked_achievements.return_value = []
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -174,12 +179,13 @@ async def test_import_in_progress(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
messages = get_messages(write)
|
||||
assert {
|
||||
@@ -192,7 +198,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
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from galaxy.api.errors import (
|
||||
Banned,
|
||||
AccessDenied,
|
||||
)
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -25,9 +25,10 @@ async def test_success(plugin, read, write):
|
||||
"id": "3",
|
||||
"method": "init_authentication"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.authenticate.return_value = Authentication("132", "Zenek")
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.authenticate.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -43,28 +44,29 @@ 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",
|
||||
"method": "init_authentication"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.authenticate.side_effect = error()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.authenticate.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -73,7 +75,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}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -91,9 +94,10 @@ async def test_stored_credentials(plugin, read, write):
|
||||
}
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.authenticate.return_value = Authentication("132", "Zenek")
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
|
||||
write.assert_called()
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import json
|
||||
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_chunked_messages(plugin, read):
|
||||
@@ -16,8 +14,9 @@ async def test_chunked_messages(plugin, read):
|
||||
}
|
||||
|
||||
message = json.dumps(request).encode() + b"\n"
|
||||
read.side_effect = [async_return_value(message[:5]), async_return_value(message[5:]), async_return_value(b"")]
|
||||
read.side_effect = [message[:5], message[5:], b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.install_game.assert_called_with(game_id="3")
|
||||
|
||||
|
||||
@@ -41,8 +40,9 @@ async def test_joined_messages(plugin, read):
|
||||
]
|
||||
data = b"".join([json.dumps(request).encode() + b"\n" for request in requests])
|
||||
|
||||
read.side_effect = [async_return_value(data), async_return_value(b"")]
|
||||
read.side_effect = [data, b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.install_game.assert_called_with(game_id="3")
|
||||
plugin.launch_game.assert_called_with(game_id="3")
|
||||
|
||||
@@ -58,6 +58,7 @@ async def test_not_finished(plugin, read):
|
||||
}
|
||||
|
||||
message = json.dumps(request).encode() # no new line
|
||||
read.side_effect = [async_return_value(message), async_return_value(b"")]
|
||||
read.side_effect = [message, b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.install_game.assert_not_called()
|
||||
|
||||
147
tests/test_errors.py
Normal file
147
tests/test_errors.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import pytest
|
||||
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"),
|
||||
(errors.BackendTimeout, "Backend timed out"),
|
||||
(errors.BackendError, "Backend error"),
|
||||
(errors.UnknownBackendResponse, "Backend responded in unknown way"),
|
||||
(errors.TooManyRequests, "Too many requests. Try again later"),
|
||||
(errors.InvalidCredentials, "Invalid credentials"),
|
||||
(errors.NetworkError, "Network error"),
|
||||
(errors.ProtocolError, "Protocol error"),
|
||||
(errors.TemporaryBlocked, "Temporary blocked"),
|
||||
(errors.Banned, "Banned"),
|
||||
(errors.AccessDenied, "Access denied"),
|
||||
(errors.FailedParsingManifest, "Failed parsing manifest"),
|
||||
(errors.TooManyMessagesSent, "Too many messages sent"),
|
||||
(errors.IncoherentLastMessage, "Different last message id on backend"),
|
||||
(errors.MessageNotFound, "Message not found"),
|
||||
(errors.ImportInProgress, "Import already in progress"),
|
||||
(jsonrpc.UnknownError, "Unknown error"),
|
||||
(jsonrpc.ParseError, "Parse error"),
|
||||
(jsonrpc.InvalidRequest, "Invalid Request"),
|
||||
(jsonrpc.MethodNotFound, "Method not found"),
|
||||
(jsonrpc.InvalidParams, "Invalid params"),
|
||||
(jsonrpc.Timeout, "Method timed out"),
|
||||
(jsonrpc.Aborted, "Method aborted"),
|
||||
])
|
||||
def test_error_default_message(error, expected_error_msg):
|
||||
error_json = error().json()
|
||||
|
||||
assert error_json["message"] == expected_error_msg
|
||||
|
||||
|
||||
@pytest.mark.parametrize("error", [
|
||||
errors.AuthenticationRequired,
|
||||
errors.BackendNotAvailable,
|
||||
errors.BackendTimeout,
|
||||
errors.BackendError,
|
||||
errors.UnknownBackendResponse,
|
||||
errors.TooManyRequests,
|
||||
errors.InvalidCredentials,
|
||||
errors.NetworkError,
|
||||
errors.ProtocolError,
|
||||
errors.TemporaryBlocked,
|
||||
errors.Banned,
|
||||
errors.AccessDenied,
|
||||
errors.FailedParsingManifest,
|
||||
errors.TooManyMessagesSent,
|
||||
errors.IncoherentLastMessage,
|
||||
errors.MessageNotFound,
|
||||
errors.ImportInProgress,
|
||||
jsonrpc.UnknownError,
|
||||
jsonrpc.ParseError,
|
||||
jsonrpc.InvalidRequest,
|
||||
jsonrpc.MethodNotFound,
|
||||
jsonrpc.InvalidParams,
|
||||
jsonrpc.Timeout,
|
||||
jsonrpc.Aborted,
|
||||
])
|
||||
def test_set_error_custom_message(error):
|
||||
custom_message = "test message"
|
||||
|
||||
error_json = error(custom_message).json()
|
||||
|
||||
assert error_json["message"] == custom_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize("error", [
|
||||
errors.AuthenticationRequired,
|
||||
errors.BackendNotAvailable,
|
||||
errors.BackendTimeout,
|
||||
errors.BackendError,
|
||||
errors.UnknownBackendResponse,
|
||||
errors.TooManyRequests,
|
||||
errors.InvalidCredentials,
|
||||
errors.NetworkError,
|
||||
errors.ProtocolError,
|
||||
errors.TemporaryBlocked,
|
||||
errors.Banned,
|
||||
errors.AccessDenied,
|
||||
errors.FailedParsingManifest,
|
||||
errors.TooManyMessagesSent,
|
||||
errors.IncoherentLastMessage,
|
||||
errors.MessageNotFound,
|
||||
errors.ImportInProgress,
|
||||
jsonrpc.UnknownError,
|
||||
jsonrpc.ParseError,
|
||||
jsonrpc.InvalidRequest,
|
||||
jsonrpc.MethodNotFound,
|
||||
jsonrpc.InvalidParams,
|
||||
jsonrpc.Timeout,
|
||||
jsonrpc.Aborted,
|
||||
])
|
||||
def test_set_arbitrary_error_message(error):
|
||||
arbitrary_messages = [[], {}, (), 1, None]
|
||||
|
||||
for msg in arbitrary_messages:
|
||||
error_json = error(msg).json()
|
||||
assert error_json["message"] == str(msg)
|
||||
@@ -1,6 +1,6 @@
|
||||
from galaxy.api.types import UserInfo
|
||||
from galaxy.api.errors import UnknownError
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -15,14 +15,15 @@ async def test_get_friends_success(plugin, read, write):
|
||||
"method": "import_friends"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
plugin.get_friends.return_value = async_return_value([
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_friends.return_value = [
|
||||
UserInfo("3", "Jan", "https://avatar.url/u3", None),
|
||||
UserInfo("5", "Ola", None, "https://profile.url/u5"),
|
||||
UserInfo("6", "Ola2", None),
|
||||
UserInfo("7", "Ola3"),
|
||||
])
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_friends.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -49,9 +50,10 @@ async def test_get_friends_failure(plugin, read, write):
|
||||
"method": "import_friends"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_friends.side_effect = UnknownError()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_friends.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -61,6 +63,7 @@ async def test_get_friends_failure(plugin, read, write):
|
||||
"error": {
|
||||
"code": 0,
|
||||
"message": "Unknown error",
|
||||
"data": {"internal_type": "UnknownError"}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -72,6 +75,7 @@ async def test_add_friend(plugin, write):
|
||||
|
||||
plugin.add_friend(friend)
|
||||
await skip_loop()
|
||||
await plugin.wait_closed()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -93,7 +97,7 @@ async def test_add_friend(plugin, write):
|
||||
async def test_remove_friend(plugin, write):
|
||||
plugin.remove_friend("5")
|
||||
await skip_loop()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -111,7 +115,7 @@ async def test_update_friend_info(plugin, write):
|
||||
UserInfo("7", "Jakub", avatar_url="https://new-avatar.url/kuba2.jpg", profile_url="https://profile.url/kuba")
|
||||
)
|
||||
await skip_loop()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
|
||||
@@ -3,14 +3,13 @@ from unittest.mock import call
|
||||
import pytest
|
||||
from galaxy.api.types import GameLibrarySettings
|
||||
from galaxy.api.errors import BackendError
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_library_settings_success(plugin, read, write):
|
||||
plugin.prepare_game_library_settings_context.return_value = async_return_value("abc")
|
||||
plugin.prepare_game_library_settings_context.return_value = "abc"
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -19,13 +18,14 @@ async def test_get_library_settings_success(plugin, read, write):
|
||||
"game_ids": ["3", "5", "7"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_game_library_settings.side_effect = [
|
||||
async_return_value(GameLibrarySettings("3", None, True)),
|
||||
async_return_value(GameLibrarySettings("5", [], False)),
|
||||
async_return_value(GameLibrarySettings("7", ["tag1", "tag2", "tag3"], None)),
|
||||
GameLibrarySettings("3", None, True),
|
||||
GameLibrarySettings("5", [], False),
|
||||
GameLibrarySettings("7", ["tag1", "tag2", "tag3"], None),
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_game_library_settings.assert_has_calls([
|
||||
call("3", "abc"),
|
||||
call("5", "abc"),
|
||||
@@ -79,12 +79,12 @@ 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):
|
||||
plugin.prepare_game_library_settings_context.return_value = async_return_value(None)
|
||||
async def test_get_game_library_settings_error(exception, code, message, internal_type, plugin, read, write):
|
||||
plugin.prepare_game_library_settings_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -93,9 +93,10 @@ async def test_get_game_library_settings_error(exception, code, message, plugin,
|
||||
"game_ids": ["6"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_game_library_settings.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_game_library_settings.assert_called()
|
||||
plugin.game_library_settings_import_complete.assert_called_once_with()
|
||||
|
||||
@@ -112,7 +113,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}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -135,8 +137,9 @@ async def test_prepare_get_game_library_settings_context_error(plugin, read, wri
|
||||
"game_ids": ["6"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -144,7 +147,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"}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -152,7 +156,7 @@ async def test_prepare_get_game_library_settings_context_error(plugin, read, wri
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_in_progress(plugin, read, write):
|
||||
plugin.prepare_game_library_settings_context.return_value = async_return_value(None)
|
||||
plugin.prepare_game_library_settings_context.return_value = None
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -172,12 +176,13 @@ async def test_import_in_progress(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
messages = get_messages(write)
|
||||
assert {
|
||||
@@ -190,7 +195,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
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ from unittest.mock import call
|
||||
import pytest
|
||||
from galaxy.api.types import GameTime
|
||||
from galaxy.api.errors import BackendError
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_game_time_success(plugin, read, write):
|
||||
plugin.prepare_game_times_context.return_value = async_return_value("abc")
|
||||
plugin.prepare_game_times_context.return_value = "abc"
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -19,13 +19,14 @@ async def test_get_game_time_success(plugin, read, write):
|
||||
"game_ids": ["3", "5", "7"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_game_time.side_effect = [
|
||||
async_return_value(GameTime("3", 60, 1549550504)),
|
||||
async_return_value(GameTime("5", 10, None)),
|
||||
async_return_value(GameTime("7", None, 1549550502)),
|
||||
GameTime("3", 60, 1549550504),
|
||||
GameTime("5", 10, None),
|
||||
GameTime("7", None, 1549550502),
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_game_time.assert_has_calls([
|
||||
call("3", "abc"),
|
||||
call("5", "abc"),
|
||||
@@ -79,12 +80,12 @@ 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):
|
||||
plugin.prepare_game_times_context.return_value = async_return_value(None)
|
||||
async def test_get_game_time_error(exception, code, message, internal_type, plugin, read, write):
|
||||
plugin.prepare_game_times_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -93,9 +94,10 @@ async def test_get_game_time_error(exception, code, message, plugin, read, write
|
||||
"game_ids": ["6"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_game_time.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_game_time.assert_called()
|
||||
plugin.game_times_import_complete.assert_called_once_with()
|
||||
|
||||
@@ -112,7 +114,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}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -135,16 +138,17 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
|
||||
"game_ids": ["6"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
"error": {
|
||||
"code": 4,
|
||||
"message": "Backend error"
|
||||
"message": "Backend error",
|
||||
"data": {"internal_type": "BackendError"}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -152,7 +156,7 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_in_progress(plugin, read, write):
|
||||
plugin.prepare_game_times_context.return_value = async_return_value(None)
|
||||
plugin.prepare_game_times_context.return_value = None
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -172,13 +176,14 @@ async def test_import_in_progress(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
|
||||
messages = get_messages(write)
|
||||
assert {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -190,7 +195,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
|
||||
|
||||
@@ -200,7 +206,7 @@ async def test_update_game(plugin, write):
|
||||
game_time = GameTime("3", 60, 1549550504)
|
||||
plugin.update_game_time(game_time)
|
||||
await skip_loop()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message
|
||||
|
||||
|
||||
@@ -15,6 +13,7 @@ async def test_success(plugin, read):
|
||||
}
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.install_game.assert_called_with(game_id="3")
|
||||
|
||||
@@ -2,7 +2,7 @@ import pytest
|
||||
|
||||
from galaxy.api.plugin import Plugin
|
||||
from galaxy.api.consts import Platform
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
from galaxy.unittest.mock import delayed_return_value_iterable
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -20,8 +20,9 @@ async def test_get_capabilities(reader, writer, read, write):
|
||||
}
|
||||
token = "token"
|
||||
plugin = PluginImpl(Platform.Generic, "0.1", reader, writer, token)
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -44,7 +45,7 @@ async def test_shutdown(plugin, read, write):
|
||||
"id": "5",
|
||||
"method": "shutdown"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request))]
|
||||
read.side_effect = [create_message(request)]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.shutdown.assert_called_with()
|
||||
@@ -64,8 +65,9 @@ async def test_ping(plugin, read, write):
|
||||
"id": "7",
|
||||
"method": "ping"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -77,8 +79,9 @@ async def test_ping(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tick_before_handshake(plugin, read):
|
||||
read.side_effect = [async_return_value(b"")]
|
||||
read.side_effect = [b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.tick.assert_not_called()
|
||||
|
||||
|
||||
@@ -90,6 +93,7 @@ async def test_tick_after_handshake(plugin, read):
|
||||
"method": "initialize_cache",
|
||||
"params": {"data": {}}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = delayed_return_value_iterable([create_message(request), b""], 1)
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.tick.assert_called_with()
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message
|
||||
|
||||
|
||||
@@ -15,6 +13,7 @@ async def test_success(plugin, read):
|
||||
}
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.launch_game.assert_called_with(game_id="3")
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -11,7 +9,8 @@ async def test_success(plugin, read):
|
||||
"method": "launch_platform_client"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
plugin.launch_platform_client.return_value = async_return_value(None)
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.launch_platform_client.return_value = None
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.launch_platform_client.assert_called_with()
|
||||
|
||||
@@ -3,7 +3,7 @@ import pytest
|
||||
from galaxy.api.types import LocalGame
|
||||
from galaxy.api.consts import LocalGameState
|
||||
from galaxy.api.errors import UnknownError, FailedParsingManifest
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -15,14 +15,15 @@ async def test_success(plugin, read, write):
|
||||
"id": "3",
|
||||
"method": "import_local_games"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
plugin.get_local_games.return_value = async_return_value([
|
||||
plugin.get_local_games.return_value = [
|
||||
LocalGame("1", LocalGameState.Running),
|
||||
LocalGame("2", LocalGameState.Installed),
|
||||
LocalGame("3", LocalGameState.Installed | LocalGameState.Running)
|
||||
])
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_local_games.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -51,21 +52,22 @@ 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",
|
||||
"method": "import_local_games"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_local_games.side_effect = error()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_local_games.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -74,7 +76,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}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
from unittest.mock import call
|
||||
|
||||
import pytest
|
||||
from galaxy.api.errors import FailedParsingManifest
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
from galaxy.unittest.mock import delayed_return_value
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_local_size_success(plugin, read, write):
|
||||
context = {'abc': 'def'}
|
||||
plugin.prepare_local_size_context.return_value = async_return_value(context)
|
||||
plugin.prepare_local_size_context.return_value = context
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "11",
|
||||
"method": "start_local_size_import",
|
||||
"params": {"game_ids": ["777", "13", "42"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_local_size.side_effect = [
|
||||
async_return_value(100000000000, 1),
|
||||
async_return_value(None),
|
||||
async_return_value(3333333)
|
||||
100000000000,
|
||||
None,
|
||||
3333333
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_local_size.assert_has_calls([
|
||||
call("777", context),
|
||||
call("13", context),
|
||||
@@ -31,12 +32,17 @@ async def test_get_local_size_success(plugin, read, write):
|
||||
])
|
||||
plugin.local_size_import_complete.assert_called_once_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "11",
|
||||
"result": None
|
||||
},
|
||||
messages = get_messages(write)
|
||||
prepare_local_size_context_response = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "11",
|
||||
"result": None
|
||||
}
|
||||
# response for prepare_local_size_context may be returned before or after
|
||||
# notifications about import success
|
||||
assert prepare_local_size_context_response in messages
|
||||
messages.remove(prepare_local_size_context_response)
|
||||
assert messages == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "local_size_import_success",
|
||||
@@ -69,23 +75,24 @@ 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)
|
||||
plugin.prepare_local_size_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"method": "start_local_size_import",
|
||||
"params": {"game_ids": [game_id]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_local_size.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_local_size.assert_called()
|
||||
plugin.local_size_import_complete.assert_called_once_with()
|
||||
|
||||
@@ -105,7 +112,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,18 +130,18 @@ 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(error_details)
|
||||
plugin.prepare_local_size_context.side_effect = FailedParsingManifest(data=error_details)
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"method": "start_local_size_import",
|
||||
"params": {"game_ids": ["6"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -139,7 +149,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -147,8 +160,10 @@ async def test_prepare_get_local_size_context_error(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_already_in_progress_error(plugin, read, write):
|
||||
plugin.prepare_local_size_context.return_value = async_return_value(None)
|
||||
plugin.get_local_size.return_value = async_return_value(100, 5)
|
||||
plugin.prepare_local_size_context.return_value = None
|
||||
print("******", plugin.get_local_size)
|
||||
|
||||
plugin.get_local_size.side_effect = delayed_return_value(100, 5)
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -168,12 +183,13 @@ async def test_import_already_in_progress_error(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
responses = get_messages(write)
|
||||
assert {
|
||||
@@ -186,6 +202,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
|
||||
|
||||
@@ -3,7 +3,6 @@ from unittest.mock import call
|
||||
import pytest
|
||||
from galaxy.api.consts import OSCompatibility
|
||||
from galaxy.api.errors import BackendError
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -11,20 +10,21 @@ from tests import create_message, get_messages
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_os_compatibility_success(plugin, read, write):
|
||||
context = "abc"
|
||||
plugin.prepare_os_compatibility_context.return_value = async_return_value(context)
|
||||
plugin.prepare_os_compatibility_context.return_value = context
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "11",
|
||||
"method": "start_os_compatibility_import",
|
||||
"params": {"game_ids": ["666", "13", "42"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_os_compatibility.side_effect = [
|
||||
async_return_value(OSCompatibility.Linux),
|
||||
async_return_value(None),
|
||||
async_return_value(OSCompatibility.Windows | OSCompatibility.MacOS),
|
||||
OSCompatibility.Linux,
|
||||
None,
|
||||
OSCompatibility.Windows | OSCompatibility.MacOS,
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_os_compatibility.assert_has_calls([
|
||||
call("666", context),
|
||||
call("13", context),
|
||||
@@ -71,23 +71,24 @@ 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)
|
||||
plugin.prepare_os_compatibility_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"method": "start_os_compatibility_import",
|
||||
"params": {"game_ids": [game_id]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_os_compatibility.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_os_compatibility.assert_called()
|
||||
plugin.os_compatibility_import_complete.assert_called_once_with()
|
||||
|
||||
@@ -104,7 +105,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}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -126,8 +128,9 @@ async def test_prepare_get_os_compatibility_context_error(plugin, read, write):
|
||||
"method": "start_os_compatibility_import",
|
||||
"params": {"game_ids": ["6"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -135,7 +138,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"}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -143,7 +147,7 @@ async def test_prepare_get_os_compatibility_context_error(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_already_in_progress_error(plugin, read, write):
|
||||
plugin.prepare_os_compatibility_context.return_value = async_return_value(None)
|
||||
plugin.prepare_os_compatibility_context.return_value = None
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -163,12 +167,13 @@ async def test_import_already_in_progress_error(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
|
||||
responses = get_messages(write)
|
||||
assert {
|
||||
@@ -181,7 +186,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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import pytest
|
||||
from galaxy.api.types import Game, Dlc, LicenseInfo
|
||||
from galaxy.api.consts import LicenseType
|
||||
from galaxy.api.errors import UnknownError
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -15,9 +15,9 @@ async def test_success(plugin, read, write):
|
||||
"id": "3",
|
||||
"method": "import_owned_games"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
plugin.get_owned_games.return_value = async_return_value([
|
||||
plugin.get_owned_games.return_value = [
|
||||
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
|
||||
Game(
|
||||
"5",
|
||||
@@ -27,8 +27,9 @@ async def test_success(plugin, read, write):
|
||||
Dlc("8", "Temerian Armor Set", LicenseInfo(LicenseType.FreeToPlay, None)),
|
||||
],
|
||||
LicenseInfo(LicenseType.SinglePurchase, None))
|
||||
])
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_owned_games.assert_called_with()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -80,9 +81,10 @@ async def test_failure(plugin, read, write):
|
||||
"method": "import_owned_games"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_owned_games.side_effect = UnknownError()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_owned_games.assert_called_with()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -90,7 +92,8 @@ async def test_failure(plugin, read, write):
|
||||
"id": "3",
|
||||
"error": {
|
||||
"code": 0,
|
||||
"message": "Unknown error"
|
||||
"message": "Unknown error",
|
||||
"data": {"internal_type": "UnknownError"}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -42,10 +42,11 @@ async def test_initialize_cache(plugin, read, write, cache_data):
|
||||
"method": "initialize_cache",
|
||||
"params": {"data": cache_data}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
assert {} == plugin.persistent_cache
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.handshake_complete.assert_called_once_with()
|
||||
assert cache_data == plugin.persistent_cache
|
||||
assert_rpc_response(write, response_id=request_id)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import pytest
|
||||
import asyncio
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
from tests import create_message, get_messages
|
||||
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):
|
||||
|
||||
@@ -22,7 +23,7 @@ async def test_refresh_credentials_success(plugin, read, write):
|
||||
"result": refreshed_credentials
|
||||
}
|
||||
# 2 loop iterations delay is to force sending response after request has been sent
|
||||
read.side_effect = [async_return_value(create_message(response), loop_iterations_delay=2)]
|
||||
read.side_effect = [create_message(response), b""]
|
||||
|
||||
result = await plugin.refresh_credentials({}, False)
|
||||
assert get_messages(write) == [
|
||||
@@ -53,12 +54,13 @@ async def test_refresh_credentials_failure(exception, plugin, read, write):
|
||||
}
|
||||
|
||||
# 2 loop iterations delay is to force sending response after request has been sent
|
||||
read.side_effect = [async_return_value(create_message(response), loop_iterations_delay=2)]
|
||||
read.side_effect = [create_message(response), b""]
|
||||
|
||||
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",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message
|
||||
|
||||
@@ -11,7 +10,8 @@ async def test_success(plugin, read):
|
||||
"method": "shutdown_platform_client"
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
plugin.shutdown_platform_client.return_value = async_return_value(None)
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.shutdown_platform_client.return_value = None
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.shutdown_platform_client.assert_called_with()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.reader import StreamLineReader
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@@ -11,14 +10,14 @@ def stream_line_reader(reader):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_message(stream_line_reader, read):
|
||||
read.return_value = async_return_value(b"a\n")
|
||||
read.return_value = b"a\n"
|
||||
assert await stream_line_reader.readline() == b"a"
|
||||
read.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_separate_messages(stream_line_reader, read):
|
||||
read.side_effect = [async_return_value(b"a\n"), async_return_value(b"b\n")]
|
||||
read.side_effect = [b"a\n", b"b\n"]
|
||||
assert await stream_line_reader.readline() == b"a"
|
||||
assert await stream_line_reader.readline() == b"b"
|
||||
assert read.call_count == 2
|
||||
@@ -26,7 +25,7 @@ async def test_separate_messages(stream_line_reader, read):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connected_messages(stream_line_reader, read):
|
||||
read.return_value = async_return_value(b"a\nb\n")
|
||||
read.return_value = b"a\nb\n"
|
||||
assert await stream_line_reader.readline() == b"a"
|
||||
assert await stream_line_reader.readline() == b"b"
|
||||
read.assert_called_once()
|
||||
@@ -34,13 +33,13 @@ async def test_connected_messages(stream_line_reader, read):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cut_message(stream_line_reader, read):
|
||||
read.side_effect = [async_return_value(b"a"), async_return_value(b"b\n")]
|
||||
read.side_effect = [b"a", b"b\n"]
|
||||
assert await stream_line_reader.readline() == b"ab"
|
||||
assert read.call_count == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_half_message(stream_line_reader, read):
|
||||
read.side_effect = [async_return_value(b"a"), async_return_value(b"")]
|
||||
read.side_effect = [b"a", b""]
|
||||
assert await stream_line_reader.readline() == b""
|
||||
assert read.call_count == 2
|
||||
|
||||
@@ -3,7 +3,6 @@ import pytest
|
||||
from galaxy.api.types import Subscription, SubscriptionGame
|
||||
from galaxy.api.consts import SubscriptionDiscovery
|
||||
from galaxy.api.errors import FailedParsingManifest, BackendError, UnknownError
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message, get_messages
|
||||
|
||||
@@ -14,14 +13,15 @@ async def test_get_subscriptions_success(plugin, read, write):
|
||||
"id": "3",
|
||||
"method": "import_subscriptions"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
plugin.get_subscriptions.return_value = async_return_value([
|
||||
plugin.get_subscriptions.return_value = [
|
||||
Subscription("1"),
|
||||
Subscription("2", False, subscription_discovery=SubscriptionDiscovery.AUTOMATIC),
|
||||
Subscription("3", True, 1580899100, SubscriptionDiscovery.USER_ENABLED)
|
||||
])
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_subscriptions.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -53,21 +53,22 @@ 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",
|
||||
"method": "import_subscriptions"
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_subscriptions.side_effect = error()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_subscriptions.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
@@ -76,6 +77,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
|
||||
}
|
||||
}
|
||||
@@ -84,7 +86,7 @@ async def test_get_subscriptions_failure_generic(plugin, read, write, error, cod
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_subscription_games_success(plugin, read, write):
|
||||
plugin.prepare_subscription_games_context.return_value = async_return_value(5)
|
||||
plugin.prepare_subscription_games_context.return_value = 5
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -93,22 +95,28 @@ async def test_get_subscription_games_success(plugin, read, write):
|
||||
"subscription_names": ["sub_a"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
async def sub_games():
|
||||
# Create an async generator that yields the games
|
||||
async def mock_subscription_games_generator(subscription_name, context):
|
||||
games = [
|
||||
SubscriptionGame(game_title="game A", game_id="game_A"),
|
||||
SubscriptionGame(game_title="game B", game_id="game_B", start_time=1548495632),
|
||||
SubscriptionGame(game_title="game C", game_id="game_C", end_time=1548495633),
|
||||
SubscriptionGame(game_title="game D", game_id="game_D", start_time=1548495632, end_time=1548495633),
|
||||
]
|
||||
yield [game for game in games]
|
||||
# first chunk of the games
|
||||
[SubscriptionGame(game_title="game A", game_id="game_A"),
|
||||
SubscriptionGame(game_title="game B", game_id="game_B", start_time=1548495632),
|
||||
SubscriptionGame(game_title="game C", game_id="game_C", end_time=1548495633)],
|
||||
# second chunk of the games
|
||||
[SubscriptionGame(game_title="game D", game_id="game_D", start_time=1548495632, end_time=1548495633)],
|
||||
]
|
||||
for game in games:
|
||||
yield game
|
||||
|
||||
plugin.get_subscription_games.side_effect = mock_subscription_games_generator
|
||||
|
||||
plugin.get_subscription_games.return_value = sub_games()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.prepare_subscription_games_context.assert_called_with(["sub_a"])
|
||||
plugin.get_subscription_games.assert_called_with("sub_a", 5)
|
||||
plugin.subscription_games_import_complete.asert_called_with()
|
||||
plugin.subscription_games_import_complete.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -136,6 +144,15 @@ async def test_get_subscription_games_success(plugin, read, write):
|
||||
"game_id": "game_C",
|
||||
"end_time": 1548495633
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "subscription_games_import_success",
|
||||
"params": {
|
||||
"subscription_name": "sub_a",
|
||||
"subscription_games": [
|
||||
{
|
||||
"game_title": "game D",
|
||||
"game_id": "game_D",
|
||||
@@ -162,7 +179,7 @@ async def test_get_subscription_games_success(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_subscription_games_success_empty(plugin, read, write):
|
||||
plugin.prepare_subscription_games_context.return_value = async_return_value(5)
|
||||
plugin.prepare_subscription_games_context.return_value = 5
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -171,16 +188,17 @@ async def test_get_subscription_games_success_empty(plugin, read, write):
|
||||
"subscription_names": ["sub_a"]
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
|
||||
async def sub_games():
|
||||
yield None
|
||||
|
||||
plugin.get_subscription_games.return_value = sub_games()
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.prepare_subscription_games_context.assert_called_with(["sub_a"])
|
||||
plugin.get_subscription_games.assert_called_with("sub_a", 5)
|
||||
plugin.subscription_games_import_complete.asert_called_with()
|
||||
plugin.subscription_games_import_complete.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -212,12 +230,12 @@ 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):
|
||||
plugin.prepare_subscription_games_context.return_value = async_return_value(None)
|
||||
async def test_get_subscription_games_error(exception, code, message, internal_type, plugin, read, write):
|
||||
plugin.prepare_subscription_games_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "3",
|
||||
@@ -227,11 +245,12 @@ async def test_get_subscription_games_error(exception, code, message, plugin, re
|
||||
}
|
||||
}
|
||||
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_subscription_games.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_subscription_games.assert_called()
|
||||
plugin.subscription_games_import_complete.asert_called_with()
|
||||
plugin.subscription_games_import_complete.assert_called_with()
|
||||
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
@@ -246,7 +265,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,18 +289,18 @@ 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(error_details)
|
||||
plugin.prepare_subscription_games_context.side_effect = BackendError(data=error_details)
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"method": "start_subscription_games_import",
|
||||
"params": {"subscription_names": ["sub_a", "sub_b"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -288,7 +308,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -296,7 +319,7 @@ async def test_prepare_get_subscription_games_context_error(plugin, read, write)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_already_in_progress_error(plugin, read, write):
|
||||
plugin.prepare_subscription_games_context.return_value = async_return_value(None)
|
||||
plugin.prepare_subscription_games_context.return_value = None
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -316,13 +339,13 @@ async def test_import_already_in_progress_error(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
responses = get_messages(write)
|
||||
assert {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -334,7 +357,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
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from galaxy.unittest.mock import async_return_value
|
||||
|
||||
from tests import create_message
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -13,7 +11,8 @@ async def test_success(plugin, read):
|
||||
"game_id": "3"
|
||||
}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_owned_games.return_value = None
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.uninstall_game.assert_called_with(game_id="3")
|
||||
|
||||
@@ -5,7 +5,7 @@ import pytest
|
||||
from galaxy.api.consts import PresenceState
|
||||
from galaxy.api.errors import BackendError
|
||||
from galaxy.api.types import UserPresence
|
||||
from galaxy.unittest.mock import async_return_value, skip_loop
|
||||
from galaxy.unittest.mock import skip_loop
|
||||
from tests import create_message, get_messages
|
||||
|
||||
|
||||
@@ -13,52 +13,53 @@ from tests import create_message, get_messages
|
||||
async def test_get_user_presence_success(plugin, read, write):
|
||||
context = "abc"
|
||||
user_id_list = ["666", "13", "42", "69", "22"]
|
||||
plugin.prepare_user_presence_context.return_value = async_return_value(context)
|
||||
plugin.prepare_user_presence_context.return_value = context
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "11",
|
||||
"method": "start_user_presence_import",
|
||||
"params": {"user_id_list": user_id_list}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_user_presence.side_effect = [
|
||||
async_return_value(UserPresence(
|
||||
UserPresence(
|
||||
PresenceState.Unknown,
|
||||
"game-id1",
|
||||
None,
|
||||
"unknown state",
|
||||
None
|
||||
)),
|
||||
async_return_value(UserPresence(
|
||||
),
|
||||
UserPresence(
|
||||
PresenceState.Offline,
|
||||
None,
|
||||
None,
|
||||
"Going to grandma's house",
|
||||
None
|
||||
)),
|
||||
async_return_value(UserPresence(
|
||||
),
|
||||
UserPresence(
|
||||
PresenceState.Online,
|
||||
"game-id3",
|
||||
"game-title3",
|
||||
"Pew pew",
|
||||
None
|
||||
)),
|
||||
async_return_value(UserPresence(
|
||||
),
|
||||
UserPresence(
|
||||
PresenceState.Away,
|
||||
None,
|
||||
"game-title4",
|
||||
"AFKKTHXBY",
|
||||
None
|
||||
)),
|
||||
async_return_value(UserPresence(
|
||||
),
|
||||
UserPresence(
|
||||
PresenceState.Away,
|
||||
None,
|
||||
"game-title5",
|
||||
None,
|
||||
"Playing game-title5: In Menu"
|
||||
)),
|
||||
),
|
||||
]
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_user_presence.assert_has_calls([
|
||||
call(user_id, context) for user_id in user_id_list
|
||||
])
|
||||
@@ -139,23 +140,24 @@ 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)
|
||||
plugin.prepare_user_presence_context.return_value = None
|
||||
request = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"method": "start_user_presence_import",
|
||||
"params": {"user_id_list": [user_id]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
plugin.get_user_presence.side_effect = exception
|
||||
await plugin.run()
|
||||
await plugin.wait_closed()
|
||||
plugin.get_user_presence.assert_called()
|
||||
plugin.user_presence_import_complete.assert_called_once_with()
|
||||
|
||||
@@ -172,7 +174,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
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -194,16 +199,19 @@ async def test_prepare_get_user_presence_context_error(plugin, read, write):
|
||||
"method": "start_user_presence_import",
|
||||
"params": {"user_id_list": ["6"]}
|
||||
}
|
||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||
read.side_effect = [create_message(request), b""]
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
assert get_messages(write) == [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": request_id,
|
||||
"error": {
|
||||
"code": 4,
|
||||
"message": "Backend error"
|
||||
"message": "Backend error",
|
||||
"data": {
|
||||
"internal_type": "BackendError"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -211,7 +219,7 @@ async def test_prepare_get_user_presence_context_error(plugin, read, write):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_import_already_in_progress_error(plugin, read, write):
|
||||
plugin.prepare_user_presence_context.return_value = async_return_value(None)
|
||||
plugin.prepare_user_presence_context.return_value = None
|
||||
requests = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -231,13 +239,13 @@ async def test_import_already_in_progress_error(plugin, read, write):
|
||||
}
|
||||
]
|
||||
read.side_effect = [
|
||||
async_return_value(create_message(requests[0])),
|
||||
async_return_value(create_message(requests[1])),
|
||||
async_return_value(b"", 10)
|
||||
create_message(requests[0]),
|
||||
create_message(requests[1]),
|
||||
b""
|
||||
]
|
||||
|
||||
await plugin.run()
|
||||
|
||||
await plugin.wait_closed()
|
||||
responses = get_messages(write)
|
||||
assert {
|
||||
"jsonrpc": "2.0",
|
||||
@@ -249,7 +257,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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user