mirror of
https://github.com/gogcom/galaxy-integrations-python-api.git
synced 2026-01-01 11:28:12 -05:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c364b716f4 | ||
|
|
48e1782484 | ||
|
|
ff30675a25 | ||
|
|
7b3965ff4b | ||
|
|
2ebdfabd9b | ||
|
|
4e1ea8056d | ||
|
|
67e8681de6 | ||
|
|
77d742ce18 | ||
|
|
692bdbf370 | ||
|
|
207b1e1313 | ||
|
|
05042fe430 | ||
|
|
58b17d94fa | ||
|
|
be03c83d45 | ||
|
|
c07c7a2c2a | ||
|
|
cb1a5fa5e4 | ||
|
|
4790238638 | ||
|
|
5d90ba0c09 | ||
|
|
d74ed3a4b5 | ||
|
|
d6f2d00fb9 | ||
|
|
ce9f33f5d0 | ||
|
|
b28fc60088 | ||
|
|
be3d3bb7e5 | ||
|
|
6dec4a99d3 | ||
|
|
69ffef2fde |
82
PLATFORM_IDs.md
Normal file
82
PLATFORM_IDs.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
### PLATFORM ID LIST
|
||||||
|
|
||||||
|
Platform ID list for GOG Galaxy 2.0 Integrations
|
||||||
|
|
||||||
|
| ID | Name |
|
||||||
|
| --- | --- |
|
||||||
|
| steam | Steam |
|
||||||
|
| psn | PlayStation Network |
|
||||||
|
| xboxone | Xbox Live |
|
||||||
|
| generic | Manually added games |
|
||||||
|
| origin | Origin |
|
||||||
|
| uplay | Uplay |
|
||||||
|
| battlenet | Battle.net |
|
||||||
|
| epic | Epic Games Store |
|
||||||
|
| bethesda | Bethesda.net |
|
||||||
|
| paradox | Paradox Plaza |
|
||||||
|
| humble | Humble Bundle |
|
||||||
|
| kartridge | Kartridge |
|
||||||
|
| itch | Itch.io |
|
||||||
|
| nswitch | Nintendo Switch |
|
||||||
|
| nwiiu | Nintendo Wii U |
|
||||||
|
| nwii | Nintendo Wii |
|
||||||
|
| ncube | Nintendo Game Cube |
|
||||||
|
| riot | Riot |
|
||||||
|
| wargaming | Wargaming |
|
||||||
|
| ngameboy | Nintendo Game Boy |
|
||||||
|
| atari | Atari |
|
||||||
|
| amiga | Amiga |
|
||||||
|
| snes | SNES |
|
||||||
|
| beamdog | Beamdog |
|
||||||
|
| d2d | Direct2Drive |
|
||||||
|
| discord | Discord |
|
||||||
|
| dotemu | DotEmu |
|
||||||
|
| gamehouse | GameHouse |
|
||||||
|
| gmg | Green Man Gaming |
|
||||||
|
| weplay | WePlay |
|
||||||
|
| zx | Zx Spectrum PC |
|
||||||
|
| vision | ColecoVision |
|
||||||
|
| nes | NES |
|
||||||
|
| sms | Sega Master System |
|
||||||
|
| c64 | Commodore 64 |
|
||||||
|
| pce | PC Engine |
|
||||||
|
| segag | Sega Genesis |
|
||||||
|
| neo | NeoGeo |
|
||||||
|
| sega32 | Sega 32X |
|
||||||
|
| segacd | Sega CD |
|
||||||
|
| 3do | 3DO Interactive |
|
||||||
|
| saturn | SegaSaturn |
|
||||||
|
| psx | Sony PlayStation |
|
||||||
|
| ps2 | Sony PlayStation 2 |
|
||||||
|
| n64 | Nintendo64 |
|
||||||
|
| jaguar | Atari Jaguar |
|
||||||
|
| dc | Sega Dreamcast |
|
||||||
|
| xboxog | Original Xbox games |
|
||||||
|
| amazon | Amazon |
|
||||||
|
| gg | GamersGate |
|
||||||
|
| egg | Newegg |
|
||||||
|
| bb | BestBuy |
|
||||||
|
| gameuk | Game UK |
|
||||||
|
| fanatical | Fanatical store |
|
||||||
|
| playasia | PlayAsia |
|
||||||
|
| stadia | Google Stadia |
|
||||||
|
| arc | ARC |
|
||||||
|
| eso | ESO |
|
||||||
|
| glyph | Trion World |
|
||||||
|
| aionl | Aion: Legions of War |
|
||||||
|
| aion | Aion |
|
||||||
|
| blade | Blade and Soul |
|
||||||
|
| gw | Guild Wars |
|
||||||
|
| gw2 | Guild Wars 2 |
|
||||||
|
| lin2 | Lineage 2 |
|
||||||
|
| ffxi | Final Fantasy XI |
|
||||||
|
| ffxiv | Final Fantasy XIV |
|
||||||
|
| totalwar | TotalWar |
|
||||||
|
| winstore | Windows Store |
|
||||||
|
| elites | Elite Dangerous |
|
||||||
|
| star | Star Citizen |
|
||||||
|
| psp | Playstation Portable |
|
||||||
|
| psvita | Playstation Vita |
|
||||||
|
| nds | Nintendo DS |
|
||||||
|
| 3ds | Nintendo 3DS |
|
||||||
|
| pathofexile | Path of Exile |
|
||||||
12
README.md
12
README.md
@@ -20,6 +20,12 @@ The provided features are:
|
|||||||
- receiving and sending chat messages
|
- receiving and sending chat messages
|
||||||
- cache storage
|
- cache storage
|
||||||
|
|
||||||
|
## Platform Id's
|
||||||
|
|
||||||
|
Each integration can implement only one platform. Each integration must declare which platform it's integrating.
|
||||||
|
|
||||||
|
[List of possible Platofrm IDs](PLATFORM_IDs.md)
|
||||||
|
|
||||||
## Basic usage
|
## Basic usage
|
||||||
|
|
||||||
Eeach 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 in the appropriate times.
|
Eeach 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 in the appropriate times.
|
||||||
@@ -58,7 +64,8 @@ if __name__ == "__main__":
|
|||||||
The client has a built-in Python 3.7 interpreter, so the integrations are delivered as python modules.
|
The client has a built-in Python 3.7 interpreter, so the 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 has to contain [manifest.json](#deploy-manifest) and all third-party dependencies. See an [examplary structure](#deploy-structure-example).
|
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 has to contain [manifest.json](#deploy-manifest) and all third-party dependencies. See an [examplary structure](#deploy-structure-example).
|
||||||
|
|
||||||
### <a name="deploy-location"></a> Lookup directory:
|
### Lookup directory
|
||||||
|
<a name="deploy-location"></a>
|
||||||
- Windows:
|
- Windows:
|
||||||
|
|
||||||
`%localappdata%\GOG.com\Galaxy\plugins\installed`
|
`%localappdata%\GOG.com\Galaxy\plugins\installed`
|
||||||
@@ -67,7 +74,8 @@ 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`
|
`~/Library/Application Support/GOG.com/Galaxy/plugins/installed`
|
||||||
|
|
||||||
### <a name="deploy-manifest"></a> Manifest
|
### Manifest
|
||||||
|
<a name="deploy-manifest"></a>
|
||||||
Obligatory JSON file to be placed in a integration folder.
|
Obligatory JSON file to be placed in a integration folder.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ GOG Galaxy Integrations Python API
|
|||||||
|
|
||||||
Overview <overview>
|
Overview <overview>
|
||||||
API <galaxy.api>
|
API <galaxy.api>
|
||||||
|
Platform ID's <platforms>
|
||||||
|
|
||||||
Index
|
Index
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@@ -5,3 +5,11 @@
|
|||||||
|
|
||||||
.. mdinclude:: ../../README.md
|
.. mdinclude:: ../../README.md
|
||||||
:start-line: 6
|
:start-line: 6
|
||||||
|
:end-line: 26
|
||||||
|
|
||||||
|
.. excluding Platforms Id's link
|
||||||
|
|
||||||
|
:ref:`platforms-link`
|
||||||
|
|
||||||
|
.. mdinclude:: ../../README.md
|
||||||
|
:start-line: 28
|
||||||
|
|||||||
2
docs/source/platforms.rst
Normal file
2
docs/source/platforms.rst
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.. _platforms-link:
|
||||||
|
.. mdinclude:: ../../PLATFORM_IDs.md
|
||||||
2
setup.py
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="galaxy.plugin.api",
|
name="galaxy.plugin.api",
|
||||||
version="0.34",
|
version="0.40",
|
||||||
description="GOG Galaxy Integrations Python API",
|
description="GOG Galaxy Integrations Python API",
|
||||||
author='Galaxy team',
|
author='Galaxy team',
|
||||||
author_email='galaxy@gog.com',
|
author_email='galaxy@gog.com',
|
||||||
|
|||||||
@@ -13,7 +13,74 @@ class Platform(Enum):
|
|||||||
Uplay = "uplay"
|
Uplay = "uplay"
|
||||||
Battlenet = "battlenet"
|
Battlenet = "battlenet"
|
||||||
Epic = "epic"
|
Epic = "epic"
|
||||||
|
Bethesda = "bethesda"
|
||||||
|
ParadoxPlaza = "paradox"
|
||||||
|
HumbleBundle = "humble"
|
||||||
|
Kartridge = "kartridge"
|
||||||
|
ItchIo = "itch"
|
||||||
|
NintendoSwitch = "nswitch"
|
||||||
|
NintendoWiiU = "nwiiu"
|
||||||
|
NintendoWii = "nwii"
|
||||||
|
NintendoGameCube = "ncube"
|
||||||
|
RiotGames = "riot"
|
||||||
|
Wargaming = "wargaming"
|
||||||
|
NintendoGameBoy = "ngameboy"
|
||||||
|
Atari = "atari"
|
||||||
|
Amiga = "amiga"
|
||||||
|
SuperNintendoEntertainmentSystem = "snes"
|
||||||
|
Beamdog = "beamdog"
|
||||||
|
Direct2Drive = "d2d"
|
||||||
|
Discord = "discord"
|
||||||
|
DotEmu = "dotemu"
|
||||||
|
GameHouse = "gamehouse"
|
||||||
|
GreenManGaming = "gmg"
|
||||||
|
WePlay = "weplay"
|
||||||
|
ZxSpectrum = "zx"
|
||||||
|
ColecoVision = "vision"
|
||||||
|
NintendoEntertainmentSystem = "nes"
|
||||||
|
SegaMasterSystem = "sms"
|
||||||
|
Commodore64 = "c64"
|
||||||
|
PcEngine = "pce"
|
||||||
|
SegaGenesis = "segag"
|
||||||
|
NeoGeo = "neo"
|
||||||
|
Sega32X = "sega32"
|
||||||
|
SegaCd = "segacd"
|
||||||
|
_3Do = "3do"
|
||||||
|
SegaSaturn = "saturn"
|
||||||
|
PlayStation = "psx"
|
||||||
|
PlayStation2 = "ps2"
|
||||||
|
Nintendo64 = "n64"
|
||||||
|
AtariJaguar = "jaguar"
|
||||||
|
SegaDreamcast = "dc"
|
||||||
|
Xbox = "xboxog"
|
||||||
|
Amazon = "amazon"
|
||||||
|
GamersGate = "gg"
|
||||||
|
Newegg = "egg"
|
||||||
|
BestBuy = "bb"
|
||||||
|
GameUk = "gameuk"
|
||||||
|
Fanatical = "fanatical"
|
||||||
|
PlayAsia = "playasia"
|
||||||
|
Stadia = "stadia"
|
||||||
|
Arc = "arc"
|
||||||
|
ElderScrollsOnline = "eso"
|
||||||
|
Glyph = "glyph"
|
||||||
|
AionLegionsOfWar = "aionl"
|
||||||
|
Aion = "aion"
|
||||||
|
BladeAndSoul = "blade"
|
||||||
|
GuildWars = "gw"
|
||||||
|
GuildWars2 = "gw2"
|
||||||
|
Lineage2 = "lin2"
|
||||||
|
FinalFantasy11 = "ffxi"
|
||||||
|
FinalFantasy14 = "ffxiv"
|
||||||
|
TotalWar = "totalwar"
|
||||||
|
WindowsStore = "winstore"
|
||||||
|
EliteDangerous = "elites"
|
||||||
|
StarCitizen = "star"
|
||||||
|
PlayStationPortable = "psp"
|
||||||
|
PlayStationVita = "psvita"
|
||||||
|
NintendoDs = "nds"
|
||||||
|
Nintendo3Ds = "3ds"
|
||||||
|
PathOfExile = "pathofexile"
|
||||||
|
|
||||||
class Feature(Enum):
|
class Feature(Enum):
|
||||||
"""Possible features that can be implemented by an integration.
|
"""Possible features that can be implemented by an integration.
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import logging
|
|||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from galaxy.reader import StreamLineReader
|
||||||
|
|
||||||
class JsonRpcError(Exception):
|
class JsonRpcError(Exception):
|
||||||
def __init__(self, code, message, data=None):
|
def __init__(self, code, message, data=None):
|
||||||
self.code = code
|
self.code = code
|
||||||
@@ -67,7 +69,7 @@ def anonymise_sensitive_params(params, sensitive_params):
|
|||||||
class Server():
|
class Server():
|
||||||
def __init__(self, reader, writer, encoder=json.JSONEncoder()):
|
def __init__(self, reader, writer, encoder=json.JSONEncoder()):
|
||||||
self._active = True
|
self._active = True
|
||||||
self._reader = reader
|
self._reader = StreamLineReader(reader)
|
||||||
self._writer = writer
|
self._writer = writer
|
||||||
self._encoder = encoder
|
self._encoder = encoder
|
||||||
self._methods = {}
|
self._methods = {}
|
||||||
@@ -114,6 +116,7 @@ class Server():
|
|||||||
data = data.strip()
|
data = data.strip()
|
||||||
logging.debug("Received %d bytes of data", len(data))
|
logging.debug("Received %d bytes of data", len(data))
|
||||||
self._handle_input(data)
|
self._handle_input(data)
|
||||||
|
await asyncio.sleep(0) # To not starve task queue
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._active = False
|
self._active = False
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ from enum import Enum
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from typing import List, Dict
|
from typing import Any, List, Dict, Optional, Union
|
||||||
|
|
||||||
from galaxy.api.types import Achievement, Game, LocalGame, FriendInfo, GameTime, UserInfo, Room
|
from galaxy.api.types import Achievement, Game, LocalGame, FriendInfo, GameTime, UserInfo, Room
|
||||||
|
|
||||||
from galaxy.api.jsonrpc import Server, NotificationClient, ApplicationError
|
from galaxy.api.jsonrpc import Server, NotificationClient, ApplicationError
|
||||||
from galaxy.api.consts import Feature
|
from galaxy.api.consts import Feature
|
||||||
from galaxy.api.errors import UnknownError, ImportInProgress
|
from galaxy.api.errors import UnknownError, ImportInProgress
|
||||||
|
from galaxy.api.types import Authentication, NextStep, Message
|
||||||
|
|
||||||
|
|
||||||
class JSONEncoder(json.JSONEncoder):
|
class JSONEncoder(json.JSONEncoder):
|
||||||
@@ -37,6 +38,7 @@ class Plugin:
|
|||||||
|
|
||||||
self._feature_methods = OrderedDict()
|
self._feature_methods = OrderedDict()
|
||||||
self._active = True
|
self._active = True
|
||||||
|
self._pass_control_task = None
|
||||||
|
|
||||||
self._reader, self._writer = reader, writer
|
self._reader, self._writer = reader, writer
|
||||||
self._handshake_token = handshake_token
|
self._handshake_token = handshake_token
|
||||||
@@ -57,7 +59,12 @@ class Plugin:
|
|||||||
# internal
|
# internal
|
||||||
self._register_method("shutdown", self._shutdown, internal=True)
|
self._register_method("shutdown", self._shutdown, internal=True)
|
||||||
self._register_method("get_capabilities", self._get_capabilities, internal=True)
|
self._register_method("get_capabilities", self._get_capabilities, internal=True)
|
||||||
self._register_method("initialize_cache", self._initialize_cache, internal=True)
|
self._register_method(
|
||||||
|
"initialize_cache",
|
||||||
|
self._initialize_cache,
|
||||||
|
internal=True,
|
||||||
|
sensitive_params="data"
|
||||||
|
)
|
||||||
self._register_method("ping", self._ping, internal=True)
|
self._register_method("ping", self._ping, internal=True)
|
||||||
|
|
||||||
# implemented by developer
|
# implemented by developer
|
||||||
@@ -204,15 +211,17 @@ class Plugin:
|
|||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
"""Plugin's main coroutine."""
|
"""Plugin's main coroutine."""
|
||||||
async def pass_control():
|
await self._server.run()
|
||||||
while self._active:
|
if self._pass_control_task is not None:
|
||||||
try:
|
await self._pass_control_task
|
||||||
self.tick()
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Unexpected exception raised in plugin tick")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
await asyncio.gather(pass_control(), self._server.run())
|
async def _pass_control(self):
|
||||||
|
while self._active:
|
||||||
|
try:
|
||||||
|
self.tick()
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Unexpected exception raised in plugin tick")
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
def _shutdown(self):
|
def _shutdown(self):
|
||||||
logging.info("Shutting down")
|
logging.info("Shutting down")
|
||||||
@@ -230,13 +239,14 @@ class Plugin:
|
|||||||
def _initialize_cache(self, data: Dict):
|
def _initialize_cache(self, data: Dict):
|
||||||
self._persistent_cache = data
|
self._persistent_cache = data
|
||||||
self.handshake_complete()
|
self.handshake_complete()
|
||||||
|
self._pass_control_task = asyncio.create_task(self._pass_control())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ping():
|
def _ping():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# notifications
|
# notifications
|
||||||
def store_credentials(self, credentials: dict):
|
def store_credentials(self, credentials: Dict[str, Any]) -> None:
|
||||||
"""Notify the client to store authentication credentials.
|
"""Notify the client to store authentication credentials.
|
||||||
Credentials are passed on the next authenticate call.
|
Credentials are passed on the next authenticate call.
|
||||||
|
|
||||||
@@ -260,7 +270,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
self._notification_client.notify("store_credentials", credentials, sensitive_params=True)
|
self._notification_client.notify("store_credentials", credentials, sensitive_params=True)
|
||||||
|
|
||||||
def add_game(self, game: Game):
|
def add_game(self, game: Game) -> None:
|
||||||
"""Notify the client to add game to the list of owned games
|
"""Notify the client to add game to the list of owned games
|
||||||
of the currently authenticated user.
|
of the currently authenticated user.
|
||||||
|
|
||||||
@@ -282,7 +292,7 @@ class Plugin:
|
|||||||
params = {"owned_game": game}
|
params = {"owned_game": game}
|
||||||
self._notification_client.notify("owned_game_added", params)
|
self._notification_client.notify("owned_game_added", params)
|
||||||
|
|
||||||
def remove_game(self, game_id: str):
|
def remove_game(self, game_id: str) -> None:
|
||||||
"""Notify the client to remove game from the list of owned games
|
"""Notify the client to remove game from the list of owned games
|
||||||
of the currently authenticated user.
|
of the currently authenticated user.
|
||||||
|
|
||||||
@@ -304,7 +314,7 @@ class Plugin:
|
|||||||
params = {"game_id": game_id}
|
params = {"game_id": game_id}
|
||||||
self._notification_client.notify("owned_game_removed", params)
|
self._notification_client.notify("owned_game_removed", params)
|
||||||
|
|
||||||
def update_game(self, game: Game):
|
def update_game(self, game: Game) -> None:
|
||||||
"""Notify the client to update the status of a game
|
"""Notify the client to update the status of a game
|
||||||
owned by the currently authenticated user.
|
owned by the currently authenticated user.
|
||||||
|
|
||||||
@@ -313,7 +323,7 @@ class Plugin:
|
|||||||
params = {"owned_game": game}
|
params = {"owned_game": game}
|
||||||
self._notification_client.notify("owned_game_updated", params)
|
self._notification_client.notify("owned_game_updated", params)
|
||||||
|
|
||||||
def unlock_achievement(self, game_id: str, achievement: Achievement):
|
def unlock_achievement(self, game_id: str, achievement: Achievement) -> None:
|
||||||
"""Notify the client to unlock an achievement for a specific game.
|
"""Notify the client to unlock an achievement for a specific game.
|
||||||
|
|
||||||
:param game_id: game_id of the game for which to unlock an achievement.
|
:param game_id: game_id of the game for which to unlock an achievement.
|
||||||
@@ -325,7 +335,7 @@ class Plugin:
|
|||||||
}
|
}
|
||||||
self._notification_client.notify("achievement_unlocked", params)
|
self._notification_client.notify("achievement_unlocked", params)
|
||||||
|
|
||||||
def game_achievements_import_success(self, game_id: str, achievements):
|
def game_achievements_import_success(self, game_id: str, achievements: List[Achievement]) -> None:
|
||||||
"""Notify the client that import of achievements for a given game has succeeded.
|
"""Notify the client that import of achievements for a given game has succeeded.
|
||||||
This method is called by import_games_achievements.
|
This method is called by import_games_achievements.
|
||||||
|
|
||||||
@@ -338,7 +348,7 @@ class Plugin:
|
|||||||
}
|
}
|
||||||
self._notification_client.notify("game_achievements_import_success", params)
|
self._notification_client.notify("game_achievements_import_success", params)
|
||||||
|
|
||||||
def game_achievements_import_failure(self, game_id: str, error: ApplicationError):
|
def game_achievements_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
||||||
"""Notify the client that import of achievements for a given game has failed.
|
"""Notify the client that import of achievements for a given game has failed.
|
||||||
This method is called by import_games_achievements.
|
This method is called by import_games_achievements.
|
||||||
|
|
||||||
@@ -354,12 +364,12 @@ class Plugin:
|
|||||||
}
|
}
|
||||||
self._notification_client.notify("game_achievements_import_failure", params)
|
self._notification_client.notify("game_achievements_import_failure", params)
|
||||||
|
|
||||||
def achievements_import_finished(self):
|
def achievements_import_finished(self) -> None:
|
||||||
"""Notify the client that importing achievements has finished.
|
"""Notify the client that importing achievements has finished.
|
||||||
This method is called by import_games_achievements_task"""
|
This method is called by import_games_achievements_task"""
|
||||||
self._notification_client.notify("achievements_import_finished", None)
|
self._notification_client.notify("achievements_import_finished", None)
|
||||||
|
|
||||||
def update_local_game_status(self, local_game: LocalGame):
|
def update_local_game_status(self, local_game: LocalGame) -> None:
|
||||||
"""Notify the client to update the status of a local game.
|
"""Notify the client to update the status of a local game.
|
||||||
|
|
||||||
:param local_game: the LocalGame to update
|
:param local_game: the LocalGame to update
|
||||||
@@ -385,7 +395,7 @@ class Plugin:
|
|||||||
params = {"local_game": local_game}
|
params = {"local_game": local_game}
|
||||||
self._notification_client.notify("local_game_status_changed", params)
|
self._notification_client.notify("local_game_status_changed", params)
|
||||||
|
|
||||||
def add_friend(self, user: FriendInfo):
|
def add_friend(self, user: FriendInfo) -> None:
|
||||||
"""Notify the client to add a user to friends list of the currently authenticated user.
|
"""Notify the client to add a user to friends list of the currently authenticated user.
|
||||||
|
|
||||||
:param user: FriendInfo of a user that the client will add to friends list
|
:param user: FriendInfo of a user that the client will add to friends list
|
||||||
@@ -393,7 +403,7 @@ class Plugin:
|
|||||||
params = {"friend_info": user}
|
params = {"friend_info": user}
|
||||||
self._notification_client.notify("friend_added", params)
|
self._notification_client.notify("friend_added", params)
|
||||||
|
|
||||||
def remove_friend(self, user_id: str):
|
def remove_friend(self, user_id: str) -> None:
|
||||||
"""Notify the client to remove a user from friends list of the currently authenticated user.
|
"""Notify the client to remove a user from friends list of the currently authenticated user.
|
||||||
|
|
||||||
:param user_id: id of the user to remove from friends list
|
:param user_id: id of the user to remove from friends list
|
||||||
@@ -401,7 +411,12 @@ class Plugin:
|
|||||||
params = {"user_id": user_id}
|
params = {"user_id": user_id}
|
||||||
self._notification_client.notify("friend_removed", params)
|
self._notification_client.notify("friend_removed", params)
|
||||||
|
|
||||||
def update_room(self, room_id: str, unread_message_count=None, new_messages=None):
|
def update_room(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
unread_message_count: Optional[int]=None,
|
||||||
|
new_messages: Optional[List[Message]]=None
|
||||||
|
) -> None:
|
||||||
"""WIP, Notify the client to update the information regarding
|
"""WIP, Notify the client to update the information regarding
|
||||||
a chat room that the currently authenticated user is in.
|
a chat room that the currently authenticated user is in.
|
||||||
|
|
||||||
@@ -416,7 +431,7 @@ class Plugin:
|
|||||||
params["messages"] = new_messages
|
params["messages"] = new_messages
|
||||||
self._notification_client.notify("chat_room_updated", params)
|
self._notification_client.notify("chat_room_updated", params)
|
||||||
|
|
||||||
def update_game_time(self, game_time: GameTime):
|
def update_game_time(self, game_time: GameTime) -> None:
|
||||||
"""Notify the client to update game time for a game.
|
"""Notify the client to update game time for a game.
|
||||||
|
|
||||||
:param game_time: game time to update
|
:param game_time: game time to update
|
||||||
@@ -424,7 +439,7 @@ class Plugin:
|
|||||||
params = {"game_time": game_time}
|
params = {"game_time": game_time}
|
||||||
self._notification_client.notify("game_time_updated", params)
|
self._notification_client.notify("game_time_updated", params)
|
||||||
|
|
||||||
def game_time_import_success(self, game_time: GameTime):
|
def game_time_import_success(self, game_time: GameTime) -> None:
|
||||||
"""Notify the client that import of a given game_time has succeeded.
|
"""Notify the client that import of a given game_time has succeeded.
|
||||||
This method is called by import_game_times.
|
This method is called by import_game_times.
|
||||||
|
|
||||||
@@ -433,7 +448,7 @@ class Plugin:
|
|||||||
params = {"game_time": game_time}
|
params = {"game_time": game_time}
|
||||||
self._notification_client.notify("game_time_import_success", params)
|
self._notification_client.notify("game_time_import_success", params)
|
||||||
|
|
||||||
def game_time_import_failure(self, game_id: str, error: ApplicationError):
|
def game_time_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
||||||
"""Notify the client that import of a game time for a given game has failed.
|
"""Notify the client that import of a game time for a given game has failed.
|
||||||
This method is called by import_game_times.
|
This method is called by import_game_times.
|
||||||
|
|
||||||
@@ -449,35 +464,36 @@ class Plugin:
|
|||||||
}
|
}
|
||||||
self._notification_client.notify("game_time_import_failure", params)
|
self._notification_client.notify("game_time_import_failure", params)
|
||||||
|
|
||||||
def game_times_import_finished(self):
|
def game_times_import_finished(self) -> None:
|
||||||
"""Notify the client that importing game times has finished.
|
"""Notify the client that importing game times has finished.
|
||||||
This method is called by :meth:`~.import_game_times_task`.
|
This method is called by :meth:`~.import_game_times_task`.
|
||||||
"""
|
"""
|
||||||
self._notification_client.notify("game_times_import_finished", None)
|
self._notification_client.notify("game_times_import_finished", None)
|
||||||
|
|
||||||
def lost_authentication(self):
|
def lost_authentication(self) -> None:
|
||||||
"""Notify the client that integration has lost authentication for the
|
"""Notify the client that integration has lost authentication for the
|
||||||
current user and is unable to perform actions which would require it.
|
current user and is unable to perform actions which would require it.
|
||||||
"""
|
"""
|
||||||
self._notification_client.notify("authentication_lost", None)
|
self._notification_client.notify("authentication_lost", None)
|
||||||
|
|
||||||
def push_cache(self):
|
def push_cache(self) -> None:
|
||||||
"""Push local copy of the persistent cache to the GOG Galaxy Client replacing existing one.
|
"""Push local copy of the persistent cache to the GOG Galaxy Client replacing existing one.
|
||||||
"""
|
"""
|
||||||
self._notification_client.notify(
|
self._notification_client.notify(
|
||||||
"push_cache",
|
"push_cache",
|
||||||
params={"data": self._persistent_cache}
|
params={"data": self._persistent_cache},
|
||||||
|
sensitive_params="data"
|
||||||
)
|
)
|
||||||
|
|
||||||
# handlers
|
# handlers
|
||||||
def handshake_complete(self):
|
def handshake_complete(self) -> None:
|
||||||
"""This method is called right after the handshake with the GOG Galaxy Client is complete and
|
"""This method is called right after the handshake with the GOG Galaxy Client is complete and
|
||||||
before any other operations are called by the GOG Galaxy Client.
|
before any other operations are called by the GOG Galaxy Client.
|
||||||
Persistent cache is available when this method is called.
|
Persistent cache is available when this method is called.
|
||||||
Override it if you need to do additional plugin initializations.
|
Override it if you need to do additional plugin initializations.
|
||||||
This method is called internally."""
|
This method is called internally."""
|
||||||
|
|
||||||
def tick(self):
|
def tick(self) -> None:
|
||||||
"""This method is called periodically.
|
"""This method is called periodically.
|
||||||
Override it to implement periodical non-blocking tasks.
|
Override it to implement periodical non-blocking tasks.
|
||||||
This method is called internally.
|
This method is called internally.
|
||||||
@@ -497,13 +513,13 @@ class Plugin:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def shutdown(self):
|
def shutdown(self) -> None:
|
||||||
"""This method is called on integration shutdown.
|
"""This method is called on integration shutdown.
|
||||||
Override it to implement tear down.
|
Override it to implement tear down.
|
||||||
This method is called by the GOG Galaxy Client."""
|
This method is called by the GOG Galaxy Client."""
|
||||||
|
|
||||||
# methods
|
# methods
|
||||||
async def authenticate(self, stored_credentials: dict = None):
|
async def authenticate(self, stored_credentials: Optional[Dict] = None) -> Union[NextStep, Authentication]:
|
||||||
"""Override this method to handle user authentication.
|
"""Override this method to handle user authentication.
|
||||||
This method should either return :class:`~galaxy.api.types.Authentication` if the authentication is finished
|
This method should either return :class:`~galaxy.api.types.Authentication` if the authentication is finished
|
||||||
or :class:`~galaxy.api.types.NextStep` if it requires going to another url.
|
or :class:`~galaxy.api.types.NextStep` if it requires going to another url.
|
||||||
@@ -531,7 +547,8 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def pass_login_credentials(self, step: str, credentials: Dict[str, str], cookies: List[Dict[str, str]]):
|
async def pass_login_credentials(self, step: str, credentials: Dict[str, str], cookies: List[Dict[str, str]]) \
|
||||||
|
-> Union[NextStep, Authentication]:
|
||||||
"""This method is called if we return galaxy.api.types.NextStep from authenticate or from pass_login_credentials.
|
"""This method is called if we return galaxy.api.types.NextStep from authenticate or from pass_login_credentials.
|
||||||
This method's parameters provide the data extracted from the web page navigation that previous NextStep finished on.
|
This method's parameters provide the data extracted from the web page navigation that previous NextStep finished on.
|
||||||
This method should either return galaxy.api.types.Authentication if the authentication is finished
|
This method should either return galaxy.api.types.Authentication if the authentication is finished
|
||||||
@@ -586,7 +603,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def start_achievements_import(self, game_ids: List[str]):
|
async def start_achievements_import(self, game_ids: List[str]) -> None:
|
||||||
"""Starts the task of importing achievements.
|
"""Starts the task of importing achievements.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -605,7 +622,7 @@ class Plugin:
|
|||||||
asyncio.create_task(import_games_achievements_task(game_ids))
|
asyncio.create_task(import_games_achievements_task(game_ids))
|
||||||
self._achievements_import_in_progress = True
|
self._achievements_import_in_progress = True
|
||||||
|
|
||||||
async def import_games_achievements(self, game_ids: List[str]):
|
async def import_games_achievements(self, game_ids: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Override this method to return the unlocked achievements
|
Override this method to return the unlocked achievements
|
||||||
of the user that is currently logged in to the plugin.
|
of the user that is currently logged in to the plugin.
|
||||||
@@ -646,7 +663,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def launch_game(self, game_id: str):
|
async def launch_game(self, game_id: str) -> None:
|
||||||
"""Override this method to launch the game
|
"""Override this method to launch the game
|
||||||
identified by the provided game_id.
|
identified by the provided game_id.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
@@ -664,7 +681,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def install_game(self, game_id: str):
|
async def install_game(self, game_id: str) -> None:
|
||||||
"""Override this method to install the game
|
"""Override this method to install the game
|
||||||
identified by the provided game_id.
|
identified by the provided game_id.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
@@ -682,7 +699,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def uninstall_game(self, game_id: str):
|
async def uninstall_game(self, game_id: str) -> None:
|
||||||
"""Override this method to uninstall the game
|
"""Override this method to uninstall the game
|
||||||
identified by the provided game_id.
|
identified by the provided game_id.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
@@ -728,7 +745,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def send_message(self, room_id: str, message_text: str):
|
async def send_message(self, room_id: str, message_text: str) -> None:
|
||||||
"""WIP, Override this method to send message to a chat room.
|
"""WIP, Override this method to send message to a chat room.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -737,7 +754,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def mark_as_read(self, room_id: str, last_message_id: str):
|
async def mark_as_read(self, room_id: str, last_message_id: str) -> None:
|
||||||
"""WIP, Override this method to mark messages in a chat room as read up to the id provided in the parameter.
|
"""WIP, Override this method to mark messages in a chat room as read up to the id provided in the parameter.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -753,7 +770,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def get_room_history_from_message(self, room_id: str, message_id: str):
|
async def get_room_history_from_message(self, room_id: str, message_id: str) -> List[Message]:
|
||||||
"""WIP, Override this method to return the chat room history since the message provided in parameter.
|
"""WIP, Override this method to return the chat room history since the message provided in parameter.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -762,7 +779,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def get_room_history_from_timestamp(self, room_id: str, from_timestamp: int):
|
async def get_room_history_from_timestamp(self, room_id: str, from_timestamp: int) -> List[Message]:
|
||||||
"""WIP, Override this method to return the chat room history since the timestamp provided in parameter.
|
"""WIP, Override this method to return the chat room history since the timestamp provided in parameter.
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -778,7 +795,7 @@ class Plugin:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
async def start_game_times_import(self, game_ids: List[str]):
|
async def start_game_times_import(self, game_ids: List[str]) -> None:
|
||||||
"""Starts the task of importing game times
|
"""Starts the task of importing game times
|
||||||
This method is called by the GOG Galaxy Client.
|
This method is called by the GOG Galaxy Client.
|
||||||
|
|
||||||
@@ -797,7 +814,7 @@ class Plugin:
|
|||||||
asyncio.create_task(import_game_times_task(game_ids))
|
asyncio.create_task(import_game_times_task(game_ids))
|
||||||
self._game_times_import_in_progress = True
|
self._game_times_import_in_progress = True
|
||||||
|
|
||||||
async def import_game_times(self, game_ids: List[str]):
|
async def import_game_times(self, game_ids: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Override this method to return game times for
|
Override this method to return game times for
|
||||||
games owned by the currently authenticated user.
|
games owned by the currently authenticated user.
|
||||||
|
|||||||
@@ -204,5 +204,5 @@ class GameTime():
|
|||||||
:param last_time_played: last time the game was played (**unix timestamp**)
|
:param last_time_played: last time the game was played (**unix timestamp**)
|
||||||
"""
|
"""
|
||||||
game_id: str
|
game_id: str
|
||||||
time_played: int
|
time_played: Optional[int]
|
||||||
last_played_time: int
|
last_played_time: Optional[int]
|
||||||
|
|||||||
28
src/galaxy/reader.py
Normal file
28
src/galaxy/reader.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from asyncio import StreamReader
|
||||||
|
|
||||||
|
|
||||||
|
class StreamLineReader:
|
||||||
|
"""Handles StreamReader readline without buffer limit"""
|
||||||
|
def __init__(self, reader: StreamReader):
|
||||||
|
self._reader = reader
|
||||||
|
self._buffer = bytes()
|
||||||
|
self._processed_buffer_it = 0
|
||||||
|
|
||||||
|
async def readline(self):
|
||||||
|
while True:
|
||||||
|
# check if there is no unprocessed data in the buffer
|
||||||
|
if not self._buffer or self._processed_buffer_it != 0:
|
||||||
|
chunk = await self._reader.read(1024)
|
||||||
|
if not chunk:
|
||||||
|
return bytes() # EOF
|
||||||
|
self._buffer += chunk
|
||||||
|
|
||||||
|
it = self._buffer.find(b"\n", self._processed_buffer_it)
|
||||||
|
if it < 0:
|
||||||
|
self._processed_buffer_it = len(self._buffer)
|
||||||
|
continue
|
||||||
|
|
||||||
|
line = self._buffer[:it]
|
||||||
|
self._buffer = self._buffer[it+1:]
|
||||||
|
self._processed_buffer_it = 0
|
||||||
|
return line
|
||||||
@@ -11,7 +11,7 @@ from galaxy.unittest.mock import AsyncMock, coroutine_mock
|
|||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def reader():
|
def reader():
|
||||||
stream = MagicMock(name="stream_reader")
|
stream = MagicMock(name="stream_reader")
|
||||||
stream.readline = AsyncMock()
|
stream.read = AsyncMock()
|
||||||
yield stream
|
yield stream
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
@@ -22,8 +22,8 @@ def writer():
|
|||||||
yield stream
|
yield stream
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def readline(reader):
|
def read(reader):
|
||||||
yield reader.readline
|
yield reader.read
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def write(writer):
|
def write(writer):
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def test_initialization_no_id_nor_name():
|
|||||||
with raises(AssertionError):
|
with raises(AssertionError):
|
||||||
Achievement(unlock_time=1234567890)
|
Achievement(unlock_time=1234567890)
|
||||||
|
|
||||||
def test_success(plugin, readline, write):
|
def test_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
@@ -25,7 +25,7 @@ def test_success(plugin, readline, write):
|
|||||||
"game_id": "14"
|
"game_id": "14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_unlocked_achievements.coro.return_value = [
|
plugin.get_unlocked_achievements.coro.return_value = [
|
||||||
Achievement(achievement_id="lvl10", unlock_time=1548421241),
|
Achievement(achievement_id="lvl10", unlock_time=1548421241),
|
||||||
Achievement(achievement_name="Got level 20", unlock_time=1548422395),
|
Achievement(achievement_name="Got level 20", unlock_time=1548422395),
|
||||||
@@ -57,7 +57,7 @@ def test_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_failure(plugin, readline, write):
|
def test_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
@@ -67,7 +67,7 @@ def test_failure(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_unlocked_achievements.coro.side_effect = UnknownError()
|
plugin.get_unlocked_achievements.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_unlocked_achievements.assert_called()
|
plugin.get_unlocked_achievements.assert_called()
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ from galaxy.api.errors import (
|
|||||||
BackendNotAvailable, BackendTimeout, BackendError, TemporaryBlocked, Banned, AccessDenied
|
BackendNotAvailable, BackendTimeout, BackendError, TemporaryBlocked, Banned, AccessDenied
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_success(plugin, readline, write):
|
def test_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "init_authentication"
|
"method": "init_authentication"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
|
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.authenticate.assert_called_with()
|
plugin.authenticate.assert_called_with()
|
||||||
@@ -44,14 +44,14 @@ def test_success(plugin, readline, write):
|
|||||||
pytest.param(Banned, 105, "Banned", id="banned"),
|
pytest.param(Banned, 105, "Banned", id="banned"),
|
||||||
pytest.param(AccessDenied, 106, "Access denied", id="access_denied"),
|
pytest.param(AccessDenied, 106, "Access denied", id="access_denied"),
|
||||||
])
|
])
|
||||||
def test_failure(plugin, readline, write, error, code, message):
|
def test_failure(plugin, read, write, error, code, message):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "init_authentication"
|
"method": "init_authentication"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.authenticate.coro.side_effect = error()
|
plugin.authenticate.coro.side_effect = error()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.authenticate.assert_called_with()
|
plugin.authenticate.assert_called_with()
|
||||||
@@ -66,7 +66,7 @@ def test_failure(plugin, readline, write, error, code, message):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_stored_credentials(plugin, readline, write):
|
def test_stored_credentials(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
@@ -77,7 +77,7 @@ def test_stored_credentials(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
|
plugin.authenticate.coro.return_value = Authentication("132", "Zenek")
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
|
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
|
||||||
@@ -100,7 +100,7 @@ def test_store_credentials(plugin, write):
|
|||||||
"params": credentials
|
"params": credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_lost_authentication(plugin, readline, write):
|
def test_lost_authentication(plugin, write):
|
||||||
|
|
||||||
async def couritine():
|
async def couritine():
|
||||||
plugin.lost_authentication()
|
plugin.lost_authentication()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from galaxy.api.errors import (
|
|||||||
TooManyMessagesSent, IncoherentLastMessage, MessageNotFound
|
TooManyMessagesSent, IncoherentLastMessage, MessageNotFound
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_send_message_success(plugin, readline, write):
|
def test_send_message_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
@@ -20,7 +20,7 @@ def test_send_message_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.send_message.coro.return_value = None
|
plugin.send_message.coro.return_value = None
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.send_message.assert_called_with(room_id="14", message="Hello!")
|
plugin.send_message.assert_called_with(room_id="14", message="Hello!")
|
||||||
@@ -40,7 +40,7 @@ def test_send_message_success(plugin, readline, write):
|
|||||||
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
|
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
|
||||||
pytest.param(TooManyMessagesSent, 300, "Too many messages sent", id="too_many_messages")
|
pytest.param(TooManyMessagesSent, 300, "Too many messages sent", id="too_many_messages")
|
||||||
])
|
])
|
||||||
def test_send_message_failure(plugin, readline, write, error, code, message):
|
def test_send_message_failure(plugin, read, write, error, code, message):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "6",
|
"id": "6",
|
||||||
@@ -51,7 +51,7 @@ def test_send_message_failure(plugin, readline, write, error, code, message):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.send_message.coro.side_effect = error()
|
plugin.send_message.coro.side_effect = error()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.send_message.assert_called_with(room_id="15", message="Bye")
|
plugin.send_message.assert_called_with(room_id="15", message="Bye")
|
||||||
@@ -66,7 +66,7 @@ def test_send_message_failure(plugin, readline, write, error, code, message):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_mark_as_read_success(plugin, readline, write):
|
def test_mark_as_read_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "7",
|
"id": "7",
|
||||||
@@ -77,7 +77,7 @@ def test_mark_as_read_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.mark_as_read.coro.return_value = None
|
plugin.mark_as_read.coro.return_value = None
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.mark_as_read.assert_called_with(room_id="14", last_message_id="67")
|
plugin.mark_as_read.assert_called_with(room_id="14", last_message_id="67")
|
||||||
@@ -102,7 +102,7 @@ def test_mark_as_read_success(plugin, readline, write):
|
|||||||
id="incoherent_last_message"
|
id="incoherent_last_message"
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
def test_mark_as_read_failure(plugin, readline, write, error, code, message):
|
def test_mark_as_read_failure(plugin, read, write, error, code, message):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "4",
|
"id": "4",
|
||||||
@@ -113,7 +113,7 @@ def test_mark_as_read_failure(plugin, readline, write, error, code, message):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.mark_as_read.coro.side_effect = error()
|
plugin.mark_as_read.coro.side_effect = error()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.mark_as_read.assert_called_with(room_id="18", last_message_id="7")
|
plugin.mark_as_read.assert_called_with(room_id="18", last_message_id="7")
|
||||||
@@ -128,14 +128,14 @@ def test_mark_as_read_failure(plugin, readline, write, error, code, message):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_get_rooms_success(plugin, readline, write):
|
def test_get_rooms_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "2",
|
"id": "2",
|
||||||
"method": "import_rooms"
|
"method": "import_rooms"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_rooms.coro.return_value = [
|
plugin.get_rooms.coro.return_value = [
|
||||||
Room("13", 0, None),
|
Room("13", 0, None),
|
||||||
Room("15", 34, "8")
|
Room("15", 34, "8")
|
||||||
@@ -162,14 +162,14 @@ def test_get_rooms_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_get_rooms_failure(plugin, readline, write):
|
def test_get_rooms_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "9",
|
"id": "9",
|
||||||
"method": "import_rooms"
|
"method": "import_rooms"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_rooms.coro.side_effect = UnknownError()
|
plugin.get_rooms.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_rooms.assert_called_with()
|
plugin.get_rooms.assert_called_with()
|
||||||
@@ -184,7 +184,7 @@ def test_get_rooms_failure(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_get_room_history_from_message_success(plugin, readline, write):
|
def test_get_room_history_from_message_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "2",
|
"id": "2",
|
||||||
@@ -195,7 +195,7 @@ def test_get_room_history_from_message_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_room_history_from_message.coro.return_value = [
|
plugin.get_room_history_from_message.coro.return_value = [
|
||||||
Message("13", "149", 1549454837, "Hello"),
|
Message("13", "149", 1549454837, "Hello"),
|
||||||
Message("14", "812", 1549454899, "Hi")
|
Message("14", "812", 1549454899, "Hi")
|
||||||
@@ -233,7 +233,7 @@ def test_get_room_history_from_message_success(plugin, readline, write):
|
|||||||
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
|
pytest.param(BackendError, 4, "Backend error", id="backend_error"),
|
||||||
pytest.param(MessageNotFound, 500, "Message not found", id="message_not_found")
|
pytest.param(MessageNotFound, 500, "Message not found", id="message_not_found")
|
||||||
])
|
])
|
||||||
def test_get_room_history_from_message_failure(plugin, readline, write, error, code, message):
|
def test_get_room_history_from_message_failure(plugin, read, write, error, code, message):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "7",
|
"id": "7",
|
||||||
@@ -244,7 +244,7 @@ def test_get_room_history_from_message_failure(plugin, readline, write, error, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_room_history_from_message.coro.side_effect = error()
|
plugin.get_room_history_from_message.coro.side_effect = error()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_room_history_from_message.assert_called_with(room_id="33", message_id="88")
|
plugin.get_room_history_from_message.assert_called_with(room_id="33", message_id="88")
|
||||||
@@ -259,7 +259,7 @@ def test_get_room_history_from_message_failure(plugin, readline, write, error, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_get_room_history_from_timestamp_success(plugin, readline, write):
|
def test_get_room_history_from_timestamp_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "7",
|
"id": "7",
|
||||||
@@ -270,7 +270,7 @@ def test_get_room_history_from_timestamp_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_room_history_from_timestamp.coro.return_value = [
|
plugin.get_room_history_from_timestamp.coro.return_value = [
|
||||||
Message("12", "155", 1549454836, "Bye")
|
Message("12", "155", 1549454836, "Bye")
|
||||||
]
|
]
|
||||||
@@ -296,7 +296,7 @@ def test_get_room_history_from_timestamp_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_get_room_history_from_timestamp_failure(plugin, readline, write):
|
def test_get_room_history_from_timestamp_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
@@ -307,7 +307,7 @@ def test_get_room_history_from_timestamp_failure(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_room_history_from_timestamp.coro.side_effect = UnknownError()
|
plugin.get_room_history_from_timestamp.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_room_history_from_timestamp.assert_called_with(
|
plugin.get_room_history_from_timestamp.assert_called_with(
|
||||||
|
|||||||
54
tests/test_chunk_messages.py
Normal file
54
tests/test_chunk_messages.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
|
||||||
|
def test_chunked_messages(plugin, read):
|
||||||
|
request = {
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "install_game",
|
||||||
|
"params": {
|
||||||
|
"game_id": "3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message = json.dumps(request).encode() + b"\n"
|
||||||
|
read.side_effect = [message[:5], message[5:], b""]
|
||||||
|
asyncio.run(plugin.run())
|
||||||
|
plugin.install_game.assert_called_with(game_id="3")
|
||||||
|
|
||||||
|
def test_joined_messages(plugin, read):
|
||||||
|
requests = [
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "install_game",
|
||||||
|
"params": {
|
||||||
|
"game_id": "3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "launch_game",
|
||||||
|
"params": {
|
||||||
|
"game_id": "3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
data = b"".join([json.dumps(request).encode() + b"\n" for request in requests])
|
||||||
|
|
||||||
|
read.side_effect = [data, b""]
|
||||||
|
asyncio.run(plugin.run())
|
||||||
|
plugin.install_game.assert_called_with(game_id="3")
|
||||||
|
plugin.launch_game.assert_called_with(game_id="3")
|
||||||
|
|
||||||
|
def test_not_finished(plugin, read):
|
||||||
|
request = {
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "install_game",
|
||||||
|
"params": {
|
||||||
|
"game_id": "3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message = json.dumps(request).encode() # no new line
|
||||||
|
read.side_effect = [message, b""]
|
||||||
|
asyncio.run(plugin.run())
|
||||||
|
plugin.install_game.assert_not_called()
|
||||||
@@ -5,14 +5,14 @@ from galaxy.api.types import FriendInfo
|
|||||||
from galaxy.api.errors import UnknownError
|
from galaxy.api.errors import UnknownError
|
||||||
|
|
||||||
|
|
||||||
def test_get_friends_success(plugin, readline, write):
|
def test_get_friends_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_friends"
|
"method": "import_friends"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_friends.coro.return_value = [
|
plugin.get_friends.coro.return_value = [
|
||||||
FriendInfo("3", "Jan"),
|
FriendInfo("3", "Jan"),
|
||||||
FriendInfo("5", "Ola")
|
FriendInfo("5", "Ola")
|
||||||
@@ -33,14 +33,14 @@ def test_get_friends_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_get_friends_failure(plugin, readline, write):
|
def test_get_friends_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_friends"
|
"method": "import_friends"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_friends.coro.side_effect = UnknownError()
|
plugin.get_friends.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_friends.assert_called_with()
|
plugin.get_friends.assert_called_with()
|
||||||
|
|||||||
@@ -6,17 +6,18 @@ import pytest
|
|||||||
from galaxy.api.types import GameTime
|
from galaxy.api.types import GameTime
|
||||||
from galaxy.api.errors import UnknownError, ImportInProgress, BackendError
|
from galaxy.api.errors import UnknownError, ImportInProgress, BackendError
|
||||||
|
|
||||||
def test_success(plugin, readline, write):
|
def test_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_game_times"
|
"method": "import_game_times"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_game_times.coro.return_value = [
|
plugin.get_game_times.coro.return_value = [
|
||||||
GameTime("3", 60, 1549550504),
|
GameTime("3", 60, 1549550504),
|
||||||
GameTime("5", 10, 1549550502)
|
GameTime("5", 10, None),
|
||||||
|
GameTime("7", None, 1549550502),
|
||||||
]
|
]
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_game_times.assert_called_with()
|
plugin.get_game_times.assert_called_with()
|
||||||
@@ -35,20 +36,23 @@ def test_success(plugin, readline, write):
|
|||||||
{
|
{
|
||||||
"game_id": "5",
|
"game_id": "5",
|
||||||
"time_played": 10,
|
"time_played": 10,
|
||||||
"last_played_time": 1549550502
|
},
|
||||||
|
{
|
||||||
|
"game_id": "7",
|
||||||
|
"last_played_time": 1549550502
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_failure(plugin, readline, write):
|
def test_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_game_times"
|
"method": "import_game_times"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_game_times.coro.side_effect = UnknownError()
|
plugin.get_game_times.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_game_times.assert_called_with()
|
plugin.get_game_times.assert_called_with()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
def test_success(plugin, readline):
|
def test_success(plugin, read):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "install_game",
|
"method": "install_game",
|
||||||
@@ -10,7 +10,7 @@ def test_success(plugin, readline):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_owned_games.return_value = None
|
plugin.get_owned_games.return_value = None
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.install_game.assert_called_with(game_id="3")
|
plugin.install_game.assert_called_with(game_id="3")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import json
|
|||||||
from galaxy.api.plugin import Plugin
|
from galaxy.api.plugin import Plugin
|
||||||
from galaxy.api.consts import Platform
|
from galaxy.api.consts import Platform
|
||||||
|
|
||||||
def test_get_capabilites(reader, writer, readline, write):
|
def test_get_capabilites(reader, writer, read, write):
|
||||||
class PluginImpl(Plugin): #pylint: disable=abstract-method
|
class PluginImpl(Plugin): #pylint: disable=abstract-method
|
||||||
async def get_owned_games(self):
|
async def get_owned_games(self):
|
||||||
pass
|
pass
|
||||||
@@ -16,7 +16,7 @@ def test_get_capabilites(reader, writer, readline, write):
|
|||||||
}
|
}
|
||||||
token = "token"
|
token = "token"
|
||||||
plugin = PluginImpl(Platform.Generic, "0.1", reader, writer, token)
|
plugin = PluginImpl(Platform.Generic, "0.1", reader, writer, token)
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
response = json.loads(write.call_args[0][0])
|
response = json.loads(write.call_args[0][0])
|
||||||
assert response == {
|
assert response == {
|
||||||
@@ -31,13 +31,13 @@ def test_get_capabilites(reader, writer, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_shutdown(plugin, readline, write):
|
def test_shutdown(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "5",
|
"id": "5",
|
||||||
"method": "shutdown"
|
"method": "shutdown"
|
||||||
}
|
}
|
||||||
readline.side_effect = [json.dumps(request)]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.shutdown.assert_called_with()
|
plugin.shutdown.assert_called_with()
|
||||||
response = json.loads(write.call_args[0][0])
|
response = json.loads(write.call_args[0][0])
|
||||||
@@ -47,13 +47,13 @@ def test_shutdown(plugin, readline, write):
|
|||||||
"result": None
|
"result": None
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_ping(plugin, readline, write):
|
def test_ping(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "7",
|
"id": "7",
|
||||||
"method": "ping"
|
"method": "ping"
|
||||||
}
|
}
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
response = json.loads(write.call_args[0][0])
|
response = json.loads(write.call_args[0][0])
|
||||||
assert response == {
|
assert response == {
|
||||||
@@ -62,7 +62,18 @@ def test_ping(plugin, readline, write):
|
|||||||
"result": None
|
"result": None
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_tick(plugin, readline):
|
def test_tick_before_handshake(plugin, read):
|
||||||
readline.side_effect = [""]
|
read.side_effect = [b""]
|
||||||
|
asyncio.run(plugin.run())
|
||||||
|
plugin.tick.assert_not_called()
|
||||||
|
|
||||||
|
def test_tick_after_handshake(plugin, read):
|
||||||
|
request = {
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "6",
|
||||||
|
"method": "initialize_cache",
|
||||||
|
"params": {"data": {}}
|
||||||
|
}
|
||||||
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.tick.assert_called_with()
|
plugin.tick.assert_called_with()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
def test_success(plugin, readline):
|
def test_success(plugin, read):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "launch_game",
|
"method": "launch_game",
|
||||||
@@ -10,7 +10,7 @@ def test_success(plugin, readline):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_owned_games.return_value = None
|
plugin.get_owned_games.return_value = None
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.launch_game.assert_called_with(game_id="3")
|
plugin.launch_game.assert_called_with(game_id="3")
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ from galaxy.api.types import LocalGame
|
|||||||
from galaxy.api.consts import LocalGameState
|
from galaxy.api.consts import LocalGameState
|
||||||
from galaxy.api.errors import UnknownError, FailedParsingManifest
|
from galaxy.api.errors import UnknownError, FailedParsingManifest
|
||||||
|
|
||||||
def test_success(plugin, readline, write):
|
def test_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_local_games"
|
"method": "import_local_games"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
|
|
||||||
plugin.get_local_games.coro.return_value = [
|
plugin.get_local_games.coro.return_value = [
|
||||||
LocalGame("1", LocalGameState.Running),
|
LocalGame("1", LocalGameState.Running),
|
||||||
@@ -53,14 +53,14 @@ def test_success(plugin, readline, write):
|
|||||||
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing")
|
pytest.param(FailedParsingManifest, 200, "Failed parsing manifest", id="failed_parsing")
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_failure(plugin, readline, write, error, code, message):
|
def test_failure(plugin, read, write, error, code, message):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_local_games"
|
"method": "import_local_games"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_local_games.coro.side_effect = error()
|
plugin.get_local_games.coro.side_effect = error()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_local_games.assert_called_with()
|
plugin.get_local_games.assert_called_with()
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ from galaxy.api.types import Game, Dlc, LicenseInfo
|
|||||||
from galaxy.api.consts import LicenseType
|
from galaxy.api.consts import LicenseType
|
||||||
from galaxy.api.errors import UnknownError
|
from galaxy.api.errors import UnknownError
|
||||||
|
|
||||||
def test_success(plugin, readline, write):
|
def test_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_owned_games"
|
"method": "import_owned_games"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_owned_games.coro.return_value = [
|
plugin.get_owned_games.coro.return_value = [
|
||||||
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
|
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
|
||||||
Game(
|
Game(
|
||||||
@@ -67,14 +67,14 @@ def test_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_failure(plugin, readline, write):
|
def test_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_owned_games"
|
"method": "import_owned_games"
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_owned_games.coro.side_effect = UnknownError()
|
plugin.get_owned_games.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_owned_games.assert_called_with()
|
plugin.get_owned_games.assert_called_with()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def cache_data():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_initialize_cache(plugin, readline, write, cache_data):
|
def test_initialize_cache(plugin, read, write, cache_data):
|
||||||
request_id = 3
|
request_id = 3
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@@ -36,7 +36,7 @@ def test_initialize_cache(plugin, readline, write, cache_data):
|
|||||||
"method": "initialize_cache",
|
"method": "initialize_cache",
|
||||||
"params": {"data": cache_data}
|
"params": {"data": cache_data}
|
||||||
}
|
}
|
||||||
readline.side_effect = [json.dumps(request)]
|
read.side_effect = [json.dumps(request).encode() + b"\n"]
|
||||||
|
|
||||||
assert {} == plugin.persistent_cache
|
assert {} == plugin.persistent_cache
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
|
|||||||
52
tests/test_stream_line_reader.py
Normal file
52
tests/test_stream_line_reader.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from galaxy.reader import StreamLineReader
|
||||||
|
from galaxy.unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def stream_reader():
|
||||||
|
reader = MagicMock()
|
||||||
|
reader.read = AsyncMock()
|
||||||
|
return reader
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def read(stream_reader):
|
||||||
|
return stream_reader.read
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def reader(stream_reader):
|
||||||
|
return StreamLineReader(stream_reader)
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_message(reader, read):
|
||||||
|
read.return_value = b"a\n"
|
||||||
|
assert await reader.readline() == b"a"
|
||||||
|
read.assert_called_once()
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_separate_messages(reader, read):
|
||||||
|
read.side_effect = [b"a\n", b"b\n"]
|
||||||
|
assert await reader.readline() == b"a"
|
||||||
|
assert await reader.readline() == b"b"
|
||||||
|
assert read.call_count == 2
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_connected_messages(reader, read):
|
||||||
|
read.return_value = b"a\nb\n"
|
||||||
|
assert await reader.readline() == b"a"
|
||||||
|
assert await reader.readline() == b"b"
|
||||||
|
read.assert_called_once()
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_cut_message(reader, read):
|
||||||
|
read.side_effect = [b"a", b"b\n"]
|
||||||
|
assert await reader.readline() == b"ab"
|
||||||
|
assert read.call_count == 2
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_half_message(reader, read):
|
||||||
|
read.side_effect = [b"a", b""]
|
||||||
|
assert await reader.readline() == b""
|
||||||
|
assert read.call_count == 2
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
def test_success(plugin, readline):
|
def test_success(plugin, read):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"method": "uninstall_game",
|
"method": "uninstall_game",
|
||||||
@@ -10,7 +10,7 @@ def test_success(plugin, readline):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_owned_games.return_value = None
|
plugin.get_owned_games.return_value = None
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.uninstall_game.assert_called_with(game_id="3")
|
plugin.uninstall_game.assert_called_with(game_id="3")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from galaxy.api.errors import UnknownError
|
|||||||
from galaxy.api.consts import PresenceState
|
from galaxy.api.consts import PresenceState
|
||||||
|
|
||||||
|
|
||||||
def test_get_users_success(plugin, readline, write):
|
def test_get_users_success(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "8",
|
"id": "8",
|
||||||
@@ -16,7 +16,7 @@ def test_get_users_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_users.coro.return_value = [
|
plugin.get_users.coro.return_value = [
|
||||||
UserInfo("5", False, "Ula", "http://avatar.png", Presence(PresenceState.Offline))
|
UserInfo("5", False, "Ula", "http://avatar.png", Presence(PresenceState.Offline))
|
||||||
]
|
]
|
||||||
@@ -43,7 +43,7 @@ def test_get_users_success(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_get_users_failure(plugin, readline, write):
|
def test_get_users_failure(plugin, read, write):
|
||||||
request = {
|
request = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": "12",
|
"id": "12",
|
||||||
@@ -53,7 +53,7 @@ def test_get_users_failure(plugin, readline, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.side_effect = [json.dumps(request), ""]
|
read.side_effect = [json.dumps(request).encode() + b"\n", b""]
|
||||||
plugin.get_users.coro.side_effect = UnknownError()
|
plugin.get_users.coro.side_effect = UnknownError()
|
||||||
asyncio.run(plugin.run())
|
asyncio.run(plugin.run())
|
||||||
plugin.get_users.assert_called_with(user_id_list=["10", "11", "12"])
|
plugin.get_users.assert_called_with(user_id_list=["10", "11", "12"])
|
||||||
|
|||||||
Reference in New Issue
Block a user