From bb9a844e22134a2537652ea14f93e82acb4ee380 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Tue, 30 Apr 2024 15:39:29 +0200 Subject: Improve tests --- src/etoolkit/__main__.py | 14 ++++-- src/etoolkit/etoolkit.py | 19 +++++--- tests/conftest.py | 35 +++++++++----- tests/test_cli.py | 67 ++++++++++++++++++-------- tests/test_envtoolkit_instance.py | 16 +++--- tests/test_envtoolkit_instance_static.py | 83 ++++++++++++++++++-------------- 6 files changed, 151 insertions(+), 83 deletions(-) diff --git a/src/etoolkit/__main__.py b/src/etoolkit/__main__.py index fba9dc7..8b5bd55 100644 --- a/src/etoolkit/__main__.py +++ b/src/etoolkit/__main__.py @@ -319,7 +319,13 @@ def main(inargs=None): sys.exit(0) inst = etoolkit.EtoolkitInstance(args.instance, config_dict) - inst.prompt_func = etoolkit.EtoolkitInstance.confirm_password_prompt + if ( + args.master_password_prompt + or os.environ.get('ETOOLKIT_MASTER_PASSWORD') is None + ): + inst.prompt_func = ( + etoolkit.EtoolkitInstance.confirm_password_prompt + ) env = inst.get_environ() if args.dump_output: @@ -328,9 +334,11 @@ def main(inargs=None): os.environ.update(env) if args.spawn: - subprocess.run(args.spawn.split(), check=True) + subprocess.run(args.spawn.split(), check=False) else: - subprocess.run(os.environ.get('SHELL', 'bash').split(), check=True) + subprocess.run( + os.environ.get('SHELL', 'bash').split(), check=False + ) except KeyboardInterrupt: logger.debug('KeyboardInterrupt') print(os.linesep) diff --git a/src/etoolkit/etoolkit.py b/src/etoolkit/etoolkit.py index 870a4be..a2e0d7a 100644 --- a/src/etoolkit/etoolkit.py +++ b/src/etoolkit/etoolkit.py @@ -385,7 +385,7 @@ class EtoolkitInstance: if self._parent is not None else '' ) - if isinstance(value, str) and value.startswith('enc-val$1$'): + if isinstance(value, str) and value.startswith('enc-val$'): value = self._decrypt_value(value) if isinstance(value, str) and value.endswith(':'): # if 'value' ends with ':', append the existing value of @@ -440,11 +440,16 @@ class EtoolkitInstance: """ if self._master_password is None: if self._prompt_func is None: - raise EtoolkitInstanceError( - 'Neither password or prompt function set' + if ( + mp_from_env := os.environ.get('ETOOLKIT_MASTER_PASSWORD') + ) is None: + raise EtoolkitInstanceError( + 'Neither password or prompt function set' + ) + self.master_password = mp_from_env + else: + # use master_password setter in order to propagate to parent + self.master_password = self._prompt_func( + self._master_password_hash, confirm=False ) - # use master_password setter in order to propagate to parent - self.master_password = self._prompt_func( - self._master_password_hash, confirm=False - ) return EtoolkitInstance.decrypt(self._master_password, evalue) diff --git a/tests/conftest.py b/tests/conftest.py index e555a49..cbf7152 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,26 +27,27 @@ def config_data(): return { 'general': { 'MASTER_PASSWORD_HASH': ( - 'pbkdf2_sha256$100000$uYpZM1VfAGq0CDZL2duITs076CQj+hIFEgx+F4m' - 'n80o=$h3PSPLCd37fP15zKdW4CBGn7CXE+q5UiydaF3vbeZHo=' + 'pbkdf2_sha256$500000$UY3o78KUM1Btzxk3k3JCsijnwtJ2lx+hH9Newp' + 'VKxo8=$tHwDm8OVKanC4DoYTigTCb0R3lQIa/CbBYj0B3TZtHg=' ) }, 'instances': { '_default': { 'ETOOLKIT_PROMPT': '(%i)', - 'PYTHONPATH': '/home/foo/%i/python', + 'ETOOLKIT_TEST_PYTHONPATH': '/home/foo/%i/python', }, 'dev': { 'ETOOLKIT_PARENT': '_default', - 'PYTHONPATH': '%p:/home/user/%i/.pythonpath', + 'ETOOLKIT_TEST_PYTHONPATH': '%p:/home/user/%i/.pythonpath', }, 'secret': { 'ETOOLKIT_PARENT': '_default', - 'ETOOLKIT_SENSITIVE': ['PASSWORD'], + 'ETOOLKIT_SENSITIVE': ['ETOOLKIT_TEST_PASSWORD'], 'GNUPGHOME': '%h/private/.gnupg', - 'PASSWORD': ( - 'enc-val$1$vIBcoCNiYrsDLtF41uLuSEnppBjhliD0B8jwcBJcj/c=$Kw' - 'OGe/y1dlxktDaCnJPIVNuaQ4Q7yNo=' + 'ETOOLKIT_TEST_PASSWORD': ( + 'enc-val$2$v6F2M7LeUDbQWNLg6WW5mUcbuYYo7aGynSxzWAENVBI=$' + 'ZcyWzf9Kp0aYI8N+biKMSmu4RGGi199ayq' + 'EYdJl+qdq7b1HSutwYlC7UR2GsSofu4Xo=' ), }, }, @@ -55,13 +56,19 @@ def config_data(): @pytest.fixture() def config_file(tmp_path, config_data): - """temporary config file for testing that includes config_data""" + """Temporary config file for testing that includes config_data""" cf = tmp_path / 'etoolkit.json' cf.write_text(json.dumps(config_data)) return str(cf) +@pytest.fixture() +def master_password(): + """Master passord""" + return 'The very secret passwd' + + @pytest.fixture() def non_random_bytes_32(): """always use the same bytes instead of os.urandom(32)""" @@ -95,6 +102,12 @@ def password_hash(): """password hash for testing, corresponding to 'The very secret passwd'""" return ( - 'pbkdf2_sha256$100000$uYpZM1VfAGq0CDZL2duITs076CQj+hIFEgx+F4mn80o=$h3' - 'PSPLCd37fP15zKdW4CBGn7CXE+q5UiydaF3vbeZHo=' + 'pbkdf2_sha256$500000$UY3o78KUM1Btzxk3k3JCsijnwtJ2lx+hH9NewpVKxo8=$' + 'tHwDm8OVKanC4DoYTigTCb0R3lQIa/CbBYj0B3TZtHg=' ) + + +@pytest.fixture() +def wrong_master_password(): + """Wrong master passord""" + return 'the very secret passwd' diff --git a/tests/test_cli.py b/tests/test_cli.py index e5db244..1693a2b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -26,14 +26,15 @@ from etoolkit.__main__ import main @unittest.mock.patch('builtins.input') -def test_decrypt_v1(binput, capsys, config_file): +def test_decrypt_v1(binput, capsys, config_file, master_password): """Tests v1 decryption via the CLI interface""" + binput.return_value = ( - 'enc-val$1$uYpZM1VfAGq0CDZL2duITs076CQj+' - 'hIFEgx+F4mn80o=$xdF/1S+R2MGlEQMCOLG6OjEuzw==' + 'enc-val$1$rye0sMGEnd35gOWyISE1FQa6dzS+8/jf6aopMO5tPr4=$' + 'RjnRY0bUJWFOiejTlM3OhKNimQ==' ) with unittest.mock.patch.dict( - os.environ, {'ETOOLKIT_MASTER_PASSWORD': 'the very secret passwd'} + os.environ, {'ETOOLKIT_MASTER_PASSWORD': master_password} ): with pytest.raises(SystemExit) as exit_info: main(['-c', f'{config_file}', '-d']) @@ -43,15 +44,16 @@ def test_decrypt_v1(binput, capsys, config_file): @unittest.mock.patch('builtins.input') -def test_decrypt_v2(binput, capsys, config_file): +def test_decrypt_v2(binput, capsys, config_file, master_password): """Tests v2 decryption via the CLI interface""" + binput.return_value = ( 'enc-val$2$RCSZqq9pWrRDoCVYVHopyu1LzaJGfv8roVviq' - 'rLTBxM=$VW3UZ6l12yDtyaqWHb7i0QEDiS9s9np' - '7huAACK54BtZVV7RZoIhbu4K6zZuz+LRCyio=' + 'rLTBxM=$+Yo6Ya2MAVcBLTQHuATkyFc+dzYsL/E' + 'SvA6ofOUDsiKZvIff35cUHAmoNxVuGG+MXv4=' ) with unittest.mock.patch.dict( - os.environ, {'ETOOLKIT_MASTER_PASSWORD': 'the very secret passwd'} + os.environ, {'ETOOLKIT_MASTER_PASSWORD': master_password} ): with pytest.raises(SystemExit) as exit_info: main(['-c', f'{config_file}', '-d']) @@ -61,12 +63,16 @@ def test_decrypt_v2(binput, capsys, config_file): @unittest.mock.patch('os.urandom') -@unittest.mock.patch('builtins.input', lambda *args: 'bar') -def test_encrypt_with_echo(urandom, capsys, non_random_bytes_61, config_file): +@unittest.mock.patch('builtins.input') +def test_encrypt_with_echo( + binput, urandom, capsys, non_random_bytes_61, config_file, master_password +): """Tests encryption via the CLI interface""" + + binput.return_value = 'bar' urandom.return_value = non_random_bytes_61 with unittest.mock.patch.dict( - os.environ, {'ETOOLKIT_MASTER_PASSWORD': 'the very secret passwd'} + os.environ, {'ETOOLKIT_MASTER_PASSWORD': master_password} ): with pytest.raises(SystemExit) as exit_info: main(['-c', f'{config_file}', '-e', '-E']) @@ -74,18 +80,22 @@ def test_encrypt_with_echo(urandom, capsys, non_random_bytes_61, config_file): assert exit_info.value.code == 0 assert capsys.readouterr().out.strip() == ( 'Encrypted value: enc-val$2$RCSZqq9pWrRDoCVYVHopyu1LzaJGfv8roVviq' - 'rLTBxM=$VW3UZ6l12yDtyaqWHb7i0QEDiS9s9np' - '7huAACK54BtZVV7RZoIhbu4K6zZuz+LRCyio=' + 'rLTBxM=$+Yo6Ya2MAVcBLTQHuATkyFc+dzYsL/E' + 'SvA6ofOUDsiKZvIff35cUHAmoNxVuGG+MXv4=' ) @unittest.mock.patch('os.urandom') -@unittest.mock.patch('getpass.getpass', lambda *args: 'bar') -def test_encrypt_without_echo(gpass, capsys, non_random_bytes_61, config_file): +@unittest.mock.patch('getpass.getpass') +def test_encrypt_without_echo( + getpass, urandom, capsys, non_random_bytes_61, config_file, master_password +): """Tests encryption via the CLI interface""" - gpass.return_value = non_random_bytes_61 + + getpass.return_value = 'bar' + urandom.return_value = non_random_bytes_61 with unittest.mock.patch.dict( - os.environ, {'ETOOLKIT_MASTER_PASSWORD': 'the very secret passwd'} + os.environ, {'ETOOLKIT_MASTER_PASSWORD': master_password} ): with pytest.raises(SystemExit) as exit_info: main(['-c', f'{config_file}', '-e']) @@ -93,13 +103,25 @@ def test_encrypt_without_echo(gpass, capsys, non_random_bytes_61, config_file): assert exit_info.value.code == 0 assert capsys.readouterr().out.strip() == ( 'Encrypted value: enc-val$2$RCSZqq9pWrRDoCVYVHopyu1LzaJGfv8roVviq' - 'rLTBxM=$VW3UZ6l12yDtyaqWHb7i0QEDiS9s9np' - '7huAACK54BtZVV7RZoIhbu4K6zZuz+LRCyio=' + 'rLTBxM=$+Yo6Ya2MAVcBLTQHuATkyFc+dzYsL/E' + 'SvA6ofOUDsiKZvIff35cUHAmoNxVuGG+MXv4=' ) +def test_fetch_encrypted_value(config_file, master_password): + """Tests decryption of encrypted value""" + + with unittest.mock.patch.dict( + os.environ, {'ETOOLKIT_MASTER_PASSWORD': master_password} + ): + assert os.environ.get('ETOOLKIT_TEST_PASSWORD') is None + main(['-c', f'{config_file}', '-q', '-s', '/bin/false', 'secret']) + assert os.environ.get('ETOOLKIT_TEST_PASSWORD') == 'bar' + + def test_list(capsys, config_file, nonexistent_config_file): """Tests list via the CLI interface""" + with pytest.raises(SystemExit) as exit_info: main(['-c', nonexistent_config_file, '-l']) assert exit_info.type == SystemExit @@ -113,6 +135,7 @@ def test_list(capsys, config_file, nonexistent_config_file): 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 @@ -123,11 +146,12 @@ def test_help(capsys): @unittest.mock.patch('os.urandom') @unittest.mock.patch('getpass.getpass') def test_generate_master_password_hash( - gpass, urandom, capsys, non_random_bytes_32 + getpass, urandom, capsys, non_random_bytes_32 ): """Tests master password hash generation via the CLI interface""" + + getpass.return_value = 'The very secret passwd' urandom.return_value = non_random_bytes_32 - gpass.return_value = 'The very secret passwd' with pytest.raises(SystemExit) as exit_info: main(['--generate-master-password-hash']) assert exit_info.type == SystemExit @@ -140,6 +164,7 @@ def test_generate_master_password_hash( 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 diff --git a/tests/test_envtoolkit_instance.py b/tests/test_envtoolkit_instance.py index 73faed6..0147c6f 100644 --- a/tests/test_envtoolkit_instance.py +++ b/tests/test_envtoolkit_instance.py @@ -22,6 +22,7 @@ import etoolkit def test_instantiation(config_data): """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 @@ -31,7 +32,7 @@ def test_instantiation(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 'ETOOLKIT_TEST_PASSWORD' in instance.sensitive_env_variables assert instance.name == 'secret' assert ( instance.master_password_hash @@ -40,15 +41,16 @@ def test_instantiation(config_data): assert instance.master_password is None -def test_get_environ(config_data): +def test_get_environ(config_data, master_password, wrong_master_password): """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 + instance.master_password = wrong_master_password with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info: env = instance.get_environ() assert exc_info.type is etoolkit.EtoolkitInstanceError @@ -56,22 +58,24 @@ def test_get_environ(config_data): 'Invalid tag when decrypting: enc-val$' ) - instance.master_password = 'the very secret passwd' # correct passwd + instance.master_password = master_password env = instance.get_environ() assert isinstance(env, dict) - assert env['PASSWORD'] == 'secret2' + assert env['ETOOLKIT_TEST_PASSWORD'] == 'bar' 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' def test_parent_vars(config_data): """Tests the EtoolkitInstance.get_full_name method""" + instance = etoolkit.EtoolkitInstance('dev', config_data) assert instance.get_full_name('->') == '_default->dev' - assert instance.get_environ()['PYTHONPATH'] == ( + assert instance.get_environ()['ETOOLKIT_TEST_PYTHONPATH'] == ( '/home/foo/_default/python:/home/user/dev/.pythonpath' ) diff --git a/tests/test_envtoolkit_instance_static.py b/tests/test_envtoolkit_instance_static.py index 5fb9451..b3b24f0 100644 --- a/tests/test_envtoolkit_instance_static.py +++ b/tests/test_envtoolkit_instance_static.py @@ -23,24 +23,26 @@ import etoolkit @unittest.mock.patch('getpass.getpass') -def test_confirm_password_prompt(getpass, password_hash): +def test_confirm_password_prompt(getpass, password_hash, master_password): """Tests the static EtoolkitInstance.confirm_password_prompt method""" - getpass.return_value = 'The very secret passwd' + + getpass.return_value = master_password assert ( etoolkit.EtoolkitInstance.confirm_password_prompt(password_hash) - == 'The very secret passwd' + == master_password ) assert ( etoolkit.EtoolkitInstance.confirm_password_prompt(password_hash, False) - == 'The very secret passwd' + == master_password ) -def test_decrypt_v1(): +def test_decrypt_v1(master_password): """Tests the static EtoolkitInstance.decrypt method""" + assert ( etoolkit.EtoolkitInstance.decrypt( - 'The very secret passwd', + master_password, ( 'enc-val$1$/cXpEMoZrTlb9yokGhw8tLTSUkqnqJ4ZoAkurNgMYx' 'w=$1VdkSMcZnLRwLiu1M8VlYcbelwmiVNY=' @@ -55,16 +57,17 @@ def test_decrypt_v1(): 'w=$1VdkSMcZnLRwLiu1M8VlYcbelwmiVNY=' ) with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info: - etoolkit.EtoolkitInstance.decrypt('The very secret passwd', edata) + etoolkit.EtoolkitInstance.decrypt(master_password, edata) assert exc_info.type is etoolkit.EtoolkitInstanceError assert exc_info.value.args[0] == f'Invalid tag when decrypting: {edata}' -def test_decrypt_v2_no_padding(): +def test_decrypt_v2_no_padding(master_password): """Tests the static EtoolkitInstance.decrypt method for v2 - no padding""" + assert ( etoolkit.EtoolkitInstance.decrypt( - 'The very secret passwd', + master_password, ( 'enc-val$2$Wer5lECGyeZhhYS58N18WVx5Zzy+rrC+BPlq3Dw89wQ=$' 'SQc0ox6Emf2m5rrumsiptpIZEujdpXXSR/' @@ -81,16 +84,17 @@ def test_decrypt_v2_no_padding(): '1VcfEZeBz4+KDSagr9ID+bkc4R2yFdxHnhig1eqQ8=' ) with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info: - etoolkit.EtoolkitInstance.decrypt('The very secret passwd', edata) + etoolkit.EtoolkitInstance.decrypt(master_password, edata) assert exc_info.type is etoolkit.EtoolkitInstanceError assert exc_info.value.args[0] == f'Invalid tag when decrypting: {edata}' -def test_decrypt_v2_with_padding(): +def test_decrypt_v2_with_padding(master_password): """Tests the static EtoolkitInstance.decrypt method for v2 with padding""" + assert ( etoolkit.EtoolkitInstance.decrypt( - 'The very secret passwd', + master_password, ( 'enc-val$2$//kzyUbDEWNoPC5dyukhB8de8+IVaLR2ngx2HwkfOuM=$' 'rhRona4wP9nhnXjcHqwkjFDsiVVVjYanAs' @@ -106,39 +110,44 @@ def test_decrypt_v2_with_padding(): 'rhRona4wP8nhnXjcHqwkjFDsiVVVjYanAsN4kknNkgC0ix4RtJQHYDeTzw1rrR1vb2w=' ) with pytest.raises(etoolkit.EtoolkitInstanceError) as exc_info: - etoolkit.EtoolkitInstance.decrypt('The very secret passwd', edata) + etoolkit.EtoolkitInstance.decrypt(master_password, edata) assert exc_info.type is etoolkit.EtoolkitInstanceError assert exc_info.value.args[0] == f'Invalid tag when decrypting: {edata}' -def test_encrypt_no_padding(): +def test_encrypt_no_padding(master_password): """Tests the static EtoolkitInstance.encrypt method with a long string""" + edata = etoolkit.EtoolkitInstance.encrypt( - 'foo', 'Nobody expects the Spanish inquisition' + master_password, 'Nobody expects the Spanish inquisition' ) assert edata.startswith('enc-val$2$') assert len(edata) == 131 # the edata should always be different because of random salting assert edata != etoolkit.EtoolkitInstance.encrypt( - 'foo', 'Nobody expects the Spanish inquisition' + master_password, 'Nobody expects the Spanish inquisition' ) -def test_encrypt_with_padding(): +def test_encrypt_with_padding(master_password): """Tests the static EtoolkitInstance.encrypt method with a short string""" - edata = etoolkit.EtoolkitInstance.encrypt('foo', 'bar') + + edata = etoolkit.EtoolkitInstance.encrypt(master_password, 'bar') assert edata.startswith('enc-val$2$') assert len(edata) == 123 # the edata should always be different because of random salting - assert edata != etoolkit.EtoolkitInstance.encrypt('foo', 'bar') + assert edata != etoolkit.EtoolkitInstance.encrypt(master_password, 'bar') @unittest.mock.patch('os.urandom') -def test_encrypt_staticly_no_padding(urandom, non_random_bytes_32): +def test_encrypt_staticly_no_padding( + urandom, master_password, non_random_bytes_32 +): """Tests the EtoolkitInstance.encrypt method always with the same salt""" + urandom.return_value = non_random_bytes_32 edata = etoolkit.EtoolkitInstance.encrypt( - 'The very secret passwd', 'Nobody expects the Spanish inquisition' + master_password, 'Nobody expects the Spanish inquisition' ) assert edata == ( 'enc-val$2$uYpZM1VfAGq0CDZL2duITs076CQj+hIFEgx+F4mn80o=$' @@ -147,51 +156,55 @@ def test_encrypt_staticly_no_padding(urandom, non_random_bytes_32): ) assert len(edata) == 131 assert edata == etoolkit.EtoolkitInstance.encrypt( - 'The very secret passwd', 'Nobody expects the Spanish inquisition' + master_password, 'Nobody expects the Spanish inquisition' ) @unittest.mock.patch('os.urandom') -def test_encrypt_staticly_with_padding(urandom, non_random_bytes_61): +def test_encrypt_staticly_with_padding( + urandom, master_password, non_random_bytes_61 +): """Tests the EtoolkitInstance.encrypt method always with the same salt""" + urandom.return_value = non_random_bytes_61 - edata = etoolkit.EtoolkitInstance.encrypt('The very secret passwd', 'bar') + edata = etoolkit.EtoolkitInstance.encrypt(master_password, 'bar') assert edata == ( 'enc-val$2$RCSZqq9pWrRDoCVYVHopyu1LzaJGfv8roVviqrLTBxM=$' '+Yo6Ya2MAVcBLTQHuATkyFc+dzYsL/ESvA6ofOUDsiKZvIff35cUHAmoNxVuGG+MXv4=' ) - assert edata == etoolkit.EtoolkitInstance.encrypt( - 'The very secret passwd', 'bar' - ) + assert edata == etoolkit.EtoolkitInstance.encrypt(master_password, 'bar') -def test_get_new_password_hash(): +def test_get_new_password_hash(master_password): """Tests the static EtoolkitInstance.get_new_password_hash method""" - new_hash = etoolkit.EtoolkitInstance.get_new_password_hash( - 'The very secret passwd' - ) + + new_hash = etoolkit.EtoolkitInstance.get_new_password_hash(master_password) # all pbkdf2 params are the same / hardcoded for the time being assert new_hash.startswith('pbkdf2_sha256$500000$') 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' + master_password ) 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): +def test_password_matches( + password_hash, master_password, wrong_master_password +): """Tests the static EtoolkitInstance.password_matches method""" + assert etoolkit.EtoolkitInstance.password_matches( - 'The very secret passwd', password_hash + master_password, password_hash ) assert not etoolkit.EtoolkitInstance.password_matches( - 'The very secret passwdo', password_hash + wrong_master_password, password_hash ) -- cgit v1.3