Reduce sample to network smoke test
This commit is contained in:
parent
e24914e76a
commit
4f0f66cf19
16
README.md
16
README.md
@ -1,6 +1,6 @@
|
|||||||
# u-js-sample
|
# u-js-sample
|
||||||
|
|
||||||
A small Node.js command-line example using `universa-core2@2.0.0-alpha.1`.
|
A minimal network smoke test using `universa-core2@2.0.0-alpha.1`.
|
||||||
|
|
||||||
## Initialize and test
|
## Initialize and test
|
||||||
|
|
||||||
@ -9,14 +9,6 @@ npm ci
|
|||||||
scripts/test-network.sh
|
scripts/test-network.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The test loads `src/universa.json` through `Topology`, connects with `Network`,
|
The test loads `src/universa.json`, initializes `Network`, connects to the
|
||||||
authenticates to every available node, and verifies `sping=spong`. The alpha
|
available topology, and asserts that the network returns `sping=spong`. The
|
||||||
package intentionally has no bundled default topology, so applications must
|
alpha package intentionally has no bundled default topology.
|
||||||
provide one explicitly.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm start -- --generate-key
|
|
||||||
npm start -- --test-network
|
|
||||||
```
|
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "u-js-sample",
|
"name": "u-js-sample",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "A small universa-core2 Node.js sample.",
|
"description": "A minimal universa-core2 network smoke test.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"bin": {
|
|
||||||
"u-js-sample": "./src/index.js"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/index.js",
|
"start": "node src/index.js",
|
||||||
"test:network": "bash scripts/test-network.sh",
|
"test:network": "bash scripts/test-network.sh",
|
||||||
"check": "node --check src/index.js",
|
"check": "node --check src/index.js",
|
||||||
"test": "npm run check && npm run test:network"
|
"test": "npm run test:network"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
|
|||||||
94
src/index.js
94
src/index.js
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
import { dirname, resolve } from 'node:path';
|
import { dirname, resolve } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
@ -13,91 +14,20 @@ import {
|
|||||||
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
||||||
const topologyFile = resolve(sourceDirectory, 'universa.json');
|
const topologyFile = resolve(sourceDirectory, 'universa.json');
|
||||||
|
|
||||||
async function withTimeout(promise, milliseconds, message) {
|
await unicryptoReady;
|
||||||
let timeoutId;
|
|
||||||
const timeout = new Promise((_, reject) => {
|
|
||||||
timeoutId = setTimeout(() => reject(new Error(message)), milliseconds);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
const topologyData = JSON.parse(await readFile(topologyFile, 'utf8'));
|
||||||
return await Promise.race([promise, timeout]);
|
const topology = await Topology.load(topologyData);
|
||||||
} finally {
|
const privateKey = await PrivateKey.generate({ strength: 2048 });
|
||||||
clearTimeout(timeoutId);
|
const network = new Network(privateKey, {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadTopology() {
|
|
||||||
const packed = JSON.parse(await readFile(topologyFile, 'utf8'));
|
|
||||||
return Topology.load(packed);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testNetwork(privateKey) {
|
|
||||||
const topology = await loadTopology();
|
|
||||||
const network = new Network(privateKey, {
|
|
||||||
topology,
|
topology,
|
||||||
directConnection: true
|
directConnection: true
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Testing ${topology.size()} Universa nodes from ${topologyFile}`);
|
console.log(`Connecting to ${topology.size()} Universa nodes from ${topologyFile}`);
|
||||||
await withTimeout(network.connect(), 15_000, 'Network connection timed out');
|
await network.connect();
|
||||||
|
|
||||||
const nodes = Object.entries(network.topology.nodes);
|
const response = await network.command('sping');
|
||||||
const results = await Promise.all(nodes.map(async ([id, node]) => {
|
assert.equal(response?.sping, 'spong', 'unexpected network state response');
|
||||||
try {
|
|
||||||
const connection = await withTimeout(
|
|
||||||
network.nodeConnection(id),
|
|
||||||
10_000,
|
|
||||||
`${node.name} connection timed out`
|
|
||||||
);
|
|
||||||
const response = await connection.command('sping', {}, { timeout: 5_000 });
|
|
||||||
if (response?.sping !== 'spong') throw new Error('unexpected sping response');
|
|
||||||
console.log(`PASS ${node.name}: authenticated, sping=spong`);
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`FAIL ${node.name}: ${error instanceof Error ? error.message : error}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const passed = results.filter(Boolean).length;
|
console.log(`Universa network is functioning: ${network.size()} nodes, sping=spong`);
|
||||||
const quorum = Math.max(1, Math.ceil(nodes.length * 0.4));
|
|
||||||
if (passed < quorum) {
|
|
||||||
throw new Error(`Network quorum failed: ${passed}/${nodes.length} nodes passed; ${quorum} required`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Universa network is functioning: ${passed}/${nodes.length} nodes passed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function printHelp() {
|
|
||||||
console.log(`Usage: u-js-sample [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--generate-key Generate a private key and print its short address
|
|
||||||
--test-network Test every node in src/universa.json
|
|
||||||
--help Show this help`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const options = new Set(process.argv.slice(2));
|
|
||||||
if (options.size === 0 || options.has('--help')) return printHelp();
|
|
||||||
|
|
||||||
const unknown = [...options].find((option) => (
|
|
||||||
option !== '--generate-key' && option !== '--test-network'
|
|
||||||
));
|
|
||||||
if (unknown) throw new Error(`Unknown option: ${unknown}`);
|
|
||||||
|
|
||||||
if (options.has('--generate-key') || options.has('--test-network')) {
|
|
||||||
await unicryptoReady;
|
|
||||||
const privateKey = await PrivateKey.generate({ strength: 2048 });
|
|
||||||
console.log(`Universa short address: ${privateKey.publicKey.shortAddress58}`);
|
|
||||||
|
|
||||||
if (options.has('--test-network')) await testNetwork(privateKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await main();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error instanceof Error ? error.message : error);
|
|
||||||
process.exitCode = 1;
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user