Skip to content

Commit 92caa3f

Browse files
committed
test(integration): add NIP-62 vanish tests and fix test enviroment
1 parent b3effd1 commit 92caa3f

4 files changed

Lines changed: 105 additions & 6 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"nostream": patch
3+
---
4+
5+
Add NIP-62 integration tests for Request to Vanish
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Feature: NIP-62
2+
Scenario: Alice requests to vanish
3+
Given someone called Alice
4+
And someone called Bob
5+
And Alice sends a set_metadata event
6+
And Alice sends a text_note event with content "please forget this"
7+
When Alice sends a request_to_vanish event
8+
And Bob subscribes to author Alice
9+
Then Bob receives 1 request_to_vanish event from Alice and EOSE
10+
11+
Scenario: Alice cannot publish after requesting to vanish
12+
Given someone called Alice
13+
When Alice sends a request_to_vanish event
14+
And Alice drafts a text_note event with content "I should be blocked"
15+
Then Alice sends their last draft event unsuccessfully because "blocked: request to vanish active for pubkey"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Then, When } from '@cucumber/cucumber'
2+
import { expect } from 'chai'
3+
import WebSocket from 'ws'
4+
5+
import { createEvent, sendEvent, waitForEventCount } from '../helpers'
6+
import { ALL_RELAYS, EventKinds, EventTags } from '../../../../src/constants/base'
7+
import { Event } from '../../../../src/@types/event'
8+
import { isDraft } from '../shared'
9+
10+
When(/^(\w+) sends a request_to_vanish event$/, async function (name: string) {
11+
const ws = this.parameters.clients[name] as WebSocket
12+
const { pubkey, privkey } = this.parameters.identities[name]
13+
14+
const event: Event = await createEvent(
15+
{ pubkey, kind: EventKinds.REQUEST_TO_VANISH, content: '', tags: [[EventTags.Relay, ALL_RELAYS]] },
16+
privkey,
17+
)
18+
19+
await sendEvent(ws, event)
20+
this.parameters.events[name].push(event)
21+
})
22+
23+
Then(
24+
/(\w+) receives (\d+) request_to_vanish events? from (\w+) and EOSE$/,
25+
async function (name: string, count: string, author: string) {
26+
const ws = this.parameters.clients[name] as WebSocket
27+
const subscription = this.parameters.subscriptions[name][this.parameters.subscriptions[name].length - 1]
28+
const events = await waitForEventCount(ws, subscription.name, Number(count), true)
29+
30+
expect(events.length).to.equal(Number(count))
31+
expect(events[0].kind).to.equal(EventKinds.REQUEST_TO_VANISH)
32+
expect(events[0].pubkey).to.equal(this.parameters.identities[author].pubkey)
33+
},
34+
)
35+
36+
Then(
37+
/^(\w+) sends their last draft event unsuccessfully because "([^"]+)"$/,
38+
async function (name: string, reason: string) {
39+
const ws = this.parameters.clients[name] as WebSocket
40+
const event = this.parameters.events[name].findLast((event: Event) => event[isDraft])
41+
42+
delete event[isDraft]
43+
44+
const command = await sendEvent(ws, event, false)
45+
expect(command[1]).to.equal(event.id)
46+
expect(command[2]).to.be.false
47+
expect(command[3]).to.equal(reason)
48+
},
49+
)

test/integration/features/shared.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { After, AfterAll, Before, BeforeAll, Given, Then, When, World } from '@cucumber/cucumber'
1+
import { After, AfterAll, Before, BeforeAll, Given, Then, When, World, setDefaultTimeout } from '@cucumber/cucumber'
22
import { assocPath, pipe } from 'ramda'
33
import { fromEvent, map, Observable, ReplaySubject, Subject, takeUntil } from 'rxjs'
44
import WebSocket, { MessageEvent } from 'ws'
@@ -15,19 +15,32 @@ import { workerFactory } from '../../../src/factories/worker-factory'
1515
export const isDraft = Symbol('draft')
1616

1717
let worker: AppWorker
18-
1918
let dbClient: DatabaseClient
2019
let rrDbClient: DatabaseClient
2120

2221
export const streams = new WeakMap<WebSocket, Observable<unknown>>()
2322

24-
BeforeAll({ timeout: 1000 }, async function () {
23+
setDefaultTimeout(30000)
24+
25+
BeforeAll(async function () {
2526
process.env.RELAY_PORT = '18808'
2627
process.env.SECRET = Math.random().toString().repeat(6)
28+
29+
process.env.DB_HOST ??= 'localhost'
30+
process.env.DB_PORT ??= '5432'
31+
process.env.DB_USER ??= 'postgres'
32+
process.env.DB_PASSWORD ??= 'postgres'
33+
process.env.DB_NAME ??= 'nostr_ts_relay_test'
34+
process.env.DB_MIN_POOL_SIZE ??= '1'
35+
process.env.DB_MAX_POOL_SIZE ??= '2'
36+
process.env.DB_ACQUIRE_CONNECTION_TIMEOUT ??= '10000'
37+
2738
dbClient = getMasterDbClient()
2839
rrDbClient = getReadReplicaDbClient()
40+
2941
await dbClient.raw('SELECT 1=1')
3042
await rrDbClient.raw('SELECT 1=1')
43+
3144
Sinon.stub(SettingsStatic, 'watchSettings')
3245
const settings = SettingsStatic.createSettings()
3346

@@ -45,9 +58,16 @@ BeforeAll({ timeout: 1000 }, async function () {
4558
})
4659

4760
AfterAll({ timeout: 30000 }, async function () {
61+
const clients = [...new Set([dbClient, rrDbClient].filter(Boolean))]
62+
4863
await new Promise<void>((resolve) => {
64+
if (!worker) {
65+
void Promise.all(clients.map((client) => client.destroy())).then(() => resolve())
66+
return
67+
}
68+
4969
worker.close(async () => {
50-
await Promise.all([dbClient.destroy(), rrDbClient.destroy()])
70+
await Promise.all(clients.map((client) => client.destroy()))
5171
resolve()
5272
})
5373
})
@@ -63,11 +83,13 @@ Before(function () {
6383
After(async function () {
6484
this.parameters.events = {}
6585
this.parameters.subscriptions = {}
86+
6687
for (const ws of Object.values(this.parameters.clients as Record<string, WebSocket>)) {
6788
if (ws && ws.readyState === WebSocket.OPEN) {
6889
ws.close()
6990
}
7091
}
92+
7193
this.parameters.clients = {}
7294

7395
await dbClient('events')
@@ -78,20 +100,22 @@ After(async function () {
78100
),
79101
)
80102
.delete()
103+
81104
this.parameters.identities = {}
82105
})
83106

84107
Given(/someone called (\w+)/, async function (name: string) {
85108
const connection = await connect(name)
109+
86110
this.parameters.identities[name] = this.parameters.identities[name] ?? createIdentity(name)
87111
this.parameters.clients[name] = connection
88112
this.parameters.subscriptions[name] = []
89113
this.parameters.events[name] = []
114+
90115
const close = new Subject()
91116
connection.once('close', close.next.bind(close))
92117

93118
const projection = (raw: MessageEvent) => JSON.parse(raw.data.toString('utf8'))
94-
95119
const replaySubject = new ReplaySubject(2, 1000)
96120

97121
fromEvent(connection, 'message')
@@ -104,7 +128,12 @@ Given(/someone called (\w+)/, async function (name: string) {
104128
When(/(\w+) subscribes to author (\w+)$/, async function (this: World<Record<string, any>>, from: string, to: string) {
105129
const ws = this.parameters.clients[from] as WebSocket
106130
const pubkey = this.parameters.identities[to].pubkey
107-
const subscription = { name: `test-${Math.random()}`, filters: [{ authors: [pubkey] }] }
131+
132+
const subscription = {
133+
name: `test-${Math.random()}`,
134+
filters: [{ authors: [pubkey] }],
135+
}
136+
108137
this.parameters.subscriptions[from].push(subscription)
109138

110139
await createSubscription(ws, subscription.name, subscription.filters)
@@ -113,6 +142,7 @@ When(/(\w+) subscribes to author (\w+)$/, async function (this: World<Record<str
113142
Then(/(\w+) unsubscribes from author \w+/, async function (from: string) {
114143
const ws = this.parameters.clients[from] as WebSocket
115144
const subscription = this.parameters.subscriptions[from].pop()
145+
116146
return new Promise<void>((resolve, reject) => {
117147
ws.send(JSON.stringify(['CLOSE', subscription.name]), (err) => (err ? reject(err) : resolve()))
118148
})

0 commit comments

Comments
 (0)