From 4914b0c74ed669d6631897950ff1660ce91ef918 Mon Sep 17 00:00:00 2001
From: Simeon Simeonov
Date: Thu, 23 Dec 2021 14:41:14 +0100
Subject: Initial commit and release for Christmas with love
---
tests/conftest.py | 59 ++++++++++++++++++
tests/test_cli.py | 40 ++++++++++++
tests/test_envtoolkit_instance.py | 64 +++++++++++++++++++
tests/test_envtoolkit_instance_static.py | 102 +++++++++++++++++++++++++++++++
4 files changed, 265 insertions(+)
create mode 100644 tests/conftest.py
create mode 100644 tests/test_cli.py
create mode 100644 tests/test_envtoolkit_instance.py
create mode 100644 tests/test_envtoolkit_instance_static.py
(limited to 'tests')
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..1f70259
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,59 @@
+# etoolkit
+# Copyright (C) 2021 Simeon Simeonov
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Common fixtures"""
+import pytest
+
+
+@pytest.fixture
+def config_data():
+ """config_data for testing"""
+ return {
+ 'general': {
+ 'MASTER_PASSWORD_HASH': (
+ 'pbkdf2_sha256$100000$kFOQkAPtStZ/Ny/O4501ygHGQnqh5Y+ySxF9qVHr'
+ 'iv8=$3BujuWzn3CfDnw4yiD9m3F+GjeW1MHHW40R/ThHNcn0='
+ )
+ },
+ 'instances': {
+ 'default': {'ETOOLKIT_PROMPT': '(%i)'},
+ 'dev': {
+ 'ETOOLKIT_PARENT': 'default',
+ 'PYTHONPATH': ':/home/user/.pythonpath',
+ 'DB_CONNECTION': (
+ 'enc-val$1$Y/TBb1F3siHTw6qZg9ERzZfA8PLPf2CwGSQLpu9jYWw=$FT'
+ '5tS9o+ABvsxogIXpJim16Gz5SVtV8='
+ ),
+ },
+ 'secret': {
+ 'ETOOLKIT_PARENT': 'default',
+ 'ETOOLKIT_SENSITIVE': ['PASSWORD'],
+ 'GNUPGHOME': '%h/private/.gnupg',
+ 'PASSWORD': (
+ 'enc-val$1$vIBcoCNiYrsDLtF41uLuSEnppBjhliD0B8jwcBJcj/c=$Kw'
+ 'OGe/y1dlxktDaCnJPIVNuaQ4Q7yNo='
+ ),
+ },
+ },
+ }
+
+
+@pytest.fixture
+def password_hash():
+ """password hash for testing, corresponding to 'the very secret passwd'"""
+ return (
+ 'pbkdf2_sha256$100000$kFOQkAPtStZ/Ny/O4501ygHGQnqh5Y+ySxF9qVHr'
+ 'iv8=$3BujuWzn3CfDnw4yiD9m3F+GjeW1MHHW40R/ThHNcn0='
+ )
diff --git a/tests/test_cli.py b/tests/test_cli.py
new file mode 100644
index 0000000..1bd9be1
--- /dev/null
+++ b/tests/test_cli.py
@@ -0,0 +1,40 @@
+# etoolkit
+# Copyright (C) 2021 Simeon Simeonov
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Tests for the CLI (etoolkit.__main__"""
+import pytest
+
+import etoolkit
+from etoolkit.__main__ import main
+
+
+def test_help(capsys):
+ """Dummy test checking if the CLI is available at all"""
+ with pytest.raises(SystemExit) as exit_info:
+ main(['-h'])
+ assert exit_info.type == SystemExit
+ assert exit_info.value.code == 0
+ assert capsys.readouterr().out.startswith('usage: etoolkit')
+
+
+def test_version(capsys):
+ """Dummy test checking if the CLI is available at all"""
+ with pytest.raises(SystemExit) as exit_info:
+ main(['-v'])
+ assert exit_info.type == SystemExit
+ assert exit_info.value.code == 0
+ assert capsys.readouterr().out.startswith(
+ f'etoolkit {etoolkit.__version__}'
+ )
diff --git a/tests/test_envtoolkit_instance.py b/tests/test_envtoolkit_instance.py
new file mode 100644
index 0000000..2ffacd2
--- /dev/null
+++ b/tests/test_envtoolkit_instance.py
@@ -0,0 +1,64 @@
+# etoolkit
+# Copyright (C) 2021 Simeon Simeonov
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Tests for etoolkit.EtoolkitInstance"""
+import pytest
+
+import etoolkit
+
+
+def test_instantiation(config_data, password_hash):
+ """Tests for object instatiation"""
+ with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info:
+ instance = etoolkit.EtoolkitInstance('devv', config_data)
+ assert exc_info.type is etoolkit.EtoolkitInstanceError
+ assert exc_info.value.args[0] == 'Unknown instance "devv"'
+
+ instance = etoolkit.EtoolkitInstance('secret', config_data)
+ assert 'ETOOLKIT_PARENT' not in instance.raw_env_variables
+ assert 'ETOOLKIT_SENSITIVE' not in instance.raw_env_variables
+ assert 'DB_CONNECTION' not in instance.sensitive_env_variables
+ assert 'PASSWORD' in instance.sensitive_env_variables
+ assert instance.name == 'secret'
+ assert instance.master_password_hash == password_hash
+ assert instance.master_password is None
+
+
+def test_get_environ(config_data):
+ """Tests the EtoolkitInstance.get_environ method"""
+ instance = etoolkit.EtoolkitInstance('secret', config_data)
+ with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info:
+ env = instance.get_environ()
+ assert exc_info.type is etoolkit.EtoolkitInstanceError
+ assert exc_info.value.args[0] == 'Neither password or prompt function set'
+
+ instance.master_password = 'The very secret passwd' # wrong passwd
+ with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info:
+ env = instance.get_environ()
+ assert exc_info.type is etoolkit.EtoolkitInstanceError
+ assert exc_info.value.args[0].startswith(
+ 'Invalid tag when decrypting: enc-val$'
+ )
+
+ instance.master_password = 'the very secret passwd' # correct passwd
+ env = instance.get_environ()
+ assert isinstance(env, dict)
+ assert env['PASSWORD'] == 'secret2'
+
+
+def test_get_full_name(config_data):
+ """Tests the EtoolkitInstance.get_full_name method"""
+ instance = etoolkit.EtoolkitInstance('secret', config_data)
+ assert instance.get_full_name('->') == 'default->secret'
diff --git a/tests/test_envtoolkit_instance_static.py b/tests/test_envtoolkit_instance_static.py
new file mode 100644
index 0000000..d7ac857
--- /dev/null
+++ b/tests/test_envtoolkit_instance_static.py
@@ -0,0 +1,102 @@
+# etoolkit
+# Copyright (C) 2021 Simeon Simeonov
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""Tests for etoolkit.EtoolkitInstance static methods"""
+import unittest.mock
+
+import pytest
+
+import etoolkit
+
+
+@unittest.mock.patch('getpass.getpass')
+def test_confirm_password_prompt(getpass, password_hash):
+ """Tests the static EtoolkitInstance.confirm_password_prompt method"""
+ getpass.return_value = 'the very secret passwd'
+ assert (
+ etoolkit.EtoolkitInstance.confirm_password_prompt(password_hash)
+ == 'the very secret passwd'
+ )
+ assert (
+ etoolkit.EtoolkitInstance.confirm_password_prompt(password_hash, False)
+ == 'the very secret passwd'
+ )
+
+
+def test_decrypt():
+ """Tests the static EtoolkitInstance.decrypt method"""
+ assert (
+ etoolkit.EtoolkitInstance.decrypt(
+ 'the very secret passwd',
+ (
+ 'enc-val$1$Y/TBb1F3siHTw6qZg9ERzZfA8PLPf2CwGSQLpu9jYWw=$FT5tS9'
+ 'o+ABvsxogIXpJim16Gz5SVtV8='
+ ),
+ )
+ == 'secret1'
+ )
+
+ # now test with modified edata
+ with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info:
+ edata = (
+ 'enc-val$1$Y/TBb1F3siHTw6qZg9ERzZfA8PLPf2CwGSQLpu9jYWw=$FT5tS9'
+ 'o+ABvsxogIXpJim17Gz5SVtV8='
+ )
+ etoolkit.EtoolkitInstance.decrypt(
+ 'the very secret passwd', edata
+ ) == 'secret1'
+ assert exc_info.type is etoolkit.EtoolkitInstanceError
+ assert exc_info.value.args[0] == f'Invalid tag when decrypting: {edata}'
+
+
+def test_encrypt():
+ """Tests the static EtoolkitInstance.encrypt method"""
+ edata = etoolkit.EtoolkitInstance.encrypt('foo', 'bar')
+ assert edata.startswith('enc-val$')
+ assert len(edata) == 83
+ # the edata should always be different because of random salting
+ assert edata != etoolkit.EtoolkitInstance.encrypt('foo', 'bar')
+
+
+def test_get_new_password_hash():
+ """Tests the static EtoolkitInstance.get_new_password_hash method"""
+ new_hash = etoolkit.EtoolkitInstance.get_new_password_hash(
+ 'the very secret passwd'
+ )
+ # all pbkdf2 params are the same / hardcoded for the time being
+ assert new_hash.startswith('pbkdf2_sha256$100000$')
+ assert len(new_hash) == 110
+ # the hash should always be different because of random salting
+ assert new_hash != etoolkit.EtoolkitInstance.get_new_password_hash(
+ 'the very secret passwd'
+ )
+
+
+def test_parse_value():
+ """Tests the static EtoolkitInstance.parse_value method"""
+ assert (
+ etoolkit.EtoolkitInstance.parse_value('t%bs%t', {'%b': 'e', '%t': 't'})
+ == 'test'
+ )
+
+
+def test_password_matches(password_hash):
+ """Tests the static EtoolkitInstance.password_matches method"""
+ assert etoolkit.EtoolkitInstance.password_matches(
+ 'the very secret passwd', password_hash
+ )
+ assert not etoolkit.EtoolkitInstance.password_matches(
+ 'the very secret passwdo', password_hash
+ )
--
cgit v1.3