mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-20 13:08:31 +02:00
Merge pull request #1 from sriniv27/separate-override-behave-test-from-core
separate-override-behave-test-from-core
This commit is contained in:
commit
36623c1100
6 changed files with 128 additions and 78 deletions
7
features/data/configs/tiny.yaml
Normal file
7
features/data/configs/tiny.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
journals:
|
||||||
|
default: /tmp/journal.jrnl
|
||||||
|
colors:
|
||||||
|
body: none
|
||||||
|
title: green
|
||||||
|
editor: "vim"
|
||||||
|
encrypt: false
|
|
@ -10,6 +10,7 @@ Given we use the config "editor.yaml"
|
||||||
When we run "jrnl --config-override '{"editor": "nano"}'"
|
When we run "jrnl --config-override '{"editor": "nano"}'"
|
||||||
Then the editor "nano" should have been called
|
Then the editor "nano" should have been called
|
||||||
|
|
||||||
|
@skip_win
|
||||||
Scenario: Override configured linewrap with a value of 23
|
Scenario: Override configured linewrap with a value of 23
|
||||||
Given we use the config "editor.yaml"
|
Given we use the config "editor.yaml"
|
||||||
When we run "jrnl -2 --config-override '{"linewrap": 23}' --format fancy"
|
When we run "jrnl -2 --config-override '{"linewrap": 23}' --format fancy"
|
||||||
|
@ -30,7 +31,8 @@ Then the output should be
|
||||||
┖─────────────────────┘
|
┖─────────────────────┘
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@skip_win
|
||||||
Scenario: Override color selections with runtime overrides
|
Scenario: Override color selections with runtime overrides
|
||||||
Given we use the config "editor.yaml"
|
Given we use the config "tiny.yaml"
|
||||||
When we run "jrnl -1 --config-override '{"colors.body": "blue"}' "
|
When we run jrnl with -1 --config-override '{"colors.body": "blue"}'
|
||||||
Then the runtime config should have colors.body set to blue
|
Then the runtime config should have colors.body set to blue
|
||||||
|
|
|
@ -13,13 +13,13 @@ from behave import given
|
||||||
from behave import then
|
from behave import then
|
||||||
from behave import when
|
from behave import when
|
||||||
import keyring
|
import keyring
|
||||||
import mock
|
|
||||||
import toml
|
import toml
|
||||||
import yaml
|
import yaml
|
||||||
from yaml.loader import FullLoader
|
|
||||||
|
|
||||||
import jrnl.time
|
import jrnl.time
|
||||||
from jrnl import Journal, override
|
from jrnl import Journal
|
||||||
from jrnl import __version__
|
from jrnl import __version__
|
||||||
from jrnl import plugins
|
from jrnl import plugins
|
||||||
from jrnl.cli import cli
|
from jrnl.cli import cli
|
||||||
|
@ -215,65 +215,6 @@ def open_editor_and_enter(context, method, text=""):
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
@then("the runtime config should have {key_as_dots} set to {override_value}")
|
|
||||||
def config_override(context, key_as_dots: str, override_value: str):
|
|
||||||
with open(context.config_path) as f:
|
|
||||||
loaded_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
|
||||||
loaded_cfg["journal"] = "features/journals/simple.journal"
|
|
||||||
base_cfg = loaded_cfg.copy()
|
|
||||||
|
|
||||||
def _mock_callback(**args):
|
|
||||||
print("callback executed")
|
|
||||||
|
|
||||||
# fmt: off
|
|
||||||
try:
|
|
||||||
with \
|
|
||||||
mock.patch.object(jrnl.override,"recursively_apply",wraps=jrnl.override.recursively_apply) as mock_recurse, \
|
|
||||||
patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
|
|
||||||
patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
|
|
||||||
:
|
|
||||||
cli(['-1','--config-override', '{"%s": "%s"}'%(key_as_dots,override_value)])
|
|
||||||
# mock_recurse.assert_any_call(base_cfg,key_as_dots.split('.'),override_value)
|
|
||||||
keys_list = key_as_dots.split('.')
|
|
||||||
callList = [
|
|
||||||
(base_cfg,keys_list,override_value),
|
|
||||||
(base_cfg,keys_list[1], override_value)
|
|
||||||
]
|
|
||||||
mock_recurse.call_args_list
|
|
||||||
|
|
||||||
except SystemExit as e :
|
|
||||||
context.exit_status = e.code
|
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
|
|
||||||
@then("the editor {editor} should have been called")
|
|
||||||
def editor_override(context, editor):
|
|
||||||
def _mock_editor(command_and_journal_file):
|
|
||||||
editor = command_and_journal_file[0]
|
|
||||||
tmpfile = command_and_journal_file[-1]
|
|
||||||
context.tmpfile = tmpfile
|
|
||||||
print("%s has been launched" % editor)
|
|
||||||
return tmpfile
|
|
||||||
|
|
||||||
# fmt: off
|
|
||||||
# see: https://github.com/psf/black/issues/664
|
|
||||||
with \
|
|
||||||
patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \
|
|
||||||
patch("sys.stdin.isatty", return_value=True), \
|
|
||||||
patch("jrnl.time.parse", side_effect=_mock_time_parse(context)), \
|
|
||||||
patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
|
|
||||||
patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
|
|
||||||
:
|
|
||||||
try :
|
|
||||||
cli(['--config-override','{"editor": "%s"}'%editor])
|
|
||||||
context.exit_status = 0
|
|
||||||
context.editor = mock_editor
|
|
||||||
assert mock_editor.assert_called_once_with(editor, context.tmpfile)
|
|
||||||
except SystemExit as e:
|
|
||||||
context.exit_status = e.code
|
|
||||||
# fmt: on
|
|
||||||
|
|
||||||
|
|
||||||
@then("the editor should have been called")
|
@then("the editor should have been called")
|
||||||
@then("the editor should have been called with {num} arguments")
|
@then("the editor should have been called with {num} arguments")
|
||||||
def count_editor_args(context, num=None):
|
def count_editor_args(context, num=None):
|
||||||
|
|
88
features/steps/override.py
Normal file
88
features/steps/override.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
from jrnl.jrnl import run
|
||||||
|
from jrnl.os_compat import split_args
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
# from __future__ import with_statement
|
||||||
|
from jrnl.args import parse_args
|
||||||
|
import os
|
||||||
|
from behave import given, when, then
|
||||||
|
import yaml
|
||||||
|
from yaml.loader import FullLoader
|
||||||
|
|
||||||
|
import jrnl
|
||||||
|
from jrnl.cli import cli
|
||||||
|
|
||||||
|
|
||||||
|
@given("we use the config {config_file}")
|
||||||
|
def load_config(context, config_file):
|
||||||
|
filepath = os.path.join("features/configs", config_file)
|
||||||
|
context.config_path = os.path.abspath(filepath)
|
||||||
|
|
||||||
|
|
||||||
|
@when("we run jrnl with {args}")
|
||||||
|
def run_command(context, args):
|
||||||
|
context.args = split_args("%s" % args)
|
||||||
|
context.parser = parse_args(context.args)
|
||||||
|
with open(context.config_path, "r") as f:
|
||||||
|
cfg = yaml.load(f, Loader=FullLoader)
|
||||||
|
context.cfg = cfg
|
||||||
|
|
||||||
|
|
||||||
|
@then("the runtime config should have {key_as_dots} set to {override_value}")
|
||||||
|
def config_override(context, key_as_dots: str, override_value: str):
|
||||||
|
key_as_vec = key_as_dots.split(".")
|
||||||
|
expected_call_args_list = [
|
||||||
|
(context.cfg, key_as_vec, override_value),
|
||||||
|
(context.cfg[key_as_vec[0]], key_as_vec[1], override_value),
|
||||||
|
]
|
||||||
|
with open(context.config_path) as f:
|
||||||
|
loaded_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||||
|
loaded_cfg["journal"] = "features/journals/simple.journal"
|
||||||
|
|
||||||
|
def _mock_callback(**args):
|
||||||
|
print("callback executed")
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
try:
|
||||||
|
with \
|
||||||
|
mock.patch.object(jrnl.override,"_recursively_apply",wraps=jrnl.override._recursively_apply) as mock_recurse, \
|
||||||
|
mock.patch('jrnl.install.load_or_install_jrnl', return_value=context.cfg), \
|
||||||
|
mock.patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
|
||||||
|
mock.patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
|
||||||
|
:
|
||||||
|
run(context.parser)
|
||||||
|
|
||||||
|
assert mock_recurse.call_count == 2
|
||||||
|
mock_recurse.call_args_list = expected_call_args_list
|
||||||
|
|
||||||
|
except SystemExit as e :
|
||||||
|
context.exit_status = e.code
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
|
@then("the editor {editor} should have been called")
|
||||||
|
def editor_override(context, editor):
|
||||||
|
def _mock_editor(command_and_journal_file):
|
||||||
|
editor = command_and_journal_file[0]
|
||||||
|
tmpfile = command_and_journal_file[-1]
|
||||||
|
context.tmpfile = tmpfile
|
||||||
|
print("%s has been launched" % editor)
|
||||||
|
return tmpfile
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
# see: https://github.com/psf/black/issues/664
|
||||||
|
with \
|
||||||
|
mock.patch("subprocess.call", side_effect=_mock_editor) as mock_editor, \
|
||||||
|
mock.patch("sys.stdin.isatty", return_value=True), \
|
||||||
|
mock.patch("jrnl.time.parse"), \
|
||||||
|
mock.patch("jrnl.config.get_config_path", side_effect=lambda: context.config_path), \
|
||||||
|
mock.patch("jrnl.install.get_config_path", side_effect=lambda: context.config_path) \
|
||||||
|
:
|
||||||
|
try :
|
||||||
|
cli(['--config-override','{"editor": "%s"}'%editor])
|
||||||
|
context.exit_status = 0
|
||||||
|
context.editor = mock_editor
|
||||||
|
assert mock_editor.assert_called_once_with(editor, context.tmpfile)
|
||||||
|
except SystemExit as e:
|
||||||
|
context.exit_status = e.code
|
||||||
|
# fmt: on
|
|
@ -3,14 +3,14 @@ def apply_overrides(overrides: dict, base_config: dict) -> dict:
|
||||||
config = base_config.copy()
|
config = base_config.copy()
|
||||||
for k in overrides:
|
for k in overrides:
|
||||||
nodes = k.split(".")
|
nodes = k.split(".")
|
||||||
config = recursively_apply(config, nodes, overrides[k])
|
config = _recursively_apply(config, nodes, overrides[k])
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def recursively_apply(config: dict, nodes: list, override_value) -> dict:
|
def _recursively_apply(config: dict, nodes: list, override_value) -> dict:
|
||||||
"""Recurse through configuration and apply overrides at the leaf of the config tree
|
"""Recurse through configuration and apply overrides at the leaf of the config tree
|
||||||
|
|
||||||
See: https://stackoverflow.com/a/47276490 for algorithm
|
Credit to iJames on SO: https://stackoverflow.com/a/47276490 for algorithm
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config (dict): loaded configuration from YAML
|
config (dict): loaded configuration from YAML
|
||||||
|
@ -18,11 +18,18 @@ def recursively_apply(config: dict, nodes: list, override_value) -> dict:
|
||||||
override_value (str): runtime override passed from the command-line
|
override_value (str): runtime override passed from the command-line
|
||||||
"""
|
"""
|
||||||
key = nodes[0]
|
key = nodes[0]
|
||||||
config[key] = (
|
if len(nodes) == 1:
|
||||||
override_value
|
config[key] = override_value
|
||||||
if len(nodes) == 1
|
else:
|
||||||
else recursively_apply(
|
next_key = nodes[1:]
|
||||||
config[key] if key in config else {}, nodes[1:], override_value
|
_recursively_apply(_get_config_node(config, key), next_key, override_value)
|
||||||
)
|
|
||||||
)
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def _get_config_node(config: dict, key: str):
|
||||||
|
if key in config:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
config[key] = None
|
||||||
|
return config[key]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from jrnl.override import apply_overrides, recursively_apply
|
from jrnl.override import apply_overrides, _recursively_apply, _get_config_node
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
|
@ -27,8 +27,13 @@ def test_override_dot_notation(minimal_config):
|
||||||
assert cfg["colors"] == {"body": "blue", "date": "green"}
|
assert cfg["colors"] == {"body": "blue", "date": "green"}
|
||||||
|
|
||||||
|
|
||||||
def test_recursive_override(minimal_config):
|
def test_recursively_apply():
|
||||||
|
|
||||||
cfg = {"colors": {"body": "red", "title": "green"}}
|
cfg = {"colors": {"body": "red", "title": "green"}}
|
||||||
cfg = recursively_apply(cfg, ["colors", "body"], "blue")
|
cfg = _recursively_apply(cfg, ["colors", "body"], "blue")
|
||||||
assert cfg["colors"]["body"] == "blue"
|
assert cfg["colors"]["body"] == "blue"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_config_node(minimal_config):
|
||||||
|
assert len(minimal_config.keys()) == 3
|
||||||
|
assert _get_config_node(minimal_config, "editor") == "vim"
|
||||||
|
assert _get_config_node(minimal_config, "display_format") == None
|
||||||
|
|
Loading…
Add table
Reference in a new issue