From b46943f7f8070aaa29ce9ddc64dde9c8ba534462 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 11 Jul 2025 15:23:31 -0400 Subject: [PATCH] Support fingerprint option in legacy CLI --- src/main.py | 15 ++++++++++--- src/seedpass/cli.py | 2 +- src/tests/test_cli_export_import.py | 4 ++-- src/tests/test_cli_subcommands.py | 34 ++++++++++++++++++++++------- src/tests/test_typer_cli.py | 33 ++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/main.py b/src/main.py index 3767a37..8085c53 100644 --- a/src/main.py +++ b/src/main.py @@ -919,8 +919,16 @@ def display_menu( print(colored("Invalid choice. Please select a valid option.", "red")) -def main(argv: list[str] | None = None) -> int: - """Entry point for the SeedPass CLI.""" +def main(argv: list[str] | None = None, *, fingerprint: str | None = None) -> int: + """Entry point for the SeedPass CLI. + + Parameters + ---------- + argv: + Command line arguments. + fingerprint: + Optional seed profile fingerprint to select automatically. + """ configure_logging() initialize_app() logger = logging.getLogger(__name__) @@ -928,6 +936,7 @@ def main(argv: list[str] | None = None) -> int: load_global_config() parser = argparse.ArgumentParser() + parser.add_argument("--fingerprint") sub = parser.add_subparsers(dest="command") exp = sub.add_parser("export") @@ -948,7 +957,7 @@ def main(argv: list[str] | None = None) -> int: args = parser.parse_args(argv) try: - password_manager = PasswordManager() + password_manager = PasswordManager(fingerprint=args.fingerprint or fingerprint) logger.info("PasswordManager initialized successfully.") except (PasswordPromptError, Bip85Error) as e: logger.error(f"Failed to initialize PasswordManager: {e}", exc_info=True) diff --git a/src/seedpass/cli.py b/src/seedpass/cli.py index cc98ace..3dffb39 100644 --- a/src/seedpass/cli.py +++ b/src/seedpass/cli.py @@ -61,7 +61,7 @@ def main(ctx: typer.Context, fingerprint: Optional[str] = fingerprint_option) -> ctx.obj = {"fingerprint": fingerprint} if ctx.invoked_subcommand is None: tui = importlib.import_module("main") - raise typer.Exit(tui.main()) + raise typer.Exit(tui.main(fingerprint=fingerprint)) @entry_app.command("list") diff --git a/src/tests/test_cli_export_import.py b/src/tests/test_cli_export_import.py index 5d4afef..5e268b7 100644 --- a/src/tests/test_cli_export_import.py +++ b/src/tests/test_cli_export_import.py @@ -45,7 +45,7 @@ def test_cli_export_creates_file(monkeypatch, tmp_path): } vault.save_index(data) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -83,7 +83,7 @@ def test_cli_import_round_trip(monkeypatch, tmp_path): vault.save_index({"schema_version": 4, "entries": {}}) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) diff --git a/src/tests/test_cli_subcommands.py b/src/tests/test_cli_subcommands.py index e62cf57..56e437f 100644 --- a/src/tests/test_cli_subcommands.py +++ b/src/tests/test_cli_subcommands.py @@ -28,7 +28,7 @@ def make_pm(search_results, entry=None, totp_code="123456"): def test_search_command(monkeypatch, capsys): pm = make_pm([(0, "Example", "user", "", False)]) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -41,7 +41,7 @@ def test_search_command(monkeypatch, capsys): def test_get_command(monkeypatch, capsys): entry = {"type": EntryType.PASSWORD.value, "length": 8} pm = make_pm([(0, "Example", "user", "", False)], entry=entry) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -55,7 +55,7 @@ def test_totp_command(monkeypatch, capsys): entry = {"type": EntryType.TOTP.value, "period": 30, "index": 0} pm = make_pm([(0, "Example", None, None, False)], entry=entry) called = {} - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -72,7 +72,7 @@ def test_totp_command(monkeypatch, capsys): def test_search_command_no_results(monkeypatch, capsys): pm = make_pm([]) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -85,7 +85,7 @@ def test_search_command_no_results(monkeypatch, capsys): def test_get_command_multiple_matches(monkeypatch, capsys): matches = [(0, "Example", "user", "", False), (1, "Ex2", "bob", "", False)] pm = make_pm(matches) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -98,7 +98,7 @@ def test_get_command_multiple_matches(monkeypatch, capsys): def test_get_command_wrong_type(monkeypatch, capsys): entry = {"type": EntryType.TOTP.value} pm = make_pm([(0, "Example", "user", "", False)], entry=entry) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -111,7 +111,7 @@ def test_get_command_wrong_type(monkeypatch, capsys): def test_totp_command_multiple_matches(monkeypatch, capsys): matches = [(0, "GH", None, None, False), (1, "Git", None, None, False)] pm = make_pm(matches) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -124,7 +124,7 @@ def test_totp_command_multiple_matches(monkeypatch, capsys): def test_totp_command_wrong_type(monkeypatch, capsys): entry = {"type": EntryType.PASSWORD.value, "length": 8} pm = make_pm([(0, "Example", "user", "", False)], entry=entry) - monkeypatch.setattr(main, "PasswordManager", lambda: pm) + monkeypatch.setattr(main, "PasswordManager", lambda *a, **k: pm) monkeypatch.setattr(main, "configure_logging", lambda: None) monkeypatch.setattr(main, "initialize_app", lambda: None) monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) @@ -132,3 +132,21 @@ def test_totp_command_wrong_type(monkeypatch, capsys): assert rc == 1 out = capsys.readouterr().out assert "Entry is not a TOTP entry" in out + + +def test_main_fingerprint_option(monkeypatch): + """Ensure the argparse CLI forwards the fingerprint to PasswordManager.""" + called = {} + + def fake_pm(fingerprint=None): + called["fp"] = fingerprint + return make_pm([]) + + monkeypatch.setattr(main, "PasswordManager", fake_pm) + monkeypatch.setattr(main, "configure_logging", lambda: None) + monkeypatch.setattr(main, "initialize_app", lambda: None) + monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None) + + rc = main.main(["--fingerprint", "abc", "search", "q"]) + assert rc == 0 + assert called.get("fp") == "abc" diff --git a/src/tests/test_typer_cli.py b/src/tests/test_typer_cli.py index 21feb6c..ea4ad87 100644 --- a/src/tests/test_typer_cli.py +++ b/src/tests/test_typer_cli.py @@ -307,6 +307,21 @@ def test_api_start_passes_fingerprint(monkeypatch): assert called.get("fp") == "abc" +def test_entry_list_passes_fingerprint(monkeypatch): + """Ensure entry commands receive the fingerprint.""" + called = {} + + class PM: + def __init__(self, fingerprint=None): + called["fp"] = fingerprint + self.entry_manager = SimpleNamespace(list_entries=lambda *a, **k: []) + + monkeypatch.setattr(cli, "PasswordManager", PM) + result = runner.invoke(app, ["--fingerprint", "abc", "entry", "list"]) + assert result.exit_code == 0 + assert called.get("fp") == "abc" + + def test_entry_add(monkeypatch): called = {} @@ -447,3 +462,21 @@ def test_update_checksum_command(monkeypatch): result = runner.invoke(app, ["util", "update-checksum"]) assert result.exit_code == 0 assert called.get("called") is True + + +def test_tui_forward_fingerprint(monkeypatch): + """Ensure --fingerprint is forwarded when launching the TUI.""" + called = {} + + def fake_main(*, fingerprint=None): + called["fp"] = fingerprint + return 0 + + fake_mod = SimpleNamespace(main=fake_main) + monkeypatch.setattr( + cli, "importlib", SimpleNamespace(import_module=lambda n: fake_mod) + ) + + result = runner.invoke(app, ["--fingerprint", "abc"]) + assert result.exit_code == 0 + assert called.get("fp") == "abc"