mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-06 12:52:04 +00:00
Advance TUI v2 parity with reveal/QR and landing theme
This commit is contained in:
1 parent
f02b360062
commit
92f17355ad
7 files changed
+800
-20
No files matched your search
@@ -286,6 +286,62 @@ class TestEntryService:
|
||||
mock_manager.entry_manager.get_totp_code.assert_called_once_with(1, b"key")
|
||||
assert code == "123456"
|
||||
|
||||
def test_get_seed_phrase(self, service, mock_manager):
|
||||
mock_manager.entry_manager.get_seed_phrase.return_value = "seed words"
|
||||
phrase = service.get_seed_phrase(7)
|
||||
mock_manager.entry_manager.get_seed_phrase.assert_called_once_with(
|
||||
7, mock_manager.parent_seed
|
||||
)
|
||||
assert phrase == "seed words"
|
||||
|
||||
def test_get_managed_account_seed(self, service, mock_manager):
|
||||
mock_manager.entry_manager.get_managed_account_seed.return_value = (
|
||||
"managed words"
|
||||
)
|
||||
phrase = service.get_managed_account_seed(8)
|
||||
mock_manager.entry_manager.get_managed_account_seed.assert_called_once_with(
|
||||
8, mock_manager.parent_seed
|
||||
)
|
||||
assert phrase == "managed words"
|
||||
|
||||
def test_get_ssh_key_pair(self, service, mock_manager):
|
||||
mock_manager.entry_manager.get_ssh_key_pair.return_value = ("priv", "pub")
|
||||
result = service.get_ssh_key_pair(3)
|
||||
mock_manager.entry_manager.get_ssh_key_pair.assert_called_once_with(
|
||||
3, mock_manager.parent_seed
|
||||
)
|
||||
assert result == ("priv", "pub")
|
||||
|
||||
def test_get_pgp_key(self, service, mock_manager):
|
||||
mock_manager.entry_manager.get_pgp_key.return_value = ("priv", "fp")
|
||||
result = service.get_pgp_key(4)
|
||||
mock_manager.entry_manager.get_pgp_key.assert_called_once_with(
|
||||
4, mock_manager.parent_seed
|
||||
)
|
||||
assert result == ("priv", "fp")
|
||||
|
||||
def test_get_nostr_key_pair(self, service, mock_manager):
|
||||
mock_manager.entry_manager.get_nostr_key_pair.return_value = ("npub", "nsec")
|
||||
result = service.get_nostr_key_pair(5)
|
||||
mock_manager.entry_manager.get_nostr_key_pair.assert_called_once_with(
|
||||
5, mock_manager.parent_seed
|
||||
)
|
||||
assert result == ("npub", "nsec")
|
||||
|
||||
def test_get_secret_mode_enabled(self, service, mock_manager):
|
||||
mock_manager.config_manager.get_secret_mode_enabled.return_value = True
|
||||
assert service.get_secret_mode_enabled() is True
|
||||
|
||||
def test_get_clipboard_clear_delay(self, service, mock_manager):
|
||||
mock_manager.config_manager.get_clipboard_clear_delay.return_value = 40
|
||||
assert service.get_clipboard_clear_delay() == 40
|
||||
|
||||
def test_copy_to_clipboard(self, service, mock_manager):
|
||||
mock_manager.config_manager.get_clipboard_clear_delay.return_value = 35
|
||||
with patch("seedpass.core.api.copy_to_clipboard", return_value=True) as patched:
|
||||
assert service.copy_to_clipboard("secret-value") is True
|
||||
patched.assert_called_once_with("secret-value", 35)
|
||||
|
||||
def test_add_entry(self, service, mock_manager):
|
||||
mock_manager.entry_manager.add_entry.return_value = 1
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ class MatrixEntryService:
|
||||
self.fail_modify = fail_modify
|
||||
self.fail_add_link = fail_add_link
|
||||
self.fail_remove_link = fail_remove_link
|
||||
self.secret_mode_enabled = False
|
||||
self.clipboard_delay = 30
|
||||
self.clipboard_values: list[str] = []
|
||||
|
||||
def search_entries(self, query: str, kinds: list[str] | None = None):
|
||||
if self.fail_search:
|
||||
@@ -74,6 +77,61 @@ class MatrixEntryService:
|
||||
if value is not None:
|
||||
entry[key] = value
|
||||
|
||||
def generate_password(self, length: int, entry_id: int) -> str:
|
||||
return f"pw-{entry_id}-{length}"
|
||||
|
||||
def get_seed_phrase(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(entry.get("seed_phrase", "abandon " * 11 + "about")).strip()
|
||||
|
||||
def get_managed_account_seed(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(
|
||||
entry.get(
|
||||
"seed_phrase",
|
||||
"legal winner thank year wave sausage worth useful legal winner thank yellow",
|
||||
)
|
||||
).strip()
|
||||
|
||||
def get_totp_secret(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(entry.get("secret", "JBSWY3DPEHPK3PXP"))
|
||||
|
||||
def get_totp_code(self, entry_id: int) -> str:
|
||||
_ = entry_id
|
||||
return "123456"
|
||||
|
||||
def get_ssh_key_pair(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("private_key", "SSH_PRIVATE")),
|
||||
str(entry.get("public_key", "ssh-ed25519 AAAA...")),
|
||||
)
|
||||
|
||||
def get_pgp_key(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("private_key", "-----BEGIN PGP PRIVATE KEY BLOCK-----")),
|
||||
str(entry.get("fingerprint", "DEADBEEF")),
|
||||
)
|
||||
|
||||
def get_nostr_key_pair(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("npub", "npub1example")),
|
||||
str(entry.get("nsec", "nsec1example")),
|
||||
)
|
||||
|
||||
def get_secret_mode_enabled(self) -> bool:
|
||||
return self.secret_mode_enabled
|
||||
|
||||
def get_clipboard_clear_delay(self) -> int:
|
||||
return self.clipboard_delay
|
||||
|
||||
def copy_to_clipboard(self, value: str) -> bool:
|
||||
self.clipboard_values.append(value)
|
||||
return True
|
||||
|
||||
def archive_entry(self, entry_id: int) -> None:
|
||||
if self.fail_archive:
|
||||
raise RuntimeError("archive failed")
|
||||
@@ -245,6 +303,14 @@ async def test_tui2_matrix_actions_palette_events_and_guards() -> None:
|
||||
app._run_palette_command("jump 2")
|
||||
await pilot.pause()
|
||||
|
||||
app._run_palette_command("open 2")
|
||||
app.action_reveal_selected()
|
||||
await pilot.pause()
|
||||
assert "pw-2-" in str(app.query_one("#secret-detail").render())
|
||||
app.action_show_qr()
|
||||
await pilot.pause()
|
||||
assert "QR not supported" in str(app.query_one("#secret-detail").render())
|
||||
|
||||
app.action_focus_left()
|
||||
app.action_focus_center()
|
||||
app.action_focus_right()
|
||||
|
||||
@@ -5,6 +5,7 @@ import pytest
|
||||
from seedpass.tui_v2.app import (
|
||||
pagination_window,
|
||||
parse_palette_command,
|
||||
render_qr_ascii,
|
||||
truncate_entry_for_display,
|
||||
)
|
||||
|
||||
@@ -61,6 +62,13 @@ def test_truncate_entry_for_display_truncates_content() -> None:
|
||||
assert payload["content_truncated"] is True
|
||||
|
||||
|
||||
def test_render_qr_ascii_nonempty() -> None:
|
||||
rendered = render_qr_ascii("otpauth://totp/example?secret=JBSWY3DPEHPK3PXP")
|
||||
lines = [line for line in rendered.splitlines() if line.strip()]
|
||||
assert len(lines) > 4
|
||||
assert any("##" in line for line in lines)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("rows", [1000, 10000, 50000])
|
||||
def test_pagination_window_large_vault_sizes(rows: int) -> None:
|
||||
# Large-vault smoke: pagination math remains stable at high row counts.
|
||||
|
||||
@@ -18,6 +18,9 @@ class FakeEntryService:
|
||||
for entry in entries
|
||||
}
|
||||
self.fail_search_times = fail_search_times
|
||||
self.secret_mode_enabled = False
|
||||
self.clipboard_delay = 30
|
||||
self.clipboard_values: list[str] = []
|
||||
|
||||
def search_entries(self, query: str, kinds: list[str] | None = None):
|
||||
if self.fail_search_times > 0:
|
||||
@@ -56,6 +59,61 @@ class FakeEntryService:
|
||||
continue
|
||||
entry[key] = value
|
||||
|
||||
def generate_password(self, length: int, entry_id: int) -> str:
|
||||
return f"pw-{entry_id}-{length}"
|
||||
|
||||
def get_seed_phrase(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(entry.get("seed_phrase", "abandon " * 11 + "about")).strip()
|
||||
|
||||
def get_managed_account_seed(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(
|
||||
entry.get(
|
||||
"seed_phrase",
|
||||
"legal winner thank year wave sausage worth useful legal winner thank yellow",
|
||||
)
|
||||
).strip()
|
||||
|
||||
def get_totp_secret(self, entry_id: int) -> str:
|
||||
entry = self._entries[int(entry_id)]
|
||||
return str(entry.get("secret", "JBSWY3DPEHPK3PXP"))
|
||||
|
||||
def get_totp_code(self, entry_id: int) -> str:
|
||||
_ = entry_id
|
||||
return "123456"
|
||||
|
||||
def get_ssh_key_pair(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("private_key", "SSH_PRIVATE")),
|
||||
str(entry.get("public_key", "ssh-ed25519 AAAA...")),
|
||||
)
|
||||
|
||||
def get_pgp_key(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("private_key", "-----BEGIN PGP PRIVATE KEY BLOCK-----")),
|
||||
str(entry.get("fingerprint", "DEADBEEF")),
|
||||
)
|
||||
|
||||
def get_nostr_key_pair(self, entry_id: int):
|
||||
entry = self._entries[int(entry_id)]
|
||||
return (
|
||||
str(entry.get("npub", "npub1example")),
|
||||
str(entry.get("nsec", "nsec1example")),
|
||||
)
|
||||
|
||||
def get_secret_mode_enabled(self) -> bool:
|
||||
return self.secret_mode_enabled
|
||||
|
||||
def get_clipboard_clear_delay(self) -> int:
|
||||
return self.clipboard_delay
|
||||
|
||||
def copy_to_clipboard(self, value: str) -> bool:
|
||||
self.clipboard_values.append(value)
|
||||
return True
|
||||
|
||||
def archive_entry(self, entry_id: int) -> None:
|
||||
self._entries[int(entry_id)]["archived"] = True
|
||||
|
||||
@@ -225,6 +283,101 @@ async def test_tui2_textual_link_commands_and_neighbor_open() -> None:
|
||||
assert "No graph links" in links_text
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_reveal_and_qr_flow() -> None:
|
||||
service = FakeEntryService(
|
||||
[
|
||||
{"id": 1, "kind": "password", "label": "Login 1", "length": 16},
|
||||
{
|
||||
"id": 2,
|
||||
"kind": "seed",
|
||||
"label": "Seed 2",
|
||||
"seed_phrase": "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"kind": "totp",
|
||||
"label": "TOTP 3",
|
||||
"secret": "JBSWY3DPEHPK3PXP",
|
||||
"period": 30,
|
||||
"digits": 6,
|
||||
},
|
||||
]
|
||||
)
|
||||
app = _build_app(service)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause()
|
||||
|
||||
app._run_palette_command("open 1")
|
||||
app.action_reveal_selected()
|
||||
await pilot.pause()
|
||||
assert "Password : pw-1-16" in _widget_text(app, "#secret-detail")
|
||||
|
||||
app._run_palette_command("open 2")
|
||||
app.action_show_qr()
|
||||
await pilot.pause()
|
||||
qr_text = _widget_text(app, "#secret-detail")
|
||||
assert "Seed #2 QR" in qr_text
|
||||
assert "Payload:" in qr_text
|
||||
|
||||
app._run_palette_command("open 3")
|
||||
app._run_palette_command("reveal")
|
||||
await pilot.pause()
|
||||
assert "Secret : JBSWY3DPEHPK3PXP" in _widget_text(app, "#secret-detail")
|
||||
|
||||
app._run_palette_command("qr")
|
||||
await pilot.pause()
|
||||
assert "TOTP #3 QR" in _widget_text(app, "#secret-detail")
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_sensitive_confirm_and_secret_mode_clipboard() -> None:
|
||||
service = FakeEntryService(
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"kind": "ssh",
|
||||
"label": "SSH 1",
|
||||
"private_key": "SSH_PRIVATE_1",
|
||||
"public_key": "ssh-ed25519 AAAA-1",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"kind": "nostr",
|
||||
"label": "Nostr 2",
|
||||
"npub": "npub1abc",
|
||||
"nsec": "nsec1abc",
|
||||
},
|
||||
]
|
||||
)
|
||||
service.secret_mode_enabled = True
|
||||
service.clipboard_delay = 45
|
||||
app = _build_app(service)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause()
|
||||
|
||||
app._run_palette_command("open 1")
|
||||
app._run_palette_command("reveal")
|
||||
await pilot.pause()
|
||||
assert "requires confirmation" in _status_text(app)
|
||||
|
||||
app._run_palette_command("reveal confirm")
|
||||
await pilot.pause()
|
||||
assert "copied to clipboard" in _widget_text(app, "#secret-detail")
|
||||
assert service.clipboard_values[-1] == "SSH_PRIVATE_1"
|
||||
|
||||
app._run_palette_command("open 2")
|
||||
app._run_palette_command("qr private")
|
||||
await pilot.pause()
|
||||
assert "requires confirmation" in _status_text(app)
|
||||
|
||||
app._run_palette_command("qr private confirm")
|
||||
await pilot.pause()
|
||||
assert "Nostr #2 QR" in _widget_text(app, "#secret-detail")
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_retry_after_search_failure() -> None:
|
||||
service = FakeEntryService(
|
||||
|
||||
Reference in new issue
Block a user