mirror of
https://github.com/gogcom/galaxy-integrations-python-api.git
synced 2025-12-31 19:08:16 -05:00
Compare commits
37 Commits
0.25
...
deployed_0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed91fd582c | ||
|
|
bd393a96f0 | ||
|
|
c53aab1abb | ||
|
|
80f40b1971 | ||
|
|
0da0296154 | ||
|
|
9a115557b3 | ||
|
|
14c2d7d9e8 | ||
|
|
4a7a759cea | ||
|
|
da8da24b01 | ||
|
|
ccbb13e685 | ||
|
|
a3ca815975 | ||
|
|
f2d4127a31 | ||
|
|
07b6edce12 | ||
|
|
ef7f9ccca1 | ||
|
|
3b296cbcc9 | ||
|
|
f5361cd5ab | ||
|
|
758909efba | ||
|
|
0bc8000f14 | ||
|
|
e62e7e0e6e | ||
|
|
be6c0eb03e | ||
|
|
0ee56193de | ||
|
|
6bc91a12fa | ||
|
|
6d513d86bf | ||
|
|
bdd2225262 | ||
|
|
68fdc4d188 | ||
|
|
f283c10a95 | ||
|
|
453734cefe | ||
|
|
85f1d83c28 | ||
|
|
701d3cf522 | ||
|
|
c8083b9006 | ||
|
|
0608ade6d3 | ||
|
|
c349a3df8e | ||
|
|
1fd959a665 | ||
|
|
234a21d085 | ||
|
|
90835ece58 | ||
|
|
9e1c8cfddd | ||
|
|
f7f170b9ca |
56
README.md
56
README.md
@@ -1,24 +1,52 @@
|
||||
# Galaxy python plugin API
|
||||
# GOG Galaxy - Community Integration - Python API
|
||||
|
||||
## Usage
|
||||
This document is still work in progress.
|
||||
|
||||
Implement plugin:
|
||||
## Basic Usage
|
||||
|
||||
Basic implementation:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from galaxy.api.plugin import Plugin
|
||||
import sys
|
||||
from galaxy.api.plugin import Plugin, create_and_run_plugin
|
||||
from galaxy.api.consts import Platform
|
||||
|
||||
class PluginExample(Plugin):
|
||||
def __init__(self, reader, writer, token):
|
||||
super().__init__(
|
||||
Platform.Generic, # Choose platform from available list
|
||||
"0.1", # Version
|
||||
reader,
|
||||
writer,
|
||||
token
|
||||
)
|
||||
|
||||
# implement methods
|
||||
async def authenticate(self, stored_credentials=None):
|
||||
pass
|
||||
|
||||
def main():
|
||||
create_and_run_plugin(PluginExample, sys.argv)
|
||||
|
||||
# run plugin event loop
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(MockPlugin().run())
|
||||
main()
|
||||
```
|
||||
|
||||
Use [pyinstaller](https://www.pyinstaller.org/) to create plugin executbale.
|
||||
Plugin should be deployed with manifest:
|
||||
```json
|
||||
{
|
||||
"name": "Example plugin",
|
||||
"platform": "generic",
|
||||
"guid": "UNIQUE-GUID",
|
||||
"version": "0.1",
|
||||
"description": "Example plugin",
|
||||
"author": "Name",
|
||||
"email": "author@email.com",
|
||||
"url": "https://github.com/user/galaxy-plugin-example",
|
||||
"script": "plugin.py"
|
||||
}
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
@@ -31,15 +59,9 @@ Run tests:
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
## Methods Documentation
|
||||
TODO
|
||||
|
||||
## Changelog
|
||||
## Legal Notice
|
||||
|
||||
### 0.21
|
||||
* Add `Epic` platform.
|
||||
### 0.16
|
||||
* Do not log sensitive data.
|
||||
* Return `LocalGameState` as int (possible combination of flags).
|
||||
### 0.15
|
||||
* `shutdown()` is called on socket disconnection.
|
||||
### 0.14
|
||||
* Added required version parameter to Plugin constructor.
|
||||
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.
|
||||
|
||||
14
jenkins/release.groovy
Normal file
14
jenkins/release.groovy
Normal file
@@ -0,0 +1,14 @@
|
||||
stage('Upload to github')
|
||||
{
|
||||
node('ActiveClientMacosxBuilder') {
|
||||
deleteDir()
|
||||
checkout scm
|
||||
withPythonEnv('/usr/local/bin/python3.7') {
|
||||
withCredentials([string(credentialsId: 'github_goggalaxy', variable: 'GITHUB_TOKEN')]) {
|
||||
sh 'pip install -r jenkins/requirements.txt'
|
||||
def version = sh(returnStdout: true, script: 'python setup.py --version').trim()
|
||||
sh "python jenkins/release.py $version"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
jenkins/release.py
Normal file
26
jenkins/release.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
import sys
|
||||
from galaxy.github.exporter import transfer_repo
|
||||
|
||||
GITHUB_USERNAME = "goggalaxy"
|
||||
GITHUB_EMAIL = "galaxy-sdk@gog.com"
|
||||
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
|
||||
GITHUB_REPO_NAME = "galaxy-integrations-python-api"
|
||||
SOURCE_BRANCH = os.environ["GIT_REFSPEC"]
|
||||
|
||||
GITLAB_USERNAME = "galaxy-client"
|
||||
GITLAB_REPO_NAME = "galaxy-plugin-api"
|
||||
|
||||
def version_provider(_):
|
||||
return sys.argv[1]
|
||||
|
||||
gh_version = transfer_repo(
|
||||
version_provider=version_provider,
|
||||
source_repo_spec="git@gitlab.gog.com:{}/{}.git".format(GITLAB_USERNAME, GITLAB_REPO_NAME),
|
||||
source_include_elements=["src", "tests", "requirements.txt", ".gitignore", "*.md", "pytest.ini", "setup.py"],
|
||||
source_branch=SOURCE_BRANCH,
|
||||
dest_repo_spec="https://{}:{}@github.com/{}/{}.git".format(GITHUB_USERNAME, GITHUB_TOKEN, "gogcom", GITHUB_REPO_NAME),
|
||||
dest_branch="master",
|
||||
dest_user_email=GITHUB_EMAIL,
|
||||
dest_user_name="GOG Galaxy SDK Team"
|
||||
)
|
||||
1
jenkins/requirements.txt
Normal file
1
jenkins/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
git+ssh://git@gitlab.gog.com/galaxy-client/github-exporter.git@v0.1
|
||||
@@ -1,2 +1,8 @@
|
||||
-e .
|
||||
pytest==4.2.0
|
||||
pytest-asyncio==0.10.0
|
||||
pytest-mock==1.10.3
|
||||
pytest-flakes==4.0.0
|
||||
# because of pip bug https://github.com/pypa/pip/issues/4780
|
||||
aiohttp==3.5.4
|
||||
certifi==2019.3.9
|
||||
|
||||
9
setup.py
9
setup.py
@@ -2,9 +2,14 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="galaxy.plugin.api",
|
||||
version="0.25",
|
||||
version="0.31.3",
|
||||
description="Galaxy python plugin API",
|
||||
author='Galaxy team',
|
||||
author_email='galaxy@gog.com',
|
||||
packages=find_packages(exclude=["tests"])
|
||||
packages=find_packages("src"),
|
||||
package_dir={'': 'src'},
|
||||
install_requires=[
|
||||
"aiohttp==3.5.4",
|
||||
"certifi==2019.3.9"
|
||||
]
|
||||
)
|
||||
|
||||
@@ -77,3 +77,7 @@ class IncoherentLastMessage(ApplicationError):
|
||||
class MessageNotFound(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(500, "Message not found", data)
|
||||
|
||||
class ImportInProgress(ApplicationError):
|
||||
def __init__(self, data=None):
|
||||
super().__init__(600, "Import already in progress", data)
|
||||
@@ -12,6 +12,9 @@ class JsonRpcError(Exception):
|
||||
self.data = data
|
||||
super().__init__()
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.code == other.code and self.message == other.message and self.data == other.data
|
||||
|
||||
class ParseError(JsonRpcError):
|
||||
def __init__(self):
|
||||
super().__init__(-32700, "Parse error")
|
||||
@@ -9,6 +9,7 @@ import sys
|
||||
|
||||
from galaxy.api.jsonrpc import Server, NotificationClient
|
||||
from galaxy.api.consts import Feature
|
||||
from galaxy.api.errors import UnknownError, ImportInProgress
|
||||
|
||||
class JSONEncoder(json.JSONEncoder):
|
||||
def default(self, o): # pylint: disable=method-hidden
|
||||
@@ -41,14 +42,25 @@ class Plugin():
|
||||
self._shutdown()
|
||||
self._server.register_eof(eof_handler)
|
||||
|
||||
self._achievements_import_in_progress = False
|
||||
self._game_times_import_in_progress = False
|
||||
|
||||
# internal
|
||||
self._register_method("shutdown", self._shutdown, internal=True)
|
||||
self._register_method("get_capabilities", self._get_capabilities, internal=True)
|
||||
self._register_method("ping", self._ping, internal=True)
|
||||
|
||||
# implemented by developer
|
||||
self._register_method("init_authentication", self.authenticate, sensitive_params=["stored_credentials"])
|
||||
self._register_method("pass_login_credentials", self.pass_login_credentials)
|
||||
self._register_method(
|
||||
"init_authentication",
|
||||
self.authenticate,
|
||||
sensitive_params=["stored_credentials"]
|
||||
)
|
||||
self._register_method(
|
||||
"pass_login_credentials",
|
||||
self.pass_login_credentials,
|
||||
sensitive_params=["cookies", "credentials"]
|
||||
)
|
||||
self._register_method(
|
||||
"import_owned_games",
|
||||
self.get_owned_games,
|
||||
@@ -61,6 +73,10 @@ class Plugin():
|
||||
result_name="unlocked_achievements",
|
||||
feature=Feature.ImportAchievements
|
||||
)
|
||||
self._register_method(
|
||||
"start_achievements_import",
|
||||
self.start_achievements_import,
|
||||
)
|
||||
self._register_method(
|
||||
"import_local_games",
|
||||
self.get_local_games,
|
||||
@@ -114,13 +130,16 @@ class Plugin():
|
||||
result_name="messages",
|
||||
feature=Feature.Chat
|
||||
)
|
||||
|
||||
self._register_method(
|
||||
"import_game_times",
|
||||
self.get_game_times,
|
||||
result_name="game_times",
|
||||
feature=Feature.ImportGameTime
|
||||
)
|
||||
self._register_method(
|
||||
"start_game_times_import",
|
||||
self.start_game_times_import,
|
||||
)
|
||||
|
||||
@property
|
||||
def features(self):
|
||||
@@ -222,6 +241,26 @@ class Plugin():
|
||||
}
|
||||
self._notification_client.notify("achievement_unlocked", params)
|
||||
|
||||
def game_achievements_import_success(self, game_id, achievements):
|
||||
params = {
|
||||
"game_id": game_id,
|
||||
"unlocked_achievements": achievements
|
||||
}
|
||||
self._notification_client.notify("game_achievements_import_success", params)
|
||||
|
||||
def game_achievements_import_failure(self, game_id, error):
|
||||
params = {
|
||||
"game_id": game_id,
|
||||
"error": {
|
||||
"code": error.code,
|
||||
"message": error.message
|
||||
}
|
||||
}
|
||||
self._notification_client.notify("game_achievements_import_failure", params)
|
||||
|
||||
def achievements_import_finished(self):
|
||||
self._notification_client.notify("achievements_import_finished", None)
|
||||
|
||||
def update_local_game_status(self, local_game):
|
||||
params = {"local_game" : local_game}
|
||||
self._notification_client.notify("local_game_status_changed", params)
|
||||
@@ -246,6 +285,23 @@ class Plugin():
|
||||
params = {"game_time" : game_time}
|
||||
self._notification_client.notify("game_time_updated", params)
|
||||
|
||||
def game_time_import_success(self, game_time):
|
||||
params = {"game_time" : game_time}
|
||||
self._notification_client.notify("game_time_import_success", params)
|
||||
|
||||
def game_time_import_failure(self, game_id, error):
|
||||
params = {
|
||||
"game_id": game_id,
|
||||
"error": {
|
||||
"code": error.code,
|
||||
"message": error.message
|
||||
}
|
||||
}
|
||||
self._notification_client.notify("game_time_import_failure", params)
|
||||
|
||||
def game_times_import_finished(self):
|
||||
self._notification_client.notify("game_times_import_finished", None)
|
||||
|
||||
def lost_authentication(self):
|
||||
self._notification_client.notify("authentication_lost", None)
|
||||
|
||||
@@ -279,6 +335,32 @@ class Plugin():
|
||||
async def get_unlocked_achievements(self, game_id):
|
||||
raise NotImplementedError()
|
||||
|
||||
async def start_achievements_import(self, game_ids):
|
||||
if self._achievements_import_in_progress:
|
||||
raise ImportInProgress()
|
||||
|
||||
async def import_games_achievements_task(game_ids):
|
||||
try:
|
||||
await self.import_games_achievements(game_ids)
|
||||
finally:
|
||||
self.achievements_import_finished()
|
||||
self._achievements_import_in_progress = False
|
||||
|
||||
asyncio.create_task(import_games_achievements_task(game_ids))
|
||||
self._achievements_import_in_progress = True
|
||||
|
||||
async def import_games_achievements(self, game_ids):
|
||||
"""Call game_achievements_import_success/game_achievements_import_failure for each game_id on the list"""
|
||||
async def import_game_achievements(game_id):
|
||||
try:
|
||||
achievements = await self.get_unlocked_achievements(game_id)
|
||||
self.game_achievements_import_success(game_id, achievements)
|
||||
except Exception as error:
|
||||
self.game_achievements_import_failure(game_id, error)
|
||||
|
||||
imports = [import_game_achievements(game_id) for game_id in game_ids]
|
||||
await asyncio.gather(*imports)
|
||||
|
||||
async def get_local_games(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -315,6 +397,36 @@ class Plugin():
|
||||
async def get_game_times(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
async def start_game_times_import(self, game_ids):
|
||||
if self._game_times_import_in_progress:
|
||||
raise ImportInProgress()
|
||||
|
||||
async def import_game_times_task(game_ids):
|
||||
try:
|
||||
await self.import_game_times(game_ids)
|
||||
finally:
|
||||
self.game_times_import_finished()
|
||||
self._game_times_import_in_progress = False
|
||||
|
||||
asyncio.create_task(import_game_times_task(game_ids))
|
||||
self._game_times_import_in_progress = True
|
||||
|
||||
async def import_game_times(self, game_ids):
|
||||
"""Call game_time_import_success/game_time_import_failure for each game_id on the list"""
|
||||
try:
|
||||
game_times = await self.get_game_times()
|
||||
game_ids_set = set(game_ids)
|
||||
for game_time in game_times:
|
||||
if game_time.game_id not in game_ids_set:
|
||||
continue
|
||||
self.game_time_import_success(game_time)
|
||||
game_ids_set.discard(game_time.game_id)
|
||||
for game_id in game_ids_set:
|
||||
self.game_time_import_failure(game_id, UnknownError())
|
||||
except Exception as error:
|
||||
for game_id in game_ids:
|
||||
self.game_time_import_failure(game_id, error)
|
||||
|
||||
def create_and_run_plugin(plugin_class, argv):
|
||||
if len(argv) < 3:
|
||||
logging.critical("Not enough parameters, required: token, port")
|
||||
@@ -338,6 +450,8 @@ def create_and_run_plugin(plugin_class, argv):
|
||||
|
||||
async def coroutine():
|
||||
reader, writer = await asyncio.open_connection("127.0.0.1", port)
|
||||
extra_info = writer.get_extra_info('sockname')
|
||||
logging.info("Using local address: %s:%u", *extra_info)
|
||||
plugin = plugin_class(reader, writer, token)
|
||||
await plugin.run()
|
||||
|
||||
@@ -20,6 +20,7 @@ class NextStep():
|
||||
next_step: str
|
||||
auth_params: Dict[str, str]
|
||||
cookies: Optional[List[Cookie]] = None
|
||||
js: Optional[Dict[str, List[str]]] = None
|
||||
|
||||
@dataclass
|
||||
class LicenseInfo():
|
||||
47
src/galaxy/http.py
Normal file
47
src/galaxy/http.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import asyncio
|
||||
import ssl
|
||||
from http import HTTPStatus
|
||||
|
||||
import aiohttp
|
||||
import certifi
|
||||
|
||||
from galaxy.api.errors import (
|
||||
AccessDenied, AuthenticationRequired,
|
||||
BackendTimeout, BackendNotAvailable, BackendError, NetworkError, UnknownBackendResponse, UnknownError
|
||||
)
|
||||
|
||||
class HttpClient:
|
||||
def __init__(self, limit=20, timeout=aiohttp.ClientTimeout(total=60), cookie_jar=None):
|
||||
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ssl_context.load_verify_locations(certifi.where())
|
||||
connector = aiohttp.TCPConnector(limit=limit, ssl=ssl_context)
|
||||
self._session = aiohttp.ClientSession(connector=connector, timeout=timeout, cookie_jar=cookie_jar)
|
||||
|
||||
async def close(self):
|
||||
await self._session.close()
|
||||
|
||||
async def request(self, method, *args, **kwargs):
|
||||
try:
|
||||
response = await self._session.request(method, *args, **kwargs)
|
||||
except asyncio.TimeoutError:
|
||||
raise BackendTimeout()
|
||||
except aiohttp.ServerDisconnectedError:
|
||||
raise BackendNotAvailable()
|
||||
except aiohttp.ClientConnectionError:
|
||||
raise NetworkError()
|
||||
except aiohttp.ContentTypeError:
|
||||
raise UnknownBackendResponse()
|
||||
except aiohttp.ClientError:
|
||||
raise UnknownError()
|
||||
if response.status == HTTPStatus.UNAUTHORIZED:
|
||||
raise AuthenticationRequired()
|
||||
if response.status == HTTPStatus.FORBIDDEN:
|
||||
raise AccessDenied()
|
||||
if response.status == HTTPStatus.SERVICE_UNAVAILABLE:
|
||||
raise BackendNotAvailable()
|
||||
if response.status >= 500:
|
||||
raise BackendError()
|
||||
if response.status >= 400:
|
||||
raise UnknownError()
|
||||
|
||||
return response
|
||||
@@ -1,9 +1,12 @@
|
||||
import asyncio
|
||||
import json
|
||||
from unittest.mock import call
|
||||
|
||||
import pytest
|
||||
from pytest import raises
|
||||
|
||||
from galaxy.api.types import Achievement
|
||||
from galaxy.api.errors import UnknownError
|
||||
from galaxy.api.errors import UnknownError, ImportInProgress, BackendError
|
||||
|
||||
def test_initialization_no_unlock_time():
|
||||
with raises(Exception):
|
||||
@@ -99,3 +102,92 @@ def test_unlock_achievement(plugin, write):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_achievements_import_success(plugin, write):
|
||||
achievements = [
|
||||
Achievement(achievement_id="lvl10", unlock_time=1548421241),
|
||||
Achievement(achievement_name="Got level 20", unlock_time=1548422395)
|
||||
]
|
||||
plugin.game_achievements_import_success("134", achievements)
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "game_achievements_import_success",
|
||||
"params": {
|
||||
"game_id": "134",
|
||||
"unlocked_achievements": [
|
||||
{
|
||||
"achievement_id": "lvl10",
|
||||
"unlock_time": 1548421241
|
||||
},
|
||||
{
|
||||
"achievement_name": "Got level 20",
|
||||
"unlock_time": 1548422395
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_achievements_import_failure(plugin, write):
|
||||
plugin.game_achievements_import_failure("134", ImportInProgress())
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "game_achievements_import_failure",
|
||||
"params": {
|
||||
"game_id": "134",
|
||||
"error": {
|
||||
"code": 600,
|
||||
"message": "Import already in progress"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_achievements_import_finished(plugin, write):
|
||||
plugin.achievements_import_finished()
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "achievements_import_finished",
|
||||
"params": None
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_achievements_import(plugin, write, mocker):
|
||||
game_achievements_import_success = mocker.patch.object(plugin, "game_achievements_import_success")
|
||||
game_achievements_import_failure = mocker.patch.object(plugin, "game_achievements_import_failure")
|
||||
achievements_import_finished = mocker.patch.object(plugin, "achievements_import_finished")
|
||||
|
||||
game_ids = ["1", "5", "9"]
|
||||
error = BackendError()
|
||||
achievements = [
|
||||
Achievement(achievement_id="lvl10", unlock_time=1548421241),
|
||||
Achievement(achievement_name="Got level 20", unlock_time=1548422395)
|
||||
]
|
||||
plugin.get_unlocked_achievements.coro.side_effect = [
|
||||
achievements,
|
||||
[],
|
||||
error
|
||||
]
|
||||
await plugin.start_achievements_import(game_ids)
|
||||
|
||||
with pytest.raises(ImportInProgress):
|
||||
await plugin.start_achievements_import(["4", "8"])
|
||||
|
||||
# wait until all tasks are finished
|
||||
for _ in range(4):
|
||||
await asyncio.sleep(0)
|
||||
|
||||
plugin.get_unlocked_achievements.coro.assert_has_calls([call("1"), call("5"), call("9")])
|
||||
game_achievements_import_success.assert_has_calls([
|
||||
call("1", achievements),
|
||||
call("5", [])
|
||||
])
|
||||
game_achievements_import_failure.assert_called_once_with("9", error)
|
||||
achievements_import_finished.assert_called_once_with()
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import asyncio
|
||||
import json
|
||||
from unittest.mock import call
|
||||
|
||||
import pytest
|
||||
from galaxy.api.types import GameTime
|
||||
from galaxy.api.errors import UnknownError
|
||||
from galaxy.api.errors import UnknownError, ImportInProgress, BackendError
|
||||
|
||||
def test_success(plugin, readline, write):
|
||||
request = {
|
||||
@@ -81,3 +83,93 @@ def test_update_game(plugin, write):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_time_import_success(plugin, write):
|
||||
plugin.game_time_import_success(GameTime("3", 60, 1549550504))
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "game_time_import_success",
|
||||
"params": {
|
||||
"game_time": {
|
||||
"game_id": "3",
|
||||
"time_played": 60,
|
||||
"last_played_time": 1549550504
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_time_import_failure(plugin, write):
|
||||
plugin.game_time_import_failure("134", ImportInProgress())
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "game_time_import_failure",
|
||||
"params": {
|
||||
"game_id": "134",
|
||||
"error": {
|
||||
"code": 600,
|
||||
"message": "Import already in progress"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_game_times_import_finished(plugin, write):
|
||||
plugin.game_times_import_finished()
|
||||
response = json.loads(write.call_args[0][0])
|
||||
|
||||
assert response == {
|
||||
"jsonrpc": "2.0",
|
||||
"method": "game_times_import_finished",
|
||||
"params": None
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_game_times_import(plugin, write, mocker):
|
||||
game_time_import_success = mocker.patch.object(plugin, "game_time_import_success")
|
||||
game_time_import_failure = mocker.patch.object(plugin, "game_time_import_failure")
|
||||
game_times_import_finished = mocker.patch.object(plugin, "game_times_import_finished")
|
||||
|
||||
game_ids = ["1", "5"]
|
||||
game_time = GameTime("1", 10, 1549550502)
|
||||
plugin.get_game_times.coro.return_value = [
|
||||
game_time
|
||||
]
|
||||
await plugin.start_game_times_import(game_ids)
|
||||
|
||||
with pytest.raises(ImportInProgress):
|
||||
await plugin.start_game_times_import(["4", "8"])
|
||||
|
||||
# wait until all tasks are finished
|
||||
for _ in range(4):
|
||||
await asyncio.sleep(0)
|
||||
|
||||
plugin.get_game_times.coro.assert_called_once_with()
|
||||
game_time_import_success.assert_called_once_with(game_time)
|
||||
game_time_import_failure.assert_called_once_with("5", UnknownError())
|
||||
game_times_import_finished.assert_called_once_with()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_game_times_import_failure(plugin, write, mocker):
|
||||
game_time_import_failure = mocker.patch.object(plugin, "game_time_import_failure")
|
||||
game_times_import_finished = mocker.patch.object(plugin, "game_times_import_finished")
|
||||
|
||||
game_ids = ["1", "5"]
|
||||
error = BackendError()
|
||||
plugin.get_game_times.coro.side_effect = error
|
||||
|
||||
await plugin.start_game_times_import(game_ids)
|
||||
|
||||
# wait until all tasks are finished
|
||||
for _ in range(4):
|
||||
await asyncio.sleep(0)
|
||||
|
||||
plugin.get_game_times.coro.assert_called_once_with()
|
||||
|
||||
assert game_time_import_failure.mock_calls == [call("1", error), call("5", error)]
|
||||
game_times_import_finished.assert_called_once_with()
|
||||
|
||||
Reference in New Issue
Block a user