Skip to content

Commit

Permalink
Implement the refresh token to the Config
Browse files Browse the repository at this point in the history
  • Loading branch information
vaclav-2012 committed May 31, 2020
1 parent f14a842 commit d0f4ed5
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ We follow [Semantic Versions](https://semver.org/).
- Implement the post submission to the `RedditCutifier`
- Add the post submission via the `RedditCutifier` to the `App`
- Add the `ConfigStorage` class - storing the refresh token
- Implement the storing of the refresh token to the `Config`


## Version 0.1.4
Expand Down
32 changes: 31 additions & 1 deletion slow_start_rewatch/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dotty_dict import dotty
from structlog import get_logger

from slow_start_rewatch.config_storage import ConfigStorage
from slow_start_rewatch.version import version

DEFAULT_CONFIG_FILENAME = "config_default.yml"
Expand All @@ -30,7 +31,9 @@ def __init__(self, filename: str = DEFAULT_CONFIG_FILENAME) -> None:

self._substitute_placeholders()

self.refresh_token: Optional[str] = None
self.storage = ConfigStorage(self.config["refresh_token_file"])

self._refresh_token: Optional[str] = None

def __getitem__(self, key):
"""Return the config item."""
Expand All @@ -44,6 +47,32 @@ def __contains__(self, key):
"""Return true if an item exists in the config."""
return key in self.config

@property
def refresh_token(self) -> Optional[str]:
"""
Get the refresh token.
Try to load the token if not set.
"""
if not self._refresh_token:
self._refresh_token = self.storage.load_refresh_token()

return self._refresh_token

@refresh_token.setter
def refresh_token(self, refresh_token: Optional[str]) -> None:
"""
Set and save the refresh token.
If the value is set to None delete the stored token.
"""
if refresh_token:
self._refresh_token = refresh_token.strip()
self.storage.save_refresh_token(self._refresh_token)
else:
self._refresh_token = None
self.storage.delete_refresh_token()

def _substitute_placeholders(self) -> None:
"""Substitute the placeholders in the config."""
mapping = {
Expand All @@ -54,6 +83,7 @@ def _substitute_placeholders(self) -> None:
keys = [
"data_dir",
"scheduled_post_file",
"refresh_token_file",
"reddit.user_agent",
]

Expand Down
3 changes: 3 additions & 0 deletions slow_start_rewatch/config_default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ data_dir: "${home_dir}${ps}slow_start_rewatch"
# The YAML file with the data about a scheduled post:
scheduled_post_file: "${home_dir}${ps}slow_start_rewatch${ps}scheduled_post.yml"

# The file where the refresh token is stored:
refresh_token_file: "${home_dir}${ps}slow_start_rewatch${ps}reddit_token.dat"

# Reddit OAuth2 settings:
reddit:
client_id: DGWt4p3WhWiQWg
Expand Down
21 changes: 18 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import os
from datetime import datetime
from typing import Optional
from unittest import mock

import pytest
Expand All @@ -23,16 +24,30 @@
HTTP_SERVER_PORT = 65000

OAUTH_CODE = "anime_girls_are_cute"
REFRESH_TOKEN = "moe_moe_kyun" # noqa: S105
REFRESH_TOKEN = "moe_moe_kyun"


class MockConfig(Config):
"""Simplified version of the Config class."""
"""
Simplified version of the Config class.
The refresh token isn't stored permanently.
"""

def __init__(self, config_data=None) -> None:
"""Initialize MockConfig."""
self.config = dotty(config_data)
self.refresh_token = None
self._refresh_token = None

@property
def refresh_token(self) -> Optional[str]:
"""Get the refresh token."""
return self._refresh_token

@refresh_token.setter
def refresh_token(self, refresh_token) -> None:
"""Set the refresh token."""
self._refresh_token = refresh_token


@pytest.fixture()
Expand Down
56 changes: 53 additions & 3 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-

import os
from unittest.mock import patch

import pytest

from slow_start_rewatch.config import Config
from slow_start_rewatch.config_storage import ConfigStorage
from tests.conftest import REFRESH_TOKEN, TEST_ROOT_DIR

TEST_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
EXAMPLE_CONFIG_FILE = os.path.join(
TEST_ROOT_DIR,
"test_config",
Expand All @@ -15,10 +17,11 @@


def test_default_config():
"""Test that default config is loaded."""
"""Test that default config is loaded and contains mandatory items."""
config = Config()

assert "data_dir" in config
assert "refresh_token_file" in config
assert "Slow Start" in config["reddit.user_agent"]


Expand All @@ -30,7 +33,7 @@ def test_custom_config():
assert "cute_girls" in config
assert "pesky_boys" not in config
assert os.path.expanduser("~") in config["data_dir"]
assert "{0}slow_start".format(os.path.sep) in config["data_dir"]
assert "{0}slow_start".format(os.path.sep) in config["refresh_token_file"]


def test_config_interface():
Expand All @@ -42,3 +45,50 @@ def test_config_interface():

config["slow_start.second_season"] = "When?"
assert config["slow_start"]["second_season"] == "When?"


@patch.object(ConfigStorage, "load_refresh_token")
def test_refresh_token_getter(mock_load):
"""
Test getting the token.
When the token is ``None`` it's loaded from the storage.
When the token is set already a cached value is used instead of loading.
"""
config = Config()
mock_load.side_effect = [
None,
REFRESH_TOKEN,
"oishiku_nare",
]

assert config.refresh_token is None
assert config.refresh_token == REFRESH_TOKEN
assert config.refresh_token == REFRESH_TOKEN
assert mock_load.call_count == 2


@patch.object(ConfigStorage, "load_refresh_token")
@patch.object(ConfigStorage, "delete_refresh_token")
@patch.object(ConfigStorage, "save_refresh_token")
def test_refresh_token_setter(mock_save, mock_delete, mock_load):
"""
Test setting the token.
The token should be saved when the property is set to a string and deleted
when it's set to ``None``.
Loading must be mocked to allow assertion of the empty value.
"""
config = Config()

config.refresh_token = REFRESH_TOKEN
assert mock_save.call_args.args == (REFRESH_TOKEN,)
assert config.refresh_token == REFRESH_TOKEN

config.refresh_token = None
mock_load.return_value = None
assert mock_delete.call_count == 1
assert config.refresh_token is None
assert mock_load.call_count == 1
2 changes: 2 additions & 0 deletions tests/test_config/config_example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ cute_girls: True

data_dir: "${home_dir}${ps}slow_start_rewatch"

refresh_token_file: "${home_dir}${ps}slow_start_rewatch${ps}reddit_token.dat"

scheduled_post_file: "${home_dir}${ps}slow_start_rewatch${ps}scheduled_post.yml"

reddit:
Expand Down

0 comments on commit d0f4ed5

Please sign in to comment.