Skip to content

Commit b21fa56

Browse files
pyropybajtos
andauthored
feat: Add list nodes tests (#16)
* Add tests for listing arweave nodes * Remove redundant test * format * Update tests/nodes.test.js Co-authored-by: Miroslav Bajtoš <oss@bajtos.net> --------- Co-authored-by: Miroslav Bajtoš <oss@bajtos.net>
1 parent 02ee719 commit b21fa56

File tree

5 files changed

+113
-52
lines changed

5 files changed

+113
-52
lines changed

lib/nodes.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const IP_ADDRESS_REGEX = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
2+
3+
export const getNodes = async (fetchNodes = fetchNodesFromViewblock) => {
4+
// TODO: Find a publicly documented API
5+
const docs = []
6+
let maxPage = Infinity
7+
for (let page = 0; page < maxPage; page++) {
8+
const res = await fetchNodes(page)
9+
const body = await res.json()
10+
if (maxPage === Infinity) {
11+
maxPage = body.pages
12+
}
13+
docs.push(...body.docs)
14+
}
15+
const nodes = [
16+
{
17+
host: 'arweave.net',
18+
port: 443,
19+
protocol: 'https'
20+
},
21+
...docs
22+
.map(doc => doc.id)
23+
.filter(Boolean)
24+
.map(addr => {
25+
const [host, port] = addr.split(':')
26+
const protocol = IP_ADDRESS_REGEX.test(host) ? 'http' : 'https'
27+
return {
28+
host,
29+
port: port
30+
? Number(port)
31+
: protocol === 'http' ? 80 : 443,
32+
protocol
33+
}
34+
})
35+
]
36+
console.log(`Found ${nodes.length} nodes`)
37+
return nodes
38+
}
39+
40+
/**
41+
* Fetches arweave nodes from the ViewBlock API
42+
*
43+
* @param {number} page
44+
*/
45+
const fetchNodesFromViewblock = (page) => {
46+
return fetch(
47+
`https://api.viewblock.io/arweave/nodes?page=${page}&network=mainnet`,
48+
{
49+
headers: {
50+
Origin: 'https://viewblock.io'
51+
}
52+
}
53+
)
54+
}

main.js

+1-45
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import './vendor/arweave.js'
44
import pTimeout from './vendor/p-timeout.js'
5+
import { getNodes } from './lib/nodes.js'
56

6-
const IP_ADDRESS_REGEX = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
77
const ONE_MINUTE = 60_000
88
const MEASUREMENT_DELAY = ONE_MINUTE
99
const UPDATE_NODES_DELAY = 10 * ONE_MINUTE
@@ -16,50 +16,6 @@ const RANDOM_TRANSACTION_IDS = [
1616
]
1717
const RETRIEVE_TIMEOUT = 10_000
1818

19-
const getNodes = async () => {
20-
// TODO: Find a publicly documented API
21-
const docs = []
22-
let maxPage = Infinity
23-
for (let page = 0; page < maxPage; page++) {
24-
const res = await fetch(
25-
`https://api.viewblock.io/arweave/nodes?page=${page + 1}&network=mainnet`,
26-
{
27-
headers: {
28-
Origin: 'https://viewblock.io'
29-
}
30-
}
31-
)
32-
const body = await res.json()
33-
if (maxPage === Infinity) {
34-
maxPage = body.pages
35-
}
36-
docs.push(...body.docs)
37-
}
38-
const nodes = [
39-
{
40-
host: 'arweave.net',
41-
port: 443,
42-
protocol: 'https'
43-
},
44-
...docs
45-
.map(doc => doc.id)
46-
.filter(Boolean)
47-
.map(addr => {
48-
const [host, port] = addr.split(':')
49-
const protocol = IP_ADDRESS_REGEX.test(host) ? 'http' : 'https'
50-
return {
51-
host,
52-
port: port
53-
? Number(port)
54-
: protocol === 'http' ? 80 : 443,
55-
protocol
56-
}
57-
})
58-
]
59-
console.log(`Found ${nodes.length} nodes`)
60-
return nodes
61-
}
62-
6319
const measure = async node => {
6420
const arweave = Arweave.init(node)
6521
const txId = RANDOM_TRANSACTION_IDS[Math.floor(Math.random() * RANDOM_TRANSACTION_IDS.length)]

test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import './tests/example.test.js'
1+
import './tests/nodes.test.js'

tests/example.test.js

-6
This file was deleted.

tests/nodes.test.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { test } from 'zinnia:test'
2+
import { assertEquals } from 'zinnia:assert'
3+
import { getNodes } from '../lib/nodes.js'
4+
5+
test('should return arweave.net node by default', async () => {
6+
const mockFetch = (pages) => {
7+
return Promise.resolve({
8+
json: () => ({
9+
pages: 1,
10+
docs: []
11+
})
12+
})
13+
}
14+
15+
const nodes = await getNodes(mockFetch)
16+
assertEquals(nodes, [{
17+
host: 'arweave.net', port: 443, protocol: 'https'
18+
}])
19+
})
20+
21+
test('should return nodes from all pages with a custom fetch function', async () => {
22+
const mockFetch = (page) => {
23+
if (page === 0) {
24+
return Promise.resolve({
25+
json: () => ({
26+
pages: 2,
27+
docs: [
28+
{ id: '127.0.0.1:1984' },
29+
{ id: '127.0.0.1:1986' },
30+
{}
31+
]
32+
})
33+
})
34+
}
35+
36+
return Promise.resolve({
37+
json: () => ({
38+
pages: 2,
39+
docs: [
40+
{ id: '127.0.0.1' },
41+
{ id: 'test.arweave.net' },
42+
{ id: '' }
43+
]
44+
})
45+
})
46+
}
47+
48+
const nodes = await getNodes(mockFetch)
49+
assertEquals(nodes.length, 5)
50+
assertEquals(nodes, [
51+
{ host: 'arweave.net', port: 443, protocol: 'https' },
52+
{ host: '127.0.0.1', port: 1984, protocol: 'http' },
53+
{ host: '127.0.0.1', port: 1986, protocol: 'http' },
54+
{ host: '127.0.0.1', port: 80, protocol: 'http' },
55+
{ host: 'test.arweave.net', port: 443, protocol: 'https' }
56+
])
57+
})

0 commit comments

Comments
 (0)