From c6f4d185dabebb7e46656c95d64937bb288bdde5 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Wed, 2 Jul 2025 12:04:16 -0400 Subject: [PATCH] Add manual Nostr index size test --- pytest.ini | 1 + src/tests/conftest.py | 13 ++++++ src/tests/test_nostr_index_size.py | 63 ++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 src/tests/test_nostr_index_size.py diff --git a/pytest.ini b/pytest.ini index 1aa25c7..25e1f5c 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,5 +7,6 @@ testpaths = src/tests markers = network: tests that require network connectivity stress: long running stress tests + desktop: desktop only tests filterwarnings = ignore::DeprecationWarning:multiprocessing.popen_fork diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 6daa678..80dbacd 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -14,10 +14,17 @@ def pytest_addoption(parser: pytest.Parser) -> None: default=False, help="run stress tests", ) + parser.addoption( + "--desktop", + action="store_true", + default=False, + help="run desktop-only tests", + ) def pytest_configure(config: pytest.Config) -> None: config.addinivalue_line("markers", "stress: long running stress tests") + config.addinivalue_line("markers", "desktop: desktop only tests") def pytest_collection_modifyitems( @@ -30,3 +37,9 @@ def pytest_collection_modifyitems( for item in items: if "stress" in item.keywords: item.add_marker(skip_stress) + + if not config.getoption("--desktop"): + skip_desktop = pytest.mark.skip(reason="need --desktop option to run") + for item in items: + if "desktop" in item.keywords: + item.add_marker(skip_desktop) diff --git a/src/tests/test_nostr_index_size.py b/src/tests/test_nostr_index_size.py new file mode 100644 index 0000000..02279fd --- /dev/null +++ b/src/tests/test_nostr_index_size.py @@ -0,0 +1,63 @@ +import time +from pathlib import Path +from tempfile import TemporaryDirectory +from unittest.mock import patch + +import pytest +from cryptography.fernet import Fernet + +from password_manager.encryption import EncryptionManager +from password_manager.entry_management import EntryManager +from password_manager.vault import Vault +from nostr.client import NostrClient, Kind, KindStandard + + +@pytest.mark.desktop +@pytest.mark.network +def test_nostr_index_size_limits(): + """Manually explore maximum index size for Nostr backups.""" + seed = ( + "abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon about" + ) + results = [] + with TemporaryDirectory() as tmpdir: + key = Fernet.generate_key() + enc_mgr = EncryptionManager(key, Path(tmpdir)) + with patch.object(enc_mgr, "decrypt_parent_seed", return_value=seed): + client = NostrClient( + enc_mgr, + "size_test_fp", + relays=["wss://relay.snort.social"], + ) + vault = Vault(enc_mgr, tmpdir) + entry_mgr = EntryManager(vault, Path(tmpdir)) + + sizes = [16, 64, 256, 1024, 2048, 4096, 8192] + for size in sizes: + try: + entry_mgr.add_entry( + website_name=f"site-{size}", + length=12, + username="u" * size, + url="https://example.com/" + "a" * size, + ) + encrypted = vault.get_encrypted_index() + payload_size = len(encrypted) if encrypted else 0 + published = client.publish_json_to_nostr(encrypted or b"") + time.sleep(2) + retrieved = client.retrieve_json_from_nostr_sync() + retrieved_ok = retrieved == encrypted + results.append((size, payload_size, published, retrieved_ok)) + if not published or not retrieved_ok: + break + except Exception: + results.append((size, None, False, False)) + break + client.close_client_pool() + + note_kind = Kind.from_std(KindStandard.TEXT_NOTE).to_int() + print(f"\nNostr note Kind: {note_kind}") + print("Size | Payload Bytes | Published | Retrieved") + for size, payload, pub, ret in results: + print(f"{size:>4} | {payload:>13} | {pub} | {ret}")