From 609751bf663ba6190f188ef2e061235fe95de154 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 2 Jul 2025 23:09:10 -0400 Subject: [PATCH] Update tests for entries schema --- src/password_manager/backup.py | 8 +++--- src/password_manager/encryption.py | 18 ++++++------ src/password_manager/entry_management.py | 6 ++-- src/password_manager/manager.py | 2 +- src/password_manager/vault.py | 2 +- src/tests/test_backup_restore.py | 6 ++-- src/tests/test_encryption_checksum.py | 4 +-- src/tests/test_encryption_files.py | 2 +- src/tests/test_entry_add.py | 28 +++++++++++++++++++ .../test_entry_management_checksum_path.py | 4 +-- src/tests/test_manager_workflow.py | 6 ++-- src/tests/test_migrations.py | 15 ++++++++++ src/tests/test_password_properties.py | 3 ++ 13 files changed, 75 insertions(+), 29 deletions(-) diff --git a/src/password_manager/backup.py b/src/password_manager/backup.py index c094ab3..5a9e5c8 100644 --- a/src/password_manager/backup.py +++ b/src/password_manager/backup.py @@ -35,7 +35,7 @@ class BackupManager: timestamped filenames to facilitate easy identification and retrieval. """ - BACKUP_FILENAME_TEMPLATE = "passwords_db_backup_{timestamp}.json.enc" + BACKUP_FILENAME_TEMPLATE = "entries_db_backup_{timestamp}.json.enc" def __init__(self, fingerprint_dir: Path): """ @@ -47,7 +47,7 @@ class BackupManager: self.fingerprint_dir = fingerprint_dir self.backup_dir = self.fingerprint_dir / "backups" self.backup_dir.mkdir(parents=True, exist_ok=True) - self.index_file = self.fingerprint_dir / "seedpass_passwords_db.json.enc" + self.index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc" logger.debug( f"BackupManager initialized with backup directory at {self.backup_dir}" ) @@ -79,7 +79,7 @@ class BackupManager: def restore_latest_backup(self) -> None: try: backup_files = sorted( - self.backup_dir.glob("passwords_db_backup_*.json.enc"), + self.backup_dir.glob("entries_db_backup_*.json.enc"), key=lambda x: x.stat().st_mtime, reverse=True, ) @@ -112,7 +112,7 @@ class BackupManager: def list_backups(self) -> None: try: backup_files = sorted( - self.backup_dir.glob("passwords_db_backup_*.json.enc"), + self.backup_dir.glob("entries_db_backup_*.json.enc"), key=lambda x: x.stat().st_mtime, reverse=True, ) diff --git a/src/password_manager/encryption.py b/src/password_manager/encryption.py index cd4cd73..4175c19 100644 --- a/src/password_manager/encryption.py +++ b/src/password_manager/encryption.py @@ -246,10 +246,10 @@ class EncryptionManager: :param data: The JSON data to save. :param relative_path: The relative path within the fingerprint directory where data will be saved. - Defaults to 'seedpass_passwords_db.json.enc'. + Defaults to 'seedpass_entries_db.json.enc'. """ if relative_path is None: - relative_path = Path("seedpass_passwords_db.json.enc") + relative_path = Path("seedpass_entries_db.json.enc") try: json_data = json.dumps(data, indent=4).encode("utf-8") self.encrypt_and_save_file(json_data, relative_path) @@ -273,11 +273,11 @@ class EncryptionManager: Decrypts and loads JSON data from the specified relative path within the fingerprint directory. :param relative_path: The relative path within the fingerprint directory from which data will be loaded. - Defaults to 'seedpass_passwords_db.json.enc'. + Defaults to 'seedpass_entries_db.json.enc'. :return: The decrypted JSON data as a dictionary. """ if relative_path is None: - relative_path = Path("seedpass_passwords_db.json.enc") + relative_path = Path("seedpass_entries_db.json.enc") file_path = self.fingerprint_dir / relative_path @@ -320,10 +320,10 @@ class EncryptionManager: Updates the checksum file for the specified file within the fingerprint directory. :param relative_path: The relative path within the fingerprint directory for which the checksum will be updated. - Defaults to 'seedpass_passwords_db.json.enc'. + Defaults to 'seedpass_entries_db.json.enc'. """ if relative_path is None: - relative_path = Path("seedpass_passwords_db.json.enc") + relative_path = Path("seedpass_entries_db.json.enc") try: file_path = self.fingerprint_dir / relative_path logger.debug("Calculating checksum of the encrypted file bytes.") @@ -368,7 +368,7 @@ class EncryptionManager: :return: Encrypted data as bytes or None if the index file does not exist. """ try: - relative_path = Path("seedpass_passwords_db.json.enc") + relative_path = Path("seedpass_entries_db.json.enc") if not (self.fingerprint_dir / relative_path).exists(): logger.error( f"Index file '{relative_path}' does not exist in '{self.fingerprint_dir}'." @@ -407,10 +407,10 @@ class EncryptionManager: :param encrypted_data: The encrypted data retrieved from Nostr. :param relative_path: The relative path within the fingerprint directory to update. - Defaults to 'seedpass_passwords_db.json.enc'. + Defaults to 'seedpass_entries_db.json.enc'. """ if relative_path is None: - relative_path = Path("seedpass_passwords_db.json.enc") + relative_path = Path("seedpass_entries_db.json.enc") try: decrypted_data = self.decrypt_data(encrypted_data) data = json.loads(decrypted_data.decode("utf-8")) diff --git a/src/password_manager/entry_management.py b/src/password_manager/entry_management.py index 911b854..9c9fc16 100644 --- a/src/password_manager/entry_management.py +++ b/src/password_manager/entry_management.py @@ -50,8 +50,8 @@ class EntryManager: self.fingerprint_dir = fingerprint_dir # Use paths relative to the fingerprint directory - self.index_file = self.fingerprint_dir / "seedpass_passwords_db.json.enc" - self.checksum_file = self.fingerprint_dir / "seedpass_passwords_db_checksum.txt" + self.index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc" + self.checksum_file = self.fingerprint_dir / "seedpass_entries_db_checksum.txt" logger.debug(f"EntryManager initialized with index file at {self.index_file}") @@ -410,7 +410,7 @@ class EntryManager: return timestamp = int(time.time()) - backup_filename = f"passwords_db_backup_{timestamp}.json.enc" + backup_filename = f"entries_db_backup_{timestamp}.json.enc" backup_path = self.fingerprint_dir / backup_filename with open(index_file_path, "rb") as original_file, open( diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index b0595e5..444ffaf 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -769,7 +769,7 @@ class PasswordManager: def sync_index_from_nostr_if_missing(self) -> None: """Retrieve the password database from Nostr if it doesn't exist locally.""" - index_file = self.fingerprint_dir / "seedpass_passwords_db.json.enc" + index_file = self.fingerprint_dir / "seedpass_entries_db.json.enc" if index_file.exists(): return try: diff --git a/src/password_manager/vault.py b/src/password_manager/vault.py index a3cfeef..78d8b99 100644 --- a/src/password_manager/vault.py +++ b/src/password_manager/vault.py @@ -10,7 +10,7 @@ from .encryption import EncryptionManager class Vault: """Simple wrapper around :class:`EncryptionManager` for vault storage.""" - INDEX_FILENAME = "seedpass_passwords_db.json.enc" + INDEX_FILENAME = "seedpass_entries_db.json.enc" CONFIG_FILENAME = "seedpass_config.json.enc" def __init__( diff --git a/src/tests/test_backup_restore.py b/src/tests/test_backup_restore.py index dbc1a24..ac61b46 100644 --- a/src/tests/test_backup_restore.py +++ b/src/tests/test_backup_restore.py @@ -17,7 +17,7 @@ def test_backup_restore_workflow(monkeypatch): vault, enc_mgr = create_vault(fp_dir, TEST_SEED, TEST_PASSWORD) backup_mgr = BackupManager(fp_dir) - index_file = fp_dir / "seedpass_passwords_db.json.enc" + index_file = fp_dir / "seedpass_entries_db.json.enc" data1 = { "schema_version": 2, @@ -30,7 +30,7 @@ def test_backup_restore_workflow(monkeypatch): monkeypatch.setattr(time, "time", lambda: 1111) backup_mgr.create_backup() - backup1 = fp_dir / "backups" / "passwords_db_backup_1111.json.enc" + backup1 = fp_dir / "backups" / "entries_db_backup_1111.json.enc" assert backup1.exists() assert backup1.stat().st_mode & 0o777 == 0o600 @@ -45,7 +45,7 @@ def test_backup_restore_workflow(monkeypatch): monkeypatch.setattr(time, "time", lambda: 2222) backup_mgr.create_backup() - backup2 = fp_dir / "backups" / "passwords_db_backup_2222.json.enc" + backup2 = fp_dir / "backups" / "entries_db_backup_2222.json.enc" assert backup2.exists() assert backup2.stat().st_mode & 0o777 == 0o600 diff --git a/src/tests/test_encryption_checksum.py b/src/tests/test_encryption_checksum.py index 0922e8d..127f503 100644 --- a/src/tests/test_encryption_checksum.py +++ b/src/tests/test_encryption_checksum.py @@ -21,8 +21,8 @@ def test_encryption_checksum_workflow(): manager.save_json_data(data) manager.update_checksum() - enc_file = tmp_path / "seedpass_passwords_db.json.enc" - chk_file = tmp_path / "seedpass_passwords_db.json_checksum.txt" + enc_file = tmp_path / "seedpass_entries_db.json.enc" + chk_file = tmp_path / "seedpass_entries_db.json_checksum.txt" checksum = chk_file.read_text().strip() assert re.fullmatch(r"[0-9a-f]{64}", checksum) diff --git a/src/tests/test_encryption_files.py b/src/tests/test_encryption_files.py index 3b93e02..0b7ddbe 100644 --- a/src/tests/test_encryption_files.py +++ b/src/tests/test_encryption_files.py @@ -20,7 +20,7 @@ def test_json_save_and_load_round_trip(): loaded = manager.load_json_data() assert loaded == data - file_path = Path(tmpdir) / "seedpass_passwords_db.json.enc" + file_path = Path(tmpdir) / "seedpass_entries_db.json.enc" raw = file_path.read_bytes() assert raw != json.dumps(data, indent=4).encode("utf-8") diff --git a/src/tests/test_entry_add.py b/src/tests/test_entry_add.py index eac6db8..fce73f4 100644 --- a/src/tests/test_entry_add.py +++ b/src/tests/test_entry_add.py @@ -1,6 +1,7 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory +import pytest from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) @@ -30,3 +31,30 @@ def test_add_and_retrieve_entry(): data = enc_mgr.load_json_data(entry_mgr.index_file) assert str(index) in data.get("entries", {}) assert data["entries"][str(index)] == entry + + +@pytest.mark.parametrize( + "method, expected_type", + [ + ("add_entry", "password"), + ("add_totp", "totp"), + ("add_ssh_key", "ssh"), + ("add_seed", "seed"), + ], +) +def test_round_trip_entry_types(method, expected_type): + with TemporaryDirectory() as tmpdir: + vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) + entry_mgr = EntryManager(vault, Path(tmpdir)) + + if method == "add_entry": + index = entry_mgr.add_entry("example.com", 8) + else: + with pytest.raises(NotImplementedError): + getattr(entry_mgr, method)() + index = 0 + + entry = entry_mgr.retrieve_entry(index) + assert entry["type"] == expected_type + data = enc_mgr.load_json_data(entry_mgr.index_file) + assert data["entries"][str(index)]["type"] == expected_type diff --git a/src/tests/test_entry_management_checksum_path.py b/src/tests/test_entry_management_checksum_path.py index fb48aee..c002008 100644 --- a/src/tests/test_entry_management_checksum_path.py +++ b/src/tests/test_entry_management_checksum_path.py @@ -19,7 +19,7 @@ def test_update_checksum_writes_to_expected_path(): vault.save_index({"entries": {}}) entry_mgr.update_checksum() - expected = tmp_path / "seedpass_passwords_db_checksum.txt" + expected = tmp_path / "seedpass_entries_db_checksum.txt" assert expected.exists() @@ -32,5 +32,5 @@ def test_backup_index_file_creates_backup_in_directory(): vault.save_index({"entries": {}}) entry_mgr.backup_index_file() - backups = list(tmp_path.glob("passwords_db_backup_*.json.enc")) + backups = list(tmp_path.glob("entries_db_backup_*.json.enc")) assert len(backups) == 1 diff --git a/src/tests/test_manager_workflow.py b/src/tests/test_manager_workflow.py index 6b0833c..d78d43a 100644 --- a/src/tests/test_manager_workflow.py +++ b/src/tests/test_manager_workflow.py @@ -64,9 +64,9 @@ def test_manager_workflow(monkeypatch): pm.handle_add_password() assert pm.is_dirty is False - backups = list(tmp_path.glob("passwords_db_backup_*.json.enc")) + backups = list(tmp_path.glob("entries_db_backup_*.json.enc")) assert len(backups) == 1 - checksum_file = tmp_path / "seedpass_passwords_db_checksum.txt" + checksum_file = tmp_path / "seedpass_entries_db_checksum.txt" assert checksum_file.exists() checksum_after_add = checksum_file.read_text() first_post = pm.nostr_client.published[-1] @@ -79,7 +79,7 @@ def test_manager_workflow(monkeypatch): assert pm.is_dirty is False pm.backup_manager.create_backup() backup_dir = tmp_path / "backups" - backups_mod = list(backup_dir.glob("passwords_db_backup_*.json.enc")) + backups_mod = list(backup_dir.glob("entries_db_backup_*.json.enc")) assert backups_mod checksum_after_modify = checksum_file.read_text() assert checksum_after_modify != checksum_after_add diff --git a/src/tests/test_migrations.py b/src/tests/test_migrations.py index feb71aa..a1cd7e4 100644 --- a/src/tests/test_migrations.py +++ b/src/tests/test_migrations.py @@ -23,6 +23,21 @@ def test_migrate_v0_to_v2(tmp_path: Path): assert data["entries"]["0"] == expected_entry +def test_migrate_v1_to_v2(tmp_path: Path): + enc_mgr, vault = setup(tmp_path) + legacy = {"schema_version": 1, "passwords": {"0": {"website": "b", "length": 10}}} + enc_mgr.save_json_data(legacy) + data = vault.load_index() + assert data["schema_version"] == LATEST_VERSION + expected_entry = { + "website": "b", + "length": 10, + "type": "password", + "notes": "", + } + assert data["entries"]["0"] == expected_entry + + def test_error_on_future_version(tmp_path: Path): enc_mgr, vault = setup(tmp_path) future = {"schema_version": LATEST_VERSION + 1, "entries": {}} diff --git a/src/tests/test_password_properties.py b/src/tests/test_password_properties.py index aa48621..f51dd5f 100644 --- a/src/tests/test_password_properties.py +++ b/src/tests/test_password_properties.py @@ -6,6 +6,7 @@ from hypothesis import given, strategies as st, settings sys.path.append(str(Path(__file__).resolve().parents[1])) from password_manager.password_generation import PasswordGenerator +from password_manager.entry_types import EntryType class DummyEnc: @@ -32,8 +33,10 @@ def make_generator(): @settings(deadline=None) def test_password_properties(length, index): pg = make_generator() + entry_type = EntryType.PASSWORD.value pw1 = pg.generate_password(length=length, index=index) pw2 = pg.generate_password(length=length, index=index) + assert entry_type == "password" assert pw1 == pw2 assert len(pw1) == length