Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nip-62-integration-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"nostream": patch
---

Add NIP-62 integration tests for Request to Vanish
15 changes: 15 additions & 0 deletions test/integration/features/nip-62/nip-62.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: NIP-62
Scenario: Alice requests to vanish
Given someone called Alice
And someone called Bob
And Alice sends a set_metadata event
And Alice sends a text_note event with content "please forget this"
When Alice sends a request_to_vanish event
And Bob subscribes to author Alice
Then Bob receives 1 request_to_vanish event from Alice and EOSE

Scenario: Alice cannot publish after requesting to vanish
Given someone called Alice
When Alice sends a request_to_vanish event
And Alice drafts a text_note event with content "I should be blocked"
Then Alice sends their last draft event unsuccessfully because "blocked: request to vanish active for pubkey"
57 changes: 57 additions & 0 deletions test/integration/features/nip-62/nip-62.feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Then, When } from '@cucumber/cucumber'
import { expect } from 'chai'
import WebSocket from 'ws'

import { createEvent, sendEvent, waitForEventCount } from '../helpers'
import { ALL_RELAYS, EventKinds, EventTags } from '../../../../src/constants/base'
import { Event } from '../../../../src/@types/event'
import { isDraft } from '../shared'

When(/^(\w+) sends a request_to_vanish event$/, async function (name: string) {
const ws = this.parameters.clients[name] as WebSocket
const { pubkey, privkey } = this.parameters.identities[name]

const event: Event = await createEvent(
{ pubkey, kind: EventKinds.REQUEST_TO_VANISH, content: '', tags: [[EventTags.Relay, ALL_RELAYS]] },
privkey,
)

await sendEvent(ws, event)
this.parameters.events[name].push(event)
})

Then(
/(\w+) receives (\d+) request_to_vanish events? from (\w+) and EOSE$/,
async function (name: string, count: string, author: string) {
const ws = this.parameters.clients[name] as WebSocket
const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1]
const expectedCount = Number(count)
const expectedPubkey = this.parameters.identities[author].pubkey
const events = await waitForEventCount(ws, subscription.name, expectedCount, true)

expect(events.length).to.equal(expectedCount)
for (const event of events) {
expect(event.kind).to.equal(EventKinds.REQUEST_TO_VANISH)
expect(event.pubkey).to.equal(expectedPubkey)
}
},
)

Then(
/^(\w+) sends their last draft event unsuccessfully because "([^"]+)"$/,
async function (name: string, reason: string) {
const ws = this.parameters.clients[name] as WebSocket
const event = this.parameters.events[name].findLast((event: Event) => event[isDraft])

Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findLast(...) can return undefined if no draft event exists; delete event[isDraft] would then throw and fail the scenario with a non-actionable error. Add an explicit assertion/guard that a draft event was found before mutating/sending it.

Suggested change
if (!event) {
throw new Error(`No draft event found for ${name}`)
}

Copilot uses AI. Check for mistakes.
if (!event) {
throw new Error(`No draft event found for ${name}`)
}

delete event[isDraft]

const command = await sendEvent(ws, event, false)
expect(command[1]).to.equal(event.id)
expect(command[2]).to.be.false
expect(command[3]).to.equal(reason)
},
)
Loading