import sys from pathlib import Path from tempfile import TemporaryDirectory from unittest.mock import patch import pytest from helpers import create_vault, TEST_SEED, TEST_PASSWORD sys.path.append(str(Path(__file__).resolve().parents[1])) from seedpass.core.entry_management import EntryManager from seedpass.core.backup import BackupManager from seedpass.core.vault import Vault from seedpass.core.config_manager import ConfigManager from seedpass.core.totp import TotpManager import pyotp def test_add_totp_and_get_code(): with TemporaryDirectory() as tmpdir: vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) backup_mgr = BackupManager(Path(tmpdir), cfg_mgr) entry_mgr = EntryManager(vault, backup_mgr) uri = entry_mgr.add_totp("Example", TEST_SEED) assert uri.startswith("otpauth://totp/") entry = entry_mgr.retrieve_entry(0) assert entry["deterministic"] is False assert "secret" in entry code = entry_mgr.get_totp_code(0, timestamp=0) expected = pyotp.TOTP(entry["secret"]).at(0) assert code == expected # second entry should have different secret entry_mgr.add_totp("Other", TEST_SEED) entry2 = entry_mgr.retrieve_entry(1) assert entry["secret"] != entry2["secret"] def test_totp_time_remaining(monkeypatch): with TemporaryDirectory() as tmpdir: vault, enc_mgr = create_vault(Path(tmpdir), TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, Path(tmpdir)) backup_mgr = BackupManager(Path(tmpdir), cfg_mgr) entry_mgr = EntryManager(vault, backup_mgr) entry_mgr.add_totp("Example", TEST_SEED) monkeypatch.setattr(TotpManager, "time_remaining", lambda period: 7) remaining = entry_mgr.get_totp_time_remaining(0) assert remaining == 7 def test_add_totp_imported(tmp_path): vault, enc = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, tmp_path) backup_mgr = BackupManager(tmp_path, cfg_mgr) em = EntryManager(vault, backup_mgr) secret = "JBSWY3DPEHPK3PXP" em.add_totp("Imported", TEST_SEED, secret=secret) entry = em.retrieve_entry(0) assert entry["secret"] == secret assert entry["deterministic"] is False code = em.get_totp_code(0, timestamp=0) assert code == pyotp.TOTP(secret).at(0) def test_add_totp_with_notes(tmp_path): vault, _ = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, tmp_path) backup_mgr = BackupManager(tmp_path, cfg_mgr) em = EntryManager(vault, backup_mgr) em.add_totp("NoteLabel", TEST_SEED, notes="some note") entry = em.retrieve_entry(0) assert entry["notes"] == "some note" def test_legacy_deterministic_entry(tmp_path): vault, enc = create_vault(tmp_path, TEST_SEED, TEST_PASSWORD) cfg_mgr = ConfigManager(vault, tmp_path) backup_mgr = BackupManager(tmp_path, cfg_mgr) em = EntryManager(vault, backup_mgr) em.add_totp("Legacy", TEST_SEED, deterministic=True) data = em._load_index() entry = data["entries"]["0"] entry.pop("deterministic", None) em._save_index(data) code = em.get_totp_code(0, TEST_SEED, timestamp=0) expected = TotpManager.current_code(TEST_SEED, 0, timestamp=0) assert code == expected exported = em.export_totp_entries(TEST_SEED) assert exported["entries"][0]["secret"] == TotpManager.derive_secret(TEST_SEED, 0)