mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Implement test keyrings and add password tests
- Implement TestKeyring - Implement NoKeyring - Implement FailedKeyring - Copy in `read_value_from_string` function from old tests (will probably rewrite this later) - Add fixtures for keyrings - Implement "we have a keyring" step - Implement step to check specific config values for tests Co-authored-by: Micah Jerome Ellison <micah.jerome.ellison@gmail.com>
This commit is contained in:
parent
1c78a30535
commit
fe018ee241
3 changed files with 110 additions and 115 deletions
|
@ -4,113 +4,9 @@ Feature: Using the installed keyring
|
|||
Given we use the config "multiple.yaml"
|
||||
And we have a keyring
|
||||
When we run "jrnl simple --encrypt" and enter
|
||||
"""
|
||||
sabertooth
|
||||
sabertooth
|
||||
y
|
||||
"""
|
||||
Y
|
||||
Then the config for journal "simple" should have "encrypt" set to "bool:True"
|
||||
When we run "jrnl simple -n 1"
|
||||
Then the output should contain "2013-06-10 15:40 Life is good"
|
||||
|
||||
Scenario: Encrypt journal with no keyring backend and do not store in keyring
|
||||
Given we use the config "simple.yaml"
|
||||
And we do not have a keyring
|
||||
When we run "jrnl test entry"
|
||||
And we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
password
|
||||
password
|
||||
n
|
||||
"""
|
||||
Then we should get no error
|
||||
And we should not see the message "Failed to retrieve keyring"
|
||||
|
||||
Scenario: Encrypt journal with no keyring backend and do store in keyring
|
||||
Given we use the config "simple.yaml"
|
||||
And we do not have a keyring
|
||||
When we run "jrnl test entry"
|
||||
And we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
password
|
||||
password
|
||||
y
|
||||
"""
|
||||
Then we should get no error
|
||||
And we should not see the message "Failed to retrieve keyring"
|
||||
# @todo add step to check contents of keyring
|
||||
|
||||
@todo
|
||||
Scenario: Open an encrypted journal with wrong password in keyring
|
||||
# This should ask the user for the password after the keyring fails
|
||||
|
||||
@todo
|
||||
Scenario: Decrypt journal with password in keyring
|
||||
|
||||
@todo
|
||||
Scenario: Decrypt journal without a keyring
|
||||
|
||||
Scenario: Encrypt journal when keyring exists but fails
|
||||
Given we use the config "simple.yaml"
|
||||
And we have a failed keyring
|
||||
When we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
this password will not be saved in keyring
|
||||
this password will not be saved in keyring
|
||||
y
|
||||
"""
|
||||
Then we should see the message "Failed to retrieve keyring"
|
||||
And we should get no error
|
||||
And we should be prompted for a password
|
||||
And the config for journal "default" should have "encrypt" set to "bool:True"
|
||||
|
||||
Scenario: Decrypt journal when keyring exists but fails
|
||||
Given we use the config "encrypted.yaml"
|
||||
And we have a failed keyring
|
||||
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
|
||||
Then we should see the message "Failed to retrieve keyring"
|
||||
And we should get no error
|
||||
And we should be prompted for a password
|
||||
And we should see the message "Journal decrypted"
|
||||
And the config for journal "default" should have "encrypt" set to "bool:False"
|
||||
And the journal should have 2 entries
|
||||
|
||||
Scenario: Open encrypted journal when keyring exists but fails
|
||||
# This should ask the user for the password after the keyring fails
|
||||
Given we use the config "encrypted.yaml"
|
||||
And we have a failed keyring
|
||||
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
|
||||
Then we should see the message "Failed to retrieve keyring"
|
||||
And we should get no error
|
||||
And we should be prompted for a password
|
||||
And the output should contain "2013-06-10 15:40 Life is good"
|
||||
|
||||
Scenario: Mistyping your password
|
||||
Given we use the config "simple.yaml"
|
||||
When we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
swordfish
|
||||
sordfish
|
||||
"""
|
||||
Then we should be prompted for a password
|
||||
And we should see the message "Passwords did not match"
|
||||
And the config for journal "default" should not have "encrypt" set
|
||||
And the journal should have 2 entries
|
||||
|
||||
Scenario: Mistyping your password, then getting it right
|
||||
Given we use the config "simple.yaml"
|
||||
When we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
swordfish
|
||||
sordfish
|
||||
swordfish
|
||||
swordfish
|
||||
n
|
||||
"""
|
||||
Then we should be prompted for a password
|
||||
And we should see the message "Passwords did not match"
|
||||
And we should see the message "Journal encrypted"
|
||||
And the config for journal "default" should have "encrypt" set to "bool:True"
|
||||
When we run "jrnl -n 1" and enter "swordfish"
|
||||
Then we should be prompted for a password
|
||||
And the output should contain "2013-06-10 15:40 Life is good"
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
# Copyright (C) 2012-2021 jrnl contributors
|
||||
# License: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
import ast
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from keyring import backend
|
||||
from keyring import set_keyring
|
||||
from keyring import errors
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
@ -16,14 +21,75 @@ import toml
|
|||
|
||||
from jrnl import __version__
|
||||
from jrnl.cli import cli
|
||||
from jrnl.config import load_config
|
||||
from jrnl.os_compat import split_args
|
||||
|
||||
|
||||
class TestKeyring(backend.KeyringBackend):
|
||||
"""A test keyring that just stores its values in a hash"""
|
||||
|
||||
priority = 1
|
||||
keys = defaultdict(dict)
|
||||
|
||||
def set_password(self, servicename, username, password):
|
||||
self.keys[servicename][username] = password
|
||||
|
||||
def get_password(self, servicename, username):
|
||||
return self.keys[servicename].get(username)
|
||||
|
||||
def delete_password(self, servicename, username):
|
||||
self.keys[servicename][username] = None
|
||||
|
||||
|
||||
class NoKeyring(backend.KeyringBackend):
|
||||
"""A keyring that simulated an environment with no keyring backend."""
|
||||
|
||||
priority = 2
|
||||
keys = defaultdict(dict)
|
||||
|
||||
def set_password(self, servicename, username, password):
|
||||
raise errors.NoKeyringError
|
||||
|
||||
def get_password(self, servicename, username):
|
||||
raise errors.NoKeyringError
|
||||
|
||||
def delete_password(self, servicename, username):
|
||||
raise errors.NoKeyringError
|
||||
|
||||
|
||||
class FailedKeyring(backend.KeyringBackend):
|
||||
"""
|
||||
A keyring that cannot be retrieved.
|
||||
"""
|
||||
|
||||
priority = 2
|
||||
|
||||
def set_password(self, servicename, username, password):
|
||||
raise errors.KeyringError
|
||||
|
||||
def get_password(self, servicename, username):
|
||||
raise errors.KeyringError
|
||||
|
||||
def delete_password(self, servicename, username):
|
||||
raise errors.KeyringError
|
||||
|
||||
|
||||
# ----- UTILS ----- #
|
||||
def failed_msg(msg, expected, actual):
|
||||
return f"{msg}\nExpected:\n{expected}\n---end---\nActual:\n{actual}\n---end---\n"
|
||||
|
||||
|
||||
def read_value_from_string(string):
|
||||
if string[0] == "{":
|
||||
# Handle value being a dictionary
|
||||
return ast.literal_eval(string)
|
||||
|
||||
# Takes strings like "bool:true" or "int:32" and coerces them into proper type
|
||||
t, value = string.split(":")
|
||||
value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](value)
|
||||
return value
|
||||
|
||||
|
||||
# ----- FIXTURES ----- #
|
||||
@fixture
|
||||
def cli_run():
|
||||
|
@ -46,22 +112,38 @@ def toml_version(working_dir):
|
|||
pyproject_contents = toml.load(pyproject)
|
||||
return pyproject_contents["tool"]["poetry"]["version"]
|
||||
|
||||
|
||||
@fixture
|
||||
def password():
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
@fixture
|
||||
def command():
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
@fixture
|
||||
def user_input():
|
||||
return ''
|
||||
return ""
|
||||
|
||||
|
||||
@fixture
|
||||
def keyring():
|
||||
set_keyring(NoKeyring())
|
||||
|
||||
|
||||
@fixture
|
||||
def config_data(config_path):
|
||||
return load_config(config_path)
|
||||
|
||||
|
||||
# ----- STEPS ----- #
|
||||
@given("we have a keyring", target_fixture="keyring")
|
||||
def we_have_keyring():
|
||||
set_keyring(FailedKeyring())
|
||||
|
||||
|
||||
@given(parse('we use the config "{config_file}"'), target_fixture="config_path")
|
||||
@given('we use the config "<config_file>"', target_fixture="config_path")
|
||||
def we_use_the_config(config_file, temp_dir, working_dir):
|
||||
|
@ -99,10 +181,13 @@ def use_password_forever(pw):
|
|||
@when('we run "jrnl"')
|
||||
@when(parse('we run "jrnl" and enter "{user_input}"'))
|
||||
@when(parse('we run "jrnl {command}" and enter\n{user_input}'))
|
||||
def we_run(command, config_path, user_input, cli_run, capsys, password):
|
||||
def we_run(command, config_path, user_input, cli_run, capsys, password, keyring):
|
||||
args = split_args(command)
|
||||
status = 0
|
||||
|
||||
if not password and user_input:
|
||||
password = user_input
|
||||
|
||||
# fmt: off
|
||||
# see: https://github.com/psf/black/issues/664
|
||||
with \
|
||||
|
@ -163,9 +248,9 @@ def output_should_not_contain(output, cli_run):
|
|||
def output_should_be(output, cli_run):
|
||||
actual_out = cli_run["stdout"].strip()
|
||||
output = output.strip()
|
||||
assert (
|
||||
output and output == actual_out
|
||||
), failed_msg('Output does not match.', output, actual_out)
|
||||
assert output and output == actual_out, failed_msg(
|
||||
"Output does not match.", output, actual_out
|
||||
)
|
||||
|
||||
|
||||
@then('the output should contain the date "<date>"')
|
||||
|
@ -183,3 +268,17 @@ def output_should_contain_version(cli_run, toml_version):
|
|||
def should_see_the_message(text, cli_run):
|
||||
out = cli_run["stderr"]
|
||||
assert text in out, [text, out]
|
||||
|
||||
|
||||
@then(parse('the config should have "{key}" set to'))
|
||||
@then(parse('the config should have "{key}" set to "{value}"'))
|
||||
@then(parse('the config for journal "{journal}" should have "{key}" set to "{value}"'))
|
||||
def config_var(config_data, key, value="", journal=None):
|
||||
value = read_value_from_string(value)
|
||||
|
||||
configuration = config_data
|
||||
if journal:
|
||||
configuration = configuration["journals"][journal]
|
||||
|
||||
assert key in configuration
|
||||
assert configuration[key] == value
|
||||
|
|
|
@ -9,7 +9,7 @@ scenarios("../features/delete.feature")
|
|||
# scenarios("../features/format.feature")
|
||||
# scenarios("../features/import.feature")
|
||||
# scenarios("../features/multiple_journals.feature")
|
||||
# scenarios("../features/password.feature")
|
||||
scenarios("../features/password.feature")
|
||||
# scenarios("../features/search.feature")
|
||||
# scenarios("../features/star.feature")
|
||||
# scenarios("../features/tag.feature")
|
||||
|
|
Loading…
Add table
Reference in a new issue