mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Add search option to menu
This commit is contained in:
20
src/main.py
20
src/main.py
@@ -615,10 +615,11 @@ def display_menu(
|
||||
Select an option:
|
||||
1. Add Entry
|
||||
2. Retrieve Entry
|
||||
3. Modify an Existing Entry
|
||||
4. 2FA Codes
|
||||
5. Settings
|
||||
6. Exit
|
||||
3. Search Entries
|
||||
4. Modify an Existing Entry
|
||||
5. 2FA Codes
|
||||
6. Settings
|
||||
7. Exit
|
||||
"""
|
||||
display_fn = getattr(password_manager, "display_stats", None)
|
||||
if callable(display_fn):
|
||||
@@ -643,7 +644,7 @@ def display_menu(
|
||||
print(colored(menu, "cyan"))
|
||||
try:
|
||||
choice = timed_input(
|
||||
"Enter your choice (1-6): ", inactivity_timeout
|
||||
"Enter your choice (1-7): ", inactivity_timeout
|
||||
).strip()
|
||||
except TimeoutError:
|
||||
print(colored("Session timed out. Vault locked.", "yellow"))
|
||||
@@ -654,7 +655,7 @@ def display_menu(
|
||||
if not choice:
|
||||
print(
|
||||
colored(
|
||||
"No input detected. Please enter a number between 1 and 6.",
|
||||
"No input detected. Please enter a number between 1 and 7.",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
@@ -682,14 +683,17 @@ def display_menu(
|
||||
password_manager.handle_retrieve_entry()
|
||||
elif choice == "3":
|
||||
password_manager.update_activity()
|
||||
password_manager.handle_modify_entry()
|
||||
password_manager.handle_search_entries()
|
||||
elif choice == "4":
|
||||
password_manager.update_activity()
|
||||
password_manager.handle_display_totp_codes()
|
||||
password_manager.handle_modify_entry()
|
||||
elif choice == "5":
|
||||
password_manager.update_activity()
|
||||
handle_settings(password_manager)
|
||||
elif choice == "6":
|
||||
password_manager.update_activity()
|
||||
password_manager.handle_display_totp_codes()
|
||||
elif choice == "7":
|
||||
logging.info("Exiting the program.")
|
||||
print(colored("Exiting the program.", "green"))
|
||||
password_manager.nostr_client.close_client_pool()
|
||||
|
@@ -1303,6 +1303,35 @@ class PasswordManager:
|
||||
logging.error(f"Error during modifying entry: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to modify entry: {e}", "red"))
|
||||
|
||||
def handle_search_entries(self) -> None:
|
||||
"""Prompt for a query and display matching entries."""
|
||||
try:
|
||||
query = input("Enter search string: ").strip()
|
||||
if not query:
|
||||
print(colored("No search string provided.", "yellow"))
|
||||
return
|
||||
|
||||
results = self.entry_manager.search_entries(query)
|
||||
if not results:
|
||||
print(colored("No matching entries found.", "yellow"))
|
||||
return
|
||||
|
||||
print(colored("\n[+] Search Results:\n", "green"))
|
||||
for entry in results:
|
||||
index, website, username, url, blacklisted = entry
|
||||
print(colored(f"Index: {index}", "cyan"))
|
||||
print(colored(f" Website: {website}", "cyan"))
|
||||
print(colored(f" Username: {username or 'N/A'}", "cyan"))
|
||||
print(colored(f" URL: {url or 'N/A'}", "cyan"))
|
||||
print(
|
||||
colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan")
|
||||
)
|
||||
print("-" * 40)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to search entries: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to search entries: {e}", "red"))
|
||||
|
||||
def delete_entry(self) -> None:
|
||||
"""Deletes an entry from the password index."""
|
||||
try:
|
||||
|
@@ -31,7 +31,7 @@ def test_auto_sync_triggers_post(monkeypatch):
|
||||
called = True
|
||||
|
||||
monkeypatch.setattr(main, "handle_post_to_nostr", fake_post)
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "6")
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "7")
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=0.1)
|
||||
|
@@ -52,7 +52,7 @@ def _make_pm(called, locked=None):
|
||||
def test_empty_and_non_numeric_choice(monkeypatch, capsys):
|
||||
called = {"add": False, "retrieve": False, "modify": False}
|
||||
pm, _ = _make_pm(called)
|
||||
inputs = iter(["", "abc", "6"])
|
||||
inputs = iter(["", "abc", "7"])
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
||||
@@ -65,7 +65,7 @@ def test_empty_and_non_numeric_choice(monkeypatch, capsys):
|
||||
def test_out_of_range_menu(monkeypatch, capsys):
|
||||
called = {"add": False, "retrieve": False, "modify": False}
|
||||
pm, _ = _make_pm(called)
|
||||
inputs = iter(["9", "6"])
|
||||
inputs = iter(["9", "7"])
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
||||
@@ -77,7 +77,7 @@ def test_out_of_range_menu(monkeypatch, capsys):
|
||||
def test_invalid_add_entry_submenu(monkeypatch, capsys):
|
||||
called = {"add": False, "retrieve": False, "modify": False}
|
||||
pm, _ = _make_pm(called)
|
||||
inputs = iter(["1", "4", "3", "6"])
|
||||
inputs = iter(["1", "4", "3", "7"])
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||
monkeypatch.setattr("builtins.input", lambda *_: next(inputs))
|
||||
with pytest.raises(SystemExit):
|
||||
@@ -92,7 +92,7 @@ def test_inactivity_timeout_loop(monkeypatch, capsys):
|
||||
pm, locked = _make_pm(called)
|
||||
pm.last_activity = 0
|
||||
monkeypatch.setattr(time, "time", lambda: 100.0)
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "6")
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "7")
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=0.1)
|
||||
out = capsys.readouterr().out
|
||||
|
@@ -36,7 +36,7 @@ def test_inactivity_triggers_lock(monkeypatch):
|
||||
unlock_vault=unlock_vault,
|
||||
)
|
||||
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "6")
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: "7")
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=0.1)
|
||||
@@ -72,7 +72,7 @@ def test_input_timeout_triggers_lock(monkeypatch):
|
||||
unlock_vault=unlock_vault,
|
||||
)
|
||||
|
||||
responses = iter([TimeoutError(), "6"])
|
||||
responses = iter([TimeoutError(), "7"])
|
||||
|
||||
def fake_input(*_args, **_kwargs):
|
||||
val = next(responses)
|
||||
|
38
src/tests/test_menu_search.py
Normal file
38
src/tests/test_menu_search.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
sys.path.append(str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import main
|
||||
|
||||
|
||||
def _make_pm(called):
|
||||
pm = SimpleNamespace(
|
||||
is_dirty=False,
|
||||
last_update=time.time(),
|
||||
last_activity=time.time(),
|
||||
nostr_client=SimpleNamespace(close_client_pool=lambda: None),
|
||||
handle_add_password=lambda: None,
|
||||
handle_add_totp=lambda: None,
|
||||
handle_retrieve_entry=lambda: None,
|
||||
handle_search_entries=lambda: called.append("search"),
|
||||
handle_modify_entry=lambda: None,
|
||||
update_activity=lambda: None,
|
||||
lock_vault=lambda: None,
|
||||
unlock_vault=lambda: None,
|
||||
)
|
||||
return pm
|
||||
|
||||
|
||||
def test_menu_search_option(monkeypatch):
|
||||
called = []
|
||||
pm = _make_pm(called)
|
||||
inputs = iter(["3", "7"])
|
||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||
monkeypatch.setattr("builtins.input", lambda *_: "query")
|
||||
with pytest.raises(SystemExit):
|
||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
||||
assert called == ["search"]
|
Reference in New Issue
Block a user