mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
test(relay): cover persona tombstone coordinate deletion e2e
The kind:5 a-tag tombstone is the only state-destroying op in the persona event flow, and GATE 2 (relay honors a-tag coordinate deletes for kind:30175) was previously closed by code-tracing rather than an executing test. Adds one e2e case to the existing relay harness: publish a persona, confirm it is live, publish an a-tag-only tombstone at its coordinate, then assert the query returns it gone. No new abstraction or mock seam. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
co-authored by
Will Pfleger
parent
1747d2ec56
commit
7c48388d35
@@ -4,6 +4,7 @@
|
||||
//! - Accepts valid persona events with proper d-tag slugs
|
||||
//! - Enforces NIP-33 replacement semantics (same d-tag, newer timestamp wins)
|
||||
//! - Rejects invalid d-tag values (empty, too long, invalid characters)
|
||||
//! - Honors NIP-09 a-tag tombstones, removing the persona coordinate
|
||||
//!
|
||||
//! # Running
|
||||
//!
|
||||
@@ -45,6 +46,20 @@ fn persona_event_at(keys: &Keys, d_tag: &str, content: &str, created_at: u64) ->
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Build a NIP-09 deletion (kind:5) targeting a persona's NIP-33 coordinate.
|
||||
///
|
||||
/// Mirrors the desktop `build_persona_delete` shape: a single `a`-tag carrying
|
||||
/// `30175:<owner>:<d_tag>` and no `e`-tag. An `e`-tag would route the relay to
|
||||
/// the event-id deletion path, leaving the parameterized-replaceable coordinate
|
||||
/// live; the coordinate delete removes the persona for every client.
|
||||
fn persona_delete_event(keys: &Keys, d_tag: &str) -> nostr::Event {
|
||||
let coord = format!("{PERSONA_KIND}:{}:{d_tag}", keys.public_key().to_hex());
|
||||
EventBuilder::new(Kind::Custom(5), "")
|
||||
.tags(vec![Tag::parse(["a", coord.as_str()]).unwrap()])
|
||||
.sign_with_keys(keys)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// ── Publish and query back ───────────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
@@ -429,3 +444,71 @@ async fn test_persona_multiple_per_author() {
|
||||
|
||||
client.disconnect().await.expect("disconnect");
|
||||
}
|
||||
|
||||
// ── NIP-09 coordinate deletion (tombstone) ───────────────────────────────────
|
||||
|
||||
/// The kind:5 a-tag tombstone is the only state-destroying operation in the
|
||||
/// persona event flow: it removes the persona for every client and across
|
||||
/// reboots. This proves the relay acts on it — publishing the persona, then an
|
||||
/// a-tag-only tombstone at its coordinate, makes a subsequent query return it
|
||||
/// gone (NIP-33 coordinate soft-delete, not the event-id path an e-tag triggers).
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_persona_tombstone_deletes_coordinate() {
|
||||
let url = relay_url();
|
||||
let keys = Keys::generate();
|
||||
let d_tag = format!("tombstone-{}", &uuid::Uuid::new_v4().to_string()[..8]);
|
||||
|
||||
let content = r#"{"name":"doomed","display_name":"Doomed","description":"To be deleted"}"#;
|
||||
|
||||
let mut client = BuzzTestClient::connect(&url, &keys).await.expect("connect");
|
||||
|
||||
// Publish the persona.
|
||||
let event = persona_event(&keys, &d_tag, content);
|
||||
let ok = client.send_event(event).await.expect("send persona");
|
||||
assert!(ok.accepted, "relay rejected persona event: {}", ok.message);
|
||||
|
||||
let filter = || {
|
||||
Filter::new()
|
||||
.kind(Kind::Custom(PERSONA_KIND))
|
||||
.author(keys.public_key())
|
||||
.custom_tags(SingleLetterTag::lowercase(Alphabet::D), [d_tag.as_str()])
|
||||
};
|
||||
|
||||
// Confirm it is live before deletion — distinguishes "deleted" from
|
||||
// "never published" if the post-tombstone query comes back empty.
|
||||
let sid = sub_id("tombstone-pre");
|
||||
client
|
||||
.subscribe(&sid, vec![filter()])
|
||||
.await
|
||||
.expect("subscribe pre");
|
||||
let before = client
|
||||
.collect_until_eose(&sid, Duration::from_secs(5))
|
||||
.await
|
||||
.expect("collect pre");
|
||||
assert_eq!(before.len(), 1, "persona should be live before deletion");
|
||||
|
||||
// Publish the a-tag-only tombstone at the persona's coordinate.
|
||||
let tombstone = persona_delete_event(&keys, &d_tag);
|
||||
let ok = client.send_event(tombstone).await.expect("send tombstone");
|
||||
assert!(ok.accepted, "relay rejected tombstone: {}", ok.message);
|
||||
|
||||
// Query the coordinate again — it must be gone.
|
||||
let sid = sub_id("tombstone-post");
|
||||
client
|
||||
.subscribe(&sid, vec![filter()])
|
||||
.await
|
||||
.expect("subscribe post");
|
||||
let after = client
|
||||
.collect_until_eose(&sid, Duration::from_secs(5))
|
||||
.await
|
||||
.expect("collect post");
|
||||
assert_eq!(
|
||||
after.len(),
|
||||
0,
|
||||
"tombstone should remove the persona coordinate, got {} event(s)",
|
||||
after.len()
|
||||
);
|
||||
|
||||
client.disconnect().await.expect("disconnect");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user