mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +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:
|
Select an option:
|
||||||
1. Add Entry
|
1. Add Entry
|
||||||
2. Retrieve Entry
|
2. Retrieve Entry
|
||||||
3. Modify an Existing Entry
|
3. Search Entries
|
||||||
4. 2FA Codes
|
4. Modify an Existing Entry
|
||||||
5. Settings
|
5. 2FA Codes
|
||||||
6. Exit
|
6. Settings
|
||||||
|
7. Exit
|
||||||
"""
|
"""
|
||||||
display_fn = getattr(password_manager, "display_stats", None)
|
display_fn = getattr(password_manager, "display_stats", None)
|
||||||
if callable(display_fn):
|
if callable(display_fn):
|
||||||
@@ -643,7 +644,7 @@ def display_menu(
|
|||||||
print(colored(menu, "cyan"))
|
print(colored(menu, "cyan"))
|
||||||
try:
|
try:
|
||||||
choice = timed_input(
|
choice = timed_input(
|
||||||
"Enter your choice (1-6): ", inactivity_timeout
|
"Enter your choice (1-7): ", inactivity_timeout
|
||||||
).strip()
|
).strip()
|
||||||
except TimeoutError:
|
except TimeoutError:
|
||||||
print(colored("Session timed out. Vault locked.", "yellow"))
|
print(colored("Session timed out. Vault locked.", "yellow"))
|
||||||
@@ -654,7 +655,7 @@ def display_menu(
|
|||||||
if not choice:
|
if not choice:
|
||||||
print(
|
print(
|
||||||
colored(
|
colored(
|
||||||
"No input detected. Please enter a number between 1 and 6.",
|
"No input detected. Please enter a number between 1 and 7.",
|
||||||
"yellow",
|
"yellow",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -682,14 +683,17 @@ def display_menu(
|
|||||||
password_manager.handle_retrieve_entry()
|
password_manager.handle_retrieve_entry()
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
password_manager.handle_modify_entry()
|
password_manager.handle_search_entries()
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
password_manager.handle_display_totp_codes()
|
password_manager.handle_modify_entry()
|
||||||
elif choice == "5":
|
elif choice == "5":
|
||||||
password_manager.update_activity()
|
password_manager.update_activity()
|
||||||
handle_settings(password_manager)
|
handle_settings(password_manager)
|
||||||
elif choice == "6":
|
elif choice == "6":
|
||||||
|
password_manager.update_activity()
|
||||||
|
password_manager.handle_display_totp_codes()
|
||||||
|
elif choice == "7":
|
||||||
logging.info("Exiting the program.")
|
logging.info("Exiting the program.")
|
||||||
print(colored("Exiting the program.", "green"))
|
print(colored("Exiting the program.", "green"))
|
||||||
password_manager.nostr_client.close_client_pool()
|
password_manager.nostr_client.close_client_pool()
|
||||||
|
@@ -1303,6 +1303,35 @@ class PasswordManager:
|
|||||||
logging.error(f"Error during modifying entry: {e}", exc_info=True)
|
logging.error(f"Error during modifying entry: {e}", exc_info=True)
|
||||||
print(colored(f"Error: Failed to modify entry: {e}", "red"))
|
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:
|
def delete_entry(self) -> None:
|
||||||
"""Deletes an entry from the password index."""
|
"""Deletes an entry from the password index."""
|
||||||
try:
|
try:
|
||||||
|
@@ -31,7 +31,7 @@ def test_auto_sync_triggers_post(monkeypatch):
|
|||||||
called = True
|
called = True
|
||||||
|
|
||||||
monkeypatch.setattr(main, "handle_post_to_nostr", fake_post)
|
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):
|
with pytest.raises(SystemExit):
|
||||||
main.display_menu(pm, sync_interval=0.1)
|
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):
|
def test_empty_and_non_numeric_choice(monkeypatch, capsys):
|
||||||
called = {"add": False, "retrieve": False, "modify": False}
|
called = {"add": False, "retrieve": False, "modify": False}
|
||||||
pm, _ = _make_pm(called)
|
pm, _ = _make_pm(called)
|
||||||
inputs = iter(["", "abc", "6"])
|
inputs = iter(["", "abc", "7"])
|
||||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
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):
|
def test_out_of_range_menu(monkeypatch, capsys):
|
||||||
called = {"add": False, "retrieve": False, "modify": False}
|
called = {"add": False, "retrieve": False, "modify": False}
|
||||||
pm, _ = _make_pm(called)
|
pm, _ = _make_pm(called)
|
||||||
inputs = iter(["9", "6"])
|
inputs = iter(["9", "7"])
|
||||||
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
monkeypatch.setattr(main, "timed_input", lambda *_: next(inputs))
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000)
|
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):
|
def test_invalid_add_entry_submenu(monkeypatch, capsys):
|
||||||
called = {"add": False, "retrieve": False, "modify": False}
|
called = {"add": False, "retrieve": False, "modify": False}
|
||||||
pm, _ = _make_pm(called)
|
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(main, "timed_input", lambda *_: next(inputs))
|
||||||
monkeypatch.setattr("builtins.input", lambda *_: next(inputs))
|
monkeypatch.setattr("builtins.input", lambda *_: next(inputs))
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
@@ -92,7 +92,7 @@ def test_inactivity_timeout_loop(monkeypatch, capsys):
|
|||||||
pm, locked = _make_pm(called)
|
pm, locked = _make_pm(called)
|
||||||
pm.last_activity = 0
|
pm.last_activity = 0
|
||||||
monkeypatch.setattr(time, "time", lambda: 100.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):
|
with pytest.raises(SystemExit):
|
||||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=0.1)
|
main.display_menu(pm, sync_interval=1000, inactivity_timeout=0.1)
|
||||||
out = capsys.readouterr().out
|
out = capsys.readouterr().out
|
||||||
|
@@ -36,7 +36,7 @@ def test_inactivity_triggers_lock(monkeypatch):
|
|||||||
unlock_vault=unlock_vault,
|
unlock_vault=unlock_vault,
|
||||||
)
|
)
|
||||||
|
|
||||||
monkeypatch.setattr(main, "timed_input", lambda *_: "6")
|
monkeypatch.setattr(main, "timed_input", lambda *_: "7")
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
main.display_menu(pm, sync_interval=1000, inactivity_timeout=0.1)
|
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,
|
unlock_vault=unlock_vault,
|
||||||
)
|
)
|
||||||
|
|
||||||
responses = iter([TimeoutError(), "6"])
|
responses = iter([TimeoutError(), "7"])
|
||||||
|
|
||||||
def fake_input(*_args, **_kwargs):
|
def fake_input(*_args, **_kwargs):
|
||||||
val = next(responses)
|
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