holepunch-hop 0.4.4 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,437 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest'
2
+ import Hyperswarm from 'hyperswarm'
3
+ import NetworkPeers from '../../src/network/peers.js'
4
+ import crypto from 'crypto'
5
+
6
+ // Set global test timeout to 60 seconds
7
+ const testTimeout = 60000;
8
+
9
+ // Helper function to create a peer
10
+ const createPeer = (index) => {
11
+ const swarm = new Hyperswarm()
12
+ const peer = new NetworkPeers({}, swarm)
13
+ return {
14
+ swarm,
15
+ peer,
16
+ publicKey: peer.swarm.keyPair.publicKey.toString('hex')
17
+ }
18
+ }
19
+
20
+ describe('Ten Peer Network Tests', () => {
21
+ let peers = []
22
+ let swarms = []
23
+ let testConfig = {}
24
+ let connectionPromises = []
25
+ const peerCount = 10
26
+
27
+ beforeEach(async () => {
28
+ // Create all peers with retry
29
+ const maxAttempts = 3
30
+ const attemptDelay = 5000
31
+
32
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
33
+ try {
34
+ // Reset arrays
35
+ peers = []
36
+ swarms = []
37
+ connectionPromises = []
38
+ testConfig = {}
39
+
40
+ // Create all peers
41
+ for (let i = 0; i < peerCount; i++) {
42
+ const { swarm, peer, publicKey } = createPeer(i)
43
+ peers.push(peer)
44
+ swarms.push(swarm)
45
+
46
+ // Create test config
47
+ testConfig[`peer${i}`] = {
48
+ publicKey,
49
+ client: i % 2 === 0 // Alternate between client and server roles
50
+ }
51
+ }
52
+
53
+ // Create connection promises for verification
54
+ for (let i = 0; i < peerCount; i++) {
55
+ connectionPromises.push(new Promise((resolve) => {
56
+ let connectionResolved = false
57
+ peers[i].swarm.on('connection', (conn, info) => {
58
+ if (!connectionResolved) {
59
+ connectionResolved = true
60
+ resolve({
61
+ peerIndex: i,
62
+ publicKey: info.publicKey.toString('hex'),
63
+ client: info.client
64
+ })
65
+ }
66
+ })
67
+ }))
68
+ }
69
+
70
+ // Verify peers are created
71
+ expect(peers.length).toBe(peerCount)
72
+ expect(swarms.length).toBe(peerCount)
73
+ expect(connectionPromises.length).toBe(peerCount)
74
+
75
+ return // Success!
76
+ } catch (error) {
77
+ console.error(`Peer creation attempt ${attempt + 1} failed:`, error.message)
78
+
79
+ // Wait before next attempt
80
+ if (attempt < maxAttempts - 1) {
81
+ await new Promise(resolve => setTimeout(resolve, attemptDelay))
82
+ }
83
+ }
84
+ }
85
+
86
+ // If we get here, all attempts failed
87
+ throw new Error('Failed to create all peers after multiple attempts')
88
+ }, 60000)
89
+
90
+ afterEach(async () => {
91
+ // Clean up all connections
92
+ for (const peer of peers) {
93
+ if (peer && peer.swarm) {
94
+ await peer.swarm.destroy()
95
+ }
96
+ }
97
+ peers = []
98
+ swarms = []
99
+ connectionPromises = []
100
+ }, 30000)
101
+
102
+ describe.skip('Mesh Network Tests', () => {
103
+ it('should establish mesh network connections', async () => {
104
+ // Create mesh network with explicit connections
105
+ const connectionRetryDelay = 2000
106
+ const maxConnectionAttempts = 5
107
+
108
+ // Create a mesh network where each peer connects to two others
109
+ for (let i = 0; i < peerCount; i++) {
110
+ const peer = peers[i]
111
+ const nextPeer = peers[(i + 1) % peerCount]
112
+ const prevPeer = peers[(i + peerCount - 1) % peerCount]
113
+
114
+ // Function to handle peer connection with retry
115
+ const connectWithRetry = async (targetPeer, isNext = true) => {
116
+ let attempts = 0
117
+
118
+ while (attempts < maxConnectionAttempts) {
119
+ try {
120
+ console.log(`Peer ${i} attempting to connect to ${isNext ? 'next' : 'prev'} peer ${attempts + 1}/${maxConnectionAttempts}`)
121
+
122
+ // First listen for incoming connections
123
+ peer.peerJoinClient()
124
+
125
+ // Then join the target peer
126
+ await peer.peerJoin({
127
+ publickey: targetPeer.swarm.keyPair.publicKey.toString('hex'),
128
+ live: false,
129
+ value: {
130
+ live: false,
131
+ key: targetPeer.swarm.keyPair.publicKey.toString('hex')
132
+ }
133
+ })
134
+
135
+ // Wait for connection to be established
136
+ await new Promise(resolve => setTimeout(resolve, 3000))
137
+
138
+ // Verify connection
139
+ const connections = peer.peerConnect
140
+ if (connections && Object.keys(connections).length >= (isNext ? 1 : 2)) {
141
+ // Log connection details
142
+ console.log(`Peer ${i} successfully connected to ${isNext ? 'next' : 'prev'} peer`)
143
+ console.log('Current connections:', connections)
144
+ return true
145
+ } else {
146
+ console.log(`Peer ${i} connection verification failed after ${attempts + 1} attempts. Connections:`, connections)
147
+ }
148
+ } catch (error) {
149
+ console.error(`Connection attempt ${attempts + 1} failed for peer ${i} to ${isNext ? 'next' : 'prev'} peer:`, error)
150
+ if (error.stack) {
151
+ console.error('Error stack:', error.stack)
152
+ }
153
+ }
154
+
155
+ attempts++
156
+ if (attempts < maxConnectionAttempts) {
157
+ console.log(`Peer ${i} waiting ${connectionRetryDelay}ms before retrying connection to ${isNext ? 'next' : 'prev'} peer`)
158
+ await new Promise(resolve => setTimeout(resolve, connectionRetryDelay))
159
+ }
160
+ }
161
+
162
+ console.error(`Failed to connect peer ${i} to ${isNext ? 'next' : 'prev'} peer after ${maxConnectionAttempts} attempts`)
163
+ return false
164
+ }
165
+
166
+ // Connect to next peer with retry
167
+ const nextConnected = await connectWithRetry(nextPeer, true)
168
+ if (!nextConnected) {
169
+ throw new Error(`Failed to connect peer ${i} to next peer after ${maxConnectionAttempts} attempts`)
170
+ }
171
+
172
+ // Connect to previous peer with retry
173
+ const prevConnected = await connectWithRetry(prevPeer, false)
174
+ if (!prevConnected) {
175
+ throw new Error(`Failed to connect peer ${i} to prev peer after ${maxConnectionAttempts} attempts`)
176
+ }
177
+ }
178
+
179
+ // Wait for all connections to stabilize
180
+ await new Promise(resolve => setTimeout(resolve, 5000))
181
+
182
+ // Verify all connections
183
+ for (let i = 0; i < peerCount; i++) {
184
+ const peer = peers[i]
185
+ if (!peer || !peer.peerConnect) {
186
+ throw new Error(`Peer ${i} has no peerConnect object`)
187
+ }
188
+
189
+ const connections = peer.peerConnect
190
+ const connectionCount = Object.keys(connections).length
191
+
192
+ if (connectionCount !== 2) {
193
+ console.log(`Peer ${i} has ${connectionCount} connections`)
194
+ console.log('Peer connections:', connections)
195
+
196
+ // Check if we have any connection data at all
197
+ if (Object.keys(connections).length === 0) {
198
+ throw new Error(`Peer ${i} has no connections. This is unexpected.`)
199
+ }
200
+
201
+ // Check if we have any connection data at all
202
+ if (Object.keys(connections).length < 2) {
203
+ throw new Error(`Peer ${i} has fewer than 2 connections. This is unexpected.`)
204
+ }
205
+ }
206
+
207
+ expect(connectionCount).toBe(2)
208
+ }
209
+ }, 60000)
210
+
211
+ it('should handle random peer disconnections', async () => {
212
+ // Disconnect a random peer
213
+ const randomIndex = Math.floor(Math.random() * peerCount)
214
+ const randomPeer = peers[randomIndex]
215
+
216
+ // Disconnect all its connections
217
+ if (randomPeer && randomPeer.peerConnect) {
218
+ Object.values(randomPeer.peerConnect).forEach(conn => {
219
+ conn.destroy()
220
+ })
221
+ randomPeer.peerConnect = {}
222
+ }
223
+
224
+ // Wait for disconnection to complete
225
+ await new Promise(resolve => setTimeout(resolve, 3000))
226
+
227
+ // Verify that other peers maintain their connections
228
+ for (let i = 0; i < peerCount; i++) {
229
+ if (i !== randomIndex) {
230
+ const peer = peers[i]
231
+ if (!peer || !peer.peerConnect) {
232
+ throw new Error(`Peer ${i} has no peerConnect object after disconnection`)
233
+ }
234
+
235
+ const connections = peer.peerConnect
236
+ const connectionCount = Object.keys(connections).length
237
+
238
+ if (connectionCount !== 2) {
239
+ console.log(`Peer ${i} has ${connectionCount} connections after disconnection`)
240
+ console.log('Peer connections:', connections)
241
+ }
242
+
243
+ expect(connectionCount).toBe(2)
244
+ }
245
+ }
246
+ }, 60000)
247
+
248
+ it('should handle reconnection after disconnection', async () => {
249
+ // Disconnect a random peer
250
+ const randomIndex = Math.floor(Math.random() * peerCount)
251
+ const randomPeer = peers[randomIndex]
252
+
253
+ // Disconnect all its connections
254
+ if (randomPeer && randomPeer.peerConnect) {
255
+ Object.values(randomPeer.peerConnect).forEach(conn => {
256
+ conn.destroy()
257
+ })
258
+ randomPeer.peerConnect = {}
259
+ }
260
+
261
+ // Wait for disconnection to complete
262
+ await new Promise(resolve => setTimeout(resolve, 2000))
263
+
264
+ // Reconnect to neighbors with retry
265
+ const connectionRetryDelay = 2000
266
+ const maxConnectionAttempts = 3
267
+
268
+ if (randomPeer) {
269
+ const nextPeer = peers[(randomIndex + 1) % peerCount]
270
+ const prevPeer = peers[(randomIndex + peerCount - 1) % peerCount]
271
+
272
+ // Function to handle peer connection with retry
273
+ const connectWithRetry = async (targetPeer, isNext = true) => {
274
+ let attempts = 0
275
+ while (attempts < maxConnectionAttempts) {
276
+ try {
277
+ randomPeer.peerJoin({
278
+ publickey: targetPeer.swarm.keyPair.publicKey.toString('hex'),
279
+ live: false,
280
+ value: {
281
+ live: false,
282
+ key: targetPeer.swarm.keyPair.publicKey.toString('hex')
283
+ }
284
+ })
285
+
286
+ // Wait for connection to be established
287
+ await new Promise(resolve => setTimeout(resolve, 1000))
288
+
289
+ // Verify connection
290
+ const connections = randomPeer.peerConnect
291
+ if (connections && Object.keys(connections).length >= (isNext ? 1 : 2)) {
292
+ return true
293
+ }
294
+ } catch (error) {
295
+ console.log(`Reconnection attempt ${attempts + 1} failed for peer ${randomIndex} to ${isNext ? 'next' : 'prev'} peer`)
296
+ }
297
+
298
+ attempts++
299
+ if (attempts < maxConnectionAttempts) {
300
+ await new Promise(resolve => setTimeout(resolve, connectionRetryDelay))
301
+ }
302
+ }
303
+ return false
304
+ }
305
+
306
+ // Reconnect to next peer
307
+ await connectWithRetry(nextPeer, true)
308
+
309
+ // Reconnect to previous peer
310
+ await connectWithRetry(prevPeer, false)
311
+ }
312
+
313
+ // Wait for all connections to be established
314
+ await new Promise(resolve => setTimeout(resolve, 2000))
315
+
316
+ // Verify all connections
317
+ for (const peer of peers) {
318
+ if (peer && peer.peerConnect) {
319
+ const connections = peer.peerConnect
320
+ const connectionCount = Object.keys(connections).length
321
+
322
+ if (connectionCount !== 2) {
323
+ console.log(`Peer has ${connectionCount} connections after reconnection`)
324
+ }
325
+
326
+ expect(connectionCount).toBe(2)
327
+ }
328
+ }
329
+ }, 60000)
330
+ })
331
+
332
+ describe('Star Network Tests', () => {
333
+ it('should establish star topology', async () => {
334
+ // Create star topology where peer0 is the center
335
+ const centerPeer = peers[0]
336
+
337
+ // All other peers connect to center peer
338
+ for (let i = 1; i < peerCount; i++) {
339
+ const peer = peers[i]
340
+ peer.peerJoin({
341
+ publickey: centerPeer.swarm.keyPair.publicKey.toString('hex'),
342
+ live: false,
343
+ value: {
344
+ live: false,
345
+ key: centerPeer.swarm.keyPair.publicKey.toString('hex')
346
+ }
347
+ })
348
+ }
349
+
350
+ // Wait for connections with retry
351
+ const maxAttempts = 5
352
+ const attemptDelay = 5000
353
+
354
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
355
+ try {
356
+ // Wait for connections to stabilize
357
+ await new Promise(resolve => setTimeout(resolve, 2000))
358
+
359
+ // Verify connections
360
+ let allConnectionsEstablished = true
361
+ for (let i = 0; i < peerCount; i++) {
362
+ const peer = peers[i]
363
+ if (!peer || !peer.peerConnect) {
364
+ throw new Error(`Peer ${i} has no peerConnect object`)
365
+ }
366
+
367
+ const connectionCount = Object.keys(peer.peerConnect).length
368
+ if (i === 0) {
369
+ if (connectionCount !== 9) {
370
+ console.log(`Center peer has ${connectionCount} connections after attempt ${attempt + 1}`)
371
+ if (attempt === maxAttempts - 1) {
372
+ expect(connectionCount).toBe(9)
373
+ }
374
+ allConnectionsEstablished = false
375
+ }
376
+ } else {
377
+ if (connectionCount !== 1) {
378
+ console.log(`Peer ${i} has ${connectionCount} connections after attempt ${attempt + 1}`)
379
+ if (attempt === maxAttempts - 1) {
380
+ expect(connectionCount).toBe(1)
381
+ }
382
+ allConnectionsEstablished = false
383
+ }
384
+ }
385
+ }
386
+
387
+ if (allConnectionsEstablished) {
388
+ return
389
+ }
390
+ } catch (error) {
391
+ console.error(`Attempt ${attempt + 1} failed:`, error.message)
392
+
393
+ // Wait before next attempt
394
+ if (attempt < maxAttempts - 1) {
395
+ await new Promise(resolve => setTimeout(resolve, attemptDelay))
396
+ }
397
+ }
398
+ }
399
+ }, 30000)
400
+
401
+ it('should handle center peer failure', async () => {
402
+ // Create star topology
403
+ const centerPeer = peers[0]
404
+ for (let i = 1; i < peerCount; i++) {
405
+ const peer = peers[i]
406
+ peer.peerJoin({
407
+ publickey: centerPeer.swarm.keyPair.publicKey.toString('hex'),
408
+ live: false,
409
+ value: {
410
+ live: false,
411
+ key: centerPeer.swarm.keyPair.publicKey.toString('hex')
412
+ }
413
+ })
414
+ }
415
+
416
+ // Wait for connections
417
+ await new Promise(resolve => setTimeout(resolve, 2000))
418
+
419
+ // Disconnect center peer
420
+ if (centerPeer && centerPeer.peerConnect) {
421
+ Object.values(centerPeer.peerConnect).forEach(conn => {
422
+ conn.destroy()
423
+ })
424
+ centerPeer.peerConnect = {}
425
+ }
426
+
427
+ // Wait for disconnection to propagate
428
+ await new Promise(resolve => setTimeout(resolve, 2000))
429
+
430
+ // Verify all other peers lost connection
431
+ for (let i = 1; i < peerCount; i++) {
432
+ const peer = peers[i]
433
+ expect(Object.keys(peer.peerConnect).length).toBe(0)
434
+ }
435
+ }, 30000)
436
+ })
437
+ })
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import HolepunchWorker from '../src/index.js'
3
+
4
+ class MockWebSocket {
5
+ constructor() {
6
+ this.sentMessages = []
7
+ }
8
+
9
+ send(data) {
10
+ this.sentMessages.push(data)
11
+ }
12
+
13
+ close() {}
14
+ }
15
+
16
+ describe('holepunch initialization', () => {
17
+ it('should initialize without store name', async () => {
18
+ const holepunch = new HolepunchWorker()
19
+ expect(holepunch).toBeDefined()
20
+ expect(holepunch.peerStore).toBe('.hop-storage')
21
+ })
22
+
23
+ it('should initialize with store name', async () => {
24
+ // setup mock websocket
25
+ const mockWs = new MockWebSocket()
26
+ // setup store
27
+ const storeName = 'hop-storage-test'
28
+ const holepunch = new HolepunchWorker(storeName)
29
+ // Add timeout to prevent hanging
30
+ console.log('start stores')
31
+ // Wait for 3 seconds before proceeding
32
+ await new Promise(resolve => setTimeout(resolve, 3000))
33
+ console.log('end stores')
34
+ expect(holepunch).toBeDefined()
35
+ holepunch.setWebsocket(mockWs)
36
+ expect(holepunch.peerStore).toBe('.' + storeName)
37
+ await holepunch.startStores()
38
+ holepunch.on('hcores-active', () => {
39
+ // count number of bee stores
40
+ console.log('bees')
41
+ console.log(holepunch.BeeData.activeBees)
42
+ })
43
+
44
+ })
45
+ })
@@ -1,16 +1,17 @@
1
- import assert from 'assert'
2
- import HolepunchData from '../src/index.js'
1
+ import { describe, it, expect } from 'vitest'
2
+ import HolepunchWorker from '../src/index.js'
3
3
 
4
- describe('holepunch hypercore bee drive peers setup', function () {
5
- it('hello from holepunch', async function () {
6
- let dataAPI = new HolepunchData()
7
- assert.equal(dataAPI.hello, 'holepunch')
8
- await dataAPI.testCoreStore()
9
- await dataAPI.readHypercoreTest()
10
- assert.equal(dataAPI.readcore.toString('utf8'), 'data data in hypercore')
11
- // console.log('back from bees')
12
- // console.log(dataAPI.BeeData)
13
- // console.log('back from drive')
14
- // console.log(dataAPI.DriveFiles)
4
+ describe('holepunch initialization', () => {
5
+ it('should initialize without store name', () => {
6
+ const holepunch = new HolepunchWorker()
7
+ expect(holepunch).toBeDefined()
8
+ expect(holepunch.peerStore).toBe('.hop-storage')
9
+ })
10
+
11
+ it('should initialize with store name', () => {
12
+ const storeName = 'hop-storage-test'
13
+ const holepunch = new HolepunchWorker(storeName)
14
+ expect(holepunch).toBeDefined()
15
+ expect(holepunch.peerStore).toBe('.' +storeName)
15
16
  })
16
17
  })