holepunch-hop 0.4.5 → 0.5.0
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.
- package/.vscode/settings.json +3 -0
- package/package.json +11 -5
- package/src/adapters/timeConvertor.js +3 -0
- package/src/index.js +45 -10
- package/src/{peers.js → network/peers.js} +507 -136
- package/src/{bees.js → storage/bees.js} +142 -60
- package/src/{drive.js → storage/drive.js} +2 -3
- package/src/{fileParser.js → storage/fileParser.js} +7 -1
- package/test/datacommands/files/data/jan3-bitcoin.csv +17 -0
- package/test/datacommands/files/large-csv.test.js +7 -0
- package/test/datacommands/files/small-csv.test.js +76 -0
- package/test/datacommands/ledger/save-get-ledger.test.js +117 -0
- package/test/datacommands/results/save-get-results.test.js +122 -0
- package/test/holepunch-initiate.test.js +47 -0
- package/test/multipers/ten-peers-network.test.js +437 -0
- package/test/setup-bee-holepunch.test.js +45 -0
- package/test/setup-holepunch.test.js +14 -13
- package/test/threepers/peer3-geninvite-after.test.js +439 -0
- package/test/threepers/three-peers.test.js +159 -0
- package/test/threepers/two-then-three.test.js +434 -0
- package/test/twopeers/peerClient-Server.test.js +243 -0
- package/test/twopeers/reconnect-peers.test.js +257 -0
- package/test/twopeers/reconnect-serverthen-conerr.test.js +304 -0
- package/test/twopeers/reconnect-then-conerr.test.js +309 -0
- package/test/twopeers/two-peer-one-disconnect.test.js +162 -0
- package/test/twopeers/two-peer-server-disconnect.test.js +167 -0
- package/vitest.config.js +8 -0
- /package/src/{kbledger.js → ledger/kbledger.js} +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import Corestore from 'corestore'
|
|
3
|
+
import Hyperswarm from 'hyperswarm'
|
|
4
|
+
import Hyperbee from 'hyperbee'
|
|
5
|
+
import b4a from 'b4a'
|
|
6
|
+
import hashObject from 'object-hash'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import os from 'os'
|
|
9
|
+
import goodbye from 'graceful-goodbye'
|
|
10
|
+
import BeeWorker from '../../../src/storage/bees.js'
|
|
11
|
+
|
|
12
|
+
// Get the path to the test data directory
|
|
13
|
+
const testDataPath = path.join(__dirname, 'data')
|
|
14
|
+
|
|
15
|
+
describe('Hyperbee Operations Tests', () => {
|
|
16
|
+
let store
|
|
17
|
+
let swarm
|
|
18
|
+
let bee
|
|
19
|
+
let core4
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
console.log('start of before each')
|
|
23
|
+
store = new Corestore(os.homedir() + '/.test-bee-store')
|
|
24
|
+
swarm = new Hyperswarm()
|
|
25
|
+
// make replication possible
|
|
26
|
+
swarm.on('connection', conn => store.replicate(conn))
|
|
27
|
+
goodbye(() => swarm.destroy())
|
|
28
|
+
|
|
29
|
+
// Additional wait for full initialization
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 4000))
|
|
31
|
+
console.log('wait afoter')
|
|
32
|
+
// Create BeeWorker instance
|
|
33
|
+
bee = new BeeWorker(store, swarm)
|
|
34
|
+
// await bee.setupHyperbee() vitest have issues starting so many core at once
|
|
35
|
+
core4 = store.get({ name: 'hopresults' })
|
|
36
|
+
bee.dbHOPresults = new Hyperbee(core4, {
|
|
37
|
+
keyEncoding: 'utf-8', // can be set to undefined (binary), utf-8, ascii or and abstract-encoding
|
|
38
|
+
valueEncoding: 'json' // same options as above
|
|
39
|
+
})
|
|
40
|
+
await bee.dbHOPresults.ready()
|
|
41
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
42
|
+
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
afterAll(async () => {
|
|
46
|
+
console.log('Cleaning up test environment')
|
|
47
|
+
// await swarm.destroy()
|
|
48
|
+
await bee.dbHOPresults.close()
|
|
49
|
+
await core4.close()
|
|
50
|
+
bee = null
|
|
51
|
+
store = null
|
|
52
|
+
//swarm = null
|
|
53
|
+
core4 = null
|
|
54
|
+
}, 20000) // 20 second timeout for cleanup
|
|
55
|
+
|
|
56
|
+
it('should perform basic Hyperbee operations', async () => {
|
|
57
|
+
// Test data
|
|
58
|
+
console.log('start of test')
|
|
59
|
+
const testHash = 'test-hash'
|
|
60
|
+
const testData = { message: 'Hello Hyperbee!' }
|
|
61
|
+
|
|
62
|
+
// 1. Put data
|
|
63
|
+
await bee.dbHOPresults.put(testHash, testData)
|
|
64
|
+
|
|
65
|
+
// 2. Get data
|
|
66
|
+
const result = await bee.dbHOPresults.get(testHash)
|
|
67
|
+
expect(result.value).toEqual(testData)
|
|
68
|
+
|
|
69
|
+
// 3. Batch operations with 32-byte keys
|
|
70
|
+
let res1 = { bbid: '1212121212', data: { value: [1, 2, 3] }}
|
|
71
|
+
let res2 = { bbid: '3232323232', data: { value: [4, 5, 6] }}
|
|
72
|
+
const batch = bee.dbHOPresults.batch()
|
|
73
|
+
const key1 = hashObject(res1)
|
|
74
|
+
const key2 = hashObject(res2)
|
|
75
|
+
await batch.put(key1, res1)
|
|
76
|
+
await batch.put(key2, res2)
|
|
77
|
+
await batch.flush()
|
|
78
|
+
|
|
79
|
+
// 4. CreateReadStream
|
|
80
|
+
const stream = bee.dbHOPresults.createReadStream()
|
|
81
|
+
const entries = []
|
|
82
|
+
for await (const entry of stream) {
|
|
83
|
+
entries.push(entry)
|
|
84
|
+
}
|
|
85
|
+
expect(entries.length).toBeGreaterThan(0)
|
|
86
|
+
|
|
87
|
+
// 5. Check version
|
|
88
|
+
expect(bee.dbHOPresults.version).toBeGreaterThan(0)
|
|
89
|
+
|
|
90
|
+
// 6. Verify batch operations
|
|
91
|
+
const result1 = await bee.dbHOPresults.get(key1)
|
|
92
|
+
expect(result1.key).toEqual(key1)
|
|
93
|
+
const result2 = await bee.dbHOPresults.get(key2)
|
|
94
|
+
expect(result2.key).toEqual(key2)
|
|
95
|
+
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should handle key value operations', async () => {
|
|
99
|
+
// Test data
|
|
100
|
+
console.log('start of test2')
|
|
101
|
+
let res1 = { bbid: '1212121212', data: { value: [1, 2, 3] }}
|
|
102
|
+
let res2 = { bbid: '3232323232', data: { value: [4, 5, 6] }}
|
|
103
|
+
const key1 = hashObject(res1)
|
|
104
|
+
const key2 = hashObject(res2)
|
|
105
|
+
|
|
106
|
+
// Put multiple values
|
|
107
|
+
await bee.dbHOPresults.put(key1, res1)
|
|
108
|
+
await bee.dbHOPresults.put(key2, res2)
|
|
109
|
+
|
|
110
|
+
// Get values
|
|
111
|
+
const result1 = await bee.dbHOPresults.get(key1)
|
|
112
|
+
const result2 = await bee.dbHOPresults.get(key2)
|
|
113
|
+
expect(result1.value.data.value[0]).toEqual(1)
|
|
114
|
+
expect(result2.value.data.value[0]).toEqual(4)
|
|
115
|
+
|
|
116
|
+
// Delete value
|
|
117
|
+
await bee.dbHOPresults.del(key1)
|
|
118
|
+
const deletedResult = await bee.dbHOPresults.get(key1)
|
|
119
|
+
expect(deletedResult).toBeNull()
|
|
120
|
+
console.log('end of test2')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|
2
|
+
import { spawn } from 'child_process'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import HolepunchWorker from '../src/index.js'
|
|
5
|
+
|
|
6
|
+
// Set global test timeout to 10 seconds
|
|
7
|
+
const testTimeout = 10000;
|
|
8
|
+
|
|
9
|
+
describe('holepunch initialization', () => {
|
|
10
|
+
let hopProcess
|
|
11
|
+
let holepunch
|
|
12
|
+
|
|
13
|
+
beforeAll(async () => {
|
|
14
|
+
// Start HOP server
|
|
15
|
+
const baseHOPStepsUp = path.join(__dirname, '..')
|
|
16
|
+
hopProcess = spawn('npm', ['run', 'start'], { stdio: 'inherit', cwd: baseHOPStepsUp })
|
|
17
|
+
|
|
18
|
+
// Wait for server to start
|
|
19
|
+
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
20
|
+
|
|
21
|
+
// Initialize HolepunchWorker
|
|
22
|
+
holepunch = new HolepunchWorker('.test-instance')
|
|
23
|
+
await holepunch.startHolepunch()
|
|
24
|
+
// Additional wait for full initialization
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
afterAll(async () => {
|
|
29
|
+
// Clean up HolepunchWorker
|
|
30
|
+
// Stop HOP server
|
|
31
|
+
if (hopProcess) {
|
|
32
|
+
hopProcess.kill()
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should have initialized store', () => {
|
|
37
|
+
expect(holepunch.store).toBeDefined()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should have initialized swarm', () => {
|
|
41
|
+
expect(holepunch.swarm).toBeDefined()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('should have initialized BeeData', () => {
|
|
45
|
+
expect(holepunch.BeeData).toBeDefined()
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -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
|
|
2
|
-
import
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import HolepunchWorker from '../src/index.js'
|
|
3
3
|
|
|
4
|
-
describe('holepunch
|
|
5
|
-
it('
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
})
|