Skip to content

Commit 9aca129

Browse files
committed
test: add intg test for nip-33
1 parent 8b012ea commit 9aca129

File tree

5 files changed

+348
-273
lines changed

5 files changed

+348
-273
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import * as secp256k1 from '@noble/secp256k1'
2+
import WebSocket, { RawData } from 'ws'
3+
import { createHmac } from 'crypto'
4+
5+
import { Event } from '../../../src/@types/event'
6+
import { MessageType } from '../../../src/@types/messages'
7+
import { serializeEvent } from '../../../src/utils/event'
8+
import { SubscriptionFilter } from '../../../src/@types/subscription'
9+
10+
11+
export async function connect(_name: string) {
12+
const host = 'ws://localhost:18808'
13+
const ws = new WebSocket(host)
14+
await new Promise<void>((resolve, reject) => {
15+
ws
16+
.once('open', () => {
17+
resolve()
18+
})
19+
.once('error', reject)
20+
.once('close', () => {
21+
ws.removeAllListeners()
22+
})
23+
})
24+
return ws
25+
}
26+
27+
let eventCount = 0
28+
29+
export async function createEvent(input: Partial<Event>, privkey: any): Promise<Event> {
30+
const event: Event = {
31+
pubkey: input.pubkey,
32+
kind: input.kind,
33+
created_at: input.created_at ?? Math.floor(Date.now() / 1000) + eventCount++,
34+
content: input.content ?? '',
35+
tags: input.tags ?? [],
36+
} as any
37+
38+
const id = Buffer.from(
39+
await secp256k1.utils.sha256(
40+
Buffer.from(JSON.stringify(serializeEvent(event)))
41+
)
42+
).toString('hex')
43+
44+
const sig = Buffer.from(
45+
await secp256k1.schnorr.sign(id, privkey)
46+
).toString('hex')
47+
48+
return { id, ...event, sig }
49+
}
50+
51+
export function createIdentity(name: string) {
52+
const hmac = createHmac('sha256', process.env.SECRET ?? Math.random().toString())
53+
hmac.update(name)
54+
const privkey = hmac.digest().toString('hex')
55+
const pubkey = Buffer.from(secp256k1.getPublicKey(privkey, true)).toString('hex').substring(2)
56+
const author = {
57+
name,
58+
privkey,
59+
pubkey,
60+
}
61+
return author
62+
}
63+
64+
export async function createSubscription(
65+
ws: WebSocket,
66+
subscriptionName: string,
67+
subscriptionFilters: SubscriptionFilter[],
68+
): Promise<void> {
69+
return new Promise<void>((resolve, reject) => {
70+
const message = JSON.stringify([
71+
'REQ',
72+
subscriptionName,
73+
...subscriptionFilters,
74+
])
75+
76+
ws.send(message, (error: Error) => {
77+
if (error) {
78+
reject(error)
79+
return
80+
}
81+
resolve()
82+
})
83+
})
84+
}
85+
86+
export async function waitForEOSE(ws: WebSocket, subscription: string): Promise<void> {
87+
return new Promise<void>((resolve, reject) => {
88+
function cleanup() {
89+
ws.removeListener('message', onMessage)
90+
ws.removeListener('error', onError)
91+
}
92+
93+
function onError(error: Error) {
94+
reject(error)
95+
cleanup()
96+
}
97+
ws.once('error', onError)
98+
99+
function onMessage(raw: RawData) {
100+
const message = JSON.parse(raw.toString('utf-8'))
101+
if (message[0] === MessageType.EOSE && message[1] === subscription) {
102+
resolve()
103+
cleanup()
104+
} else if (message[0] === MessageType.NOTICE) {
105+
reject(new Error(message[1]))
106+
cleanup()
107+
}
108+
}
109+
110+
ws.on('message', onMessage)
111+
})
112+
}
113+
114+
export async function sendEvent(ws: WebSocket, event: Event) {
115+
return new Promise<void>((resolve, reject) => {
116+
ws.send(JSON.stringify(['EVENT', event]), (err) => {
117+
if (err) {
118+
reject(err)
119+
return
120+
}
121+
resolve()
122+
})
123+
})
124+
}
125+
126+
export async function waitForNextEvent(ws: WebSocket, subscription: string): Promise<Event> {
127+
return new Promise((resolve, reject) => {
128+
ws.on('message', onMessage)
129+
ws.once('error', onError)
130+
131+
function cleanup() {
132+
ws.removeListener('message', onMessage)
133+
ws.removeListener('error', onError)
134+
}
135+
136+
function onError(error: Error) {
137+
reject(error)
138+
cleanup()
139+
}
140+
141+
function onMessage(raw: RawData) {
142+
const message = JSON.parse(raw.toString('utf-8'))
143+
if (message[0] === MessageType.EVENT && message[1] === subscription) {
144+
resolve(message[2])
145+
cleanup()
146+
} else if (message[0] === MessageType.NOTICE) {
147+
reject(new Error(message[1]))
148+
cleanup()
149+
}
150+
}
151+
})
152+
}
153+
154+
export async function waitForEventCount(
155+
ws: WebSocket,
156+
subscription: string,
157+
count = 1,
158+
eose = false,
159+
): Promise<Event[]> {
160+
const events = []
161+
162+
return new Promise((resolve, reject) => {
163+
ws.on('message', onMessage)
164+
ws.once('error', onError)
165+
function cleanup() {
166+
ws.removeListener('message', onMessage)
167+
ws.removeListener('error', onError)
168+
}
169+
170+
function onError(error: Error) {
171+
reject(error)
172+
cleanup()
173+
}
174+
function onMessage(raw: RawData) {
175+
const message = JSON.parse(raw.toString('utf-8'))
176+
if (message[0] === MessageType.EVENT && message[1] === subscription) {
177+
events.push(message[2])
178+
if (!eose && events.length === count) {
179+
resolve(events)
180+
cleanup()
181+
} else if (events.length > count) {
182+
reject(new Error(`Expected ${count} but got ${events.length} events`))
183+
cleanup()
184+
}
185+
} else if (message[0] === MessageType.EOSE && message[1] === subscription) {
186+
if (!eose) {
187+
reject(new Error('Expected event but received EOSE'))
188+
} else if (events.length !== count) {
189+
reject(new Error(`Expected ${count} but got ${events.length} events before EOSE`))
190+
} else {
191+
resolve(events)
192+
}
193+
cleanup()
194+
} else if (message[0] === MessageType.NOTICE) {
195+
reject(new Error(message[1]))
196+
cleanup()
197+
}
198+
}
199+
})
200+
}
201+
202+
export async function waitForNotice(ws: WebSocket): Promise<void> {
203+
return new Promise<void>((resolve, reject) => {
204+
function cleanup() {
205+
ws.removeListener('message', onMessage)
206+
ws.removeListener('error', onError)
207+
}
208+
209+
function onError(error: Error) {
210+
reject(error)
211+
cleanup()
212+
}
213+
ws.once('error', onError)
214+
215+
function onMessage(raw: RawData) {
216+
const message = JSON.parse(raw.toString('utf-8'))
217+
if (message[0] === MessageType.NOTICE) {
218+
resolve(message[1])
219+
cleanup()
220+
}
221+
}
222+
223+
ws.on('message', onMessage)
224+
})
225+
}

0 commit comments

Comments
 (0)