From aa688bc49a5c36def1eb85ef87952fac3dbef64e Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 3 Aug 2025 14:32:40 -0400 Subject: [PATCH] test: add legacy manifest fallback test --- src/nostr/client.py | 58 ++++++++++++++-------- src/tests/test_nostr_legacy_manifest_id.py | 49 ++++++++++++++++++ 2 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 src/tests/test_nostr_legacy_manifest_id.py diff --git a/src/nostr/client.py b/src/nostr/client.py index 76fdb00..a5c9d52 100644 --- a/src/nostr/client.py +++ b/src/nostr/client.py @@ -407,7 +407,9 @@ class NostrClient: } ) - manifest_identifier = f"{MANIFEST_ID_PREFIX}{self.fingerprint}" + manifest_identifier = ( + self.current_manifest_id or f"{MANIFEST_ID_PREFIX}{self.fingerprint}" + ) manifest_event = ( EventBuilder(Kind(KIND_MANIFEST), manifest_json) .tags([Tag.identifier(manifest_identifier)]) @@ -516,35 +518,47 @@ class NostrClient: self.last_error = None pubkey = self.keys.public_key() - ident = f"{MANIFEST_ID_PREFIX}{self.fingerprint}" - f = Filter().author(pubkey).kind(Kind(KIND_MANIFEST)).identifier(ident).limit(1) + identifiers = [ + f"{MANIFEST_ID_PREFIX}{self.fingerprint}", + MANIFEST_ID_PREFIX.rstrip("-"), + ] timeout = timedelta(seconds=10) - try: - events = (await self.client.fetch_events(f, timeout)).to_vec() - except Exception as e: # pragma: no cover - network errors - self.last_error = str(e) - logger.error( - "Failed to fetch manifest from relays %s: %s", - self.relays, - e, + for ident in identifiers: + f = ( + Filter() + .author(pubkey) + .kind(Kind(KIND_MANIFEST)) + .identifier(ident) + .limit(1) ) - return None - - if not events: - return None - - for manifest_event in events: try: - result = await self._fetch_chunks_with_retry(manifest_event) - if result is not None: - return result + events = (await self.client.fetch_events(f, timeout)).to_vec() except Exception as e: # pragma: no cover - network errors self.last_error = str(e) logger.error( - "Error retrieving snapshot from relays %s: %s", + "Failed to fetch manifest from relays %s: %s", self.relays, e, ) + return None + + if not events: + continue + + for manifest_event in events: + try: + result = await self._fetch_chunks_with_retry(manifest_event) + if result is not None: + return result + except Exception as e: # pragma: no cover - network errors + self.last_error = str(e) + logger.error( + "Error retrieving snapshot from relays %s: %s", + self.relays, + e, + ) + # manifest was found but chunks missing; do not try other identifiers + return None if self.last_error is None: self.last_error = "Snapshot not found on relays" @@ -557,7 +571,7 @@ class NostrClient: return await self._connect_async() pubkey = self.keys.public_key() - ident = f"{MANIFEST_ID_PREFIX}{self.fingerprint}" + ident = self.current_manifest_id or f"{MANIFEST_ID_PREFIX}{self.fingerprint}" f = Filter().author(pubkey).kind(Kind(KIND_MANIFEST)).identifier(ident).limit(1) timeout = timedelta(seconds=10) try: diff --git a/src/tests/test_nostr_legacy_manifest_id.py b/src/tests/test_nostr_legacy_manifest_id.py new file mode 100644 index 0000000..c839ed6 --- /dev/null +++ b/src/tests/test_nostr_legacy_manifest_id.py @@ -0,0 +1,49 @@ +import asyncio + +from helpers import TEST_SEED, dummy_nostr_client +from nostr.backup_models import KIND_MANIFEST +from nostr.client import MANIFEST_ID_PREFIX, NostrClient + + +def test_fetch_latest_snapshot_legacy_identifier(dummy_nostr_client, monkeypatch): + client, relay = dummy_nostr_client + data = b"legacy" + asyncio.run(client.publish_snapshot(data)) + relay.manifests[-1].tags = [MANIFEST_ID_PREFIX.rstrip("-")] + relay.filters.clear() + + orig_fetch = relay.fetch_events + + async def fetch_events(self, f, timeout): + identifier = f.ids[0] if getattr(f, "ids", None) else None + kind = getattr(f, "kind_val", None) + if kind == KIND_MANIFEST: + events = [m for m in self.manifests if identifier in m.tags] + self.filters.append(f) + + class Res: + def __init__(self, evs): + self._evs = evs + + def to_vec(self): + return self._evs + + return Res(events) + return await orig_fetch(f, timeout) + + monkeypatch.setattr( + relay, "fetch_events", fetch_events.__get__(relay, relay.__class__) + ) + + enc_mgr = client.encryption_manager + monkeypatch.setattr( + enc_mgr, "decrypt_parent_seed", lambda: TEST_SEED, raising=False + ) + monkeypatch.setattr("nostr.client.KeyManager", type(client.key_manager)) + client2 = NostrClient(enc_mgr, "fp") + relay.filters.clear() + result = asyncio.run(client2.fetch_latest_snapshot()) + assert result is not None + ids = [f.ids[0] for f in relay.filters] + assert ids[0] == f"{MANIFEST_ID_PREFIX}fp" + assert MANIFEST_ID_PREFIX.rstrip("-") in ids