diff --git a/README.md b/README.md index 112447ad..6e2fbe02 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Make a particle const particle = new Particle( ` (seq - (call myRelay ("op" "identify") [] result) + (call myRelay ("peer" "identify") [] result) (call %init_peer_id% ("helloService" "helloFunction") [result]) )`, { @@ -112,10 +112,34 @@ npm install ### Running tests -To run test execute +Tests are split into unit and integration categories. By default integration tests require a locally running Fluence node with 4310 port open for ws connections. The dependency can be started with docker ```bash -npm test + docker run --rm -e RUST_LOG="info" -p 1210:1210 -p 4310:4310 fluencelabs/fluence:freeze -t 1210 -w 4310 -k gKdiCSUr1TFGFEgu2t8Ch1XEUsrN5A2UfBLjSZvfci9SPR3NvZpACfcpPGC3eY4zma1pk7UvYv5zb1VjvPHwCjj +``` + +To run all tests in interactive mode + +```bash +npm run test +``` + +To run only unit tests + +```bash +npm run test:unit +``` + +To run only integration tests + +```bash +npm run test:unit +``` + +To run all tests + +```bash +npm run test:all ``` ## Contributing diff --git a/src/__test__/connection.ts b/src/__test__/connection.ts new file mode 100644 index 00000000..04bce751 --- /dev/null +++ b/src/__test__/connection.ts @@ -0,0 +1,35 @@ +import { generatePeerId } from '..'; +import { createClient } from '../api'; +import { FluenceClientImpl } from '../internal/FluenceClientImpl'; + +// Uncomment to test on dev nodes +// export const nodes = [ +// { +// multiaddr: '/dns4/dev.fluence.dev/tcp/19003/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb', +// peerId: '12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb', +// }, +// { +// multiaddr: '/dns4/dev.fluence.dev/tcp/19004/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB', +// peerId: '12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB', +// }, +// ]; + +// start docker container to run integration tests locally +// > docker run --rm -e RUST_LOG="info" -p 1210:1210 -p 4310:4310 fluencelabs/fluence:freeze -t 1210 -w 4310 -k gKdiCSUr1TFGFEgu2t8Ch1XEUsrN5A2UfBLjSZvfci9SPR3NvZpACfcpPGC3eY4zma1pk7UvYv5zb1VjvPHwCjj +export const nodes = [ + { + multiaddr: '/ip4/127.0.0.1/tcp/4310/ws/p2p/12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3', + peerId: '12D3KooWKEprYXUXqoV5xSBeyqrWLpQLLH4PXfvVkDJtmcqmh5V3', + }, +]; + +export const createLocalClient = async () => { + const peerId = await generatePeerId(); + const client = new FluenceClientImpl(peerId); + await client.local(); + return client; +}; + +export const createConnectedClient = async (node: string) => { + return (await createClient(node)) as FluenceClientImpl; +}; diff --git a/src/__test__/integration/builtins.spec.ts b/src/__test__/integration/builtins.spec.ts index 5397f5c2..ef0ff75a 100644 --- a/src/__test__/integration/builtins.spec.ts +++ b/src/__test__/integration/builtins.spec.ts @@ -1,30 +1,25 @@ import { addBlueprint, - addProvider, addScript, createService, getBlueprints, getInterfaces, getModules, - getProviders, removeScript, uploadModule, } from '../../internal/builtins'; import { ModuleConfig } from '../../internal/moduleConfig'; -import { createConnectedClient } from '../util'; import { checkConnection } from '../../api'; import log from 'loglevel'; import { generatePeerId } from '../..'; import { FluenceClientImpl } from '../../internal/FluenceClientImpl'; - -const dev2multiaddr = '/dns4/dev.fluence.dev/tcp/19003/wss/p2p/12D3KooWBUJifCTgaxAUrcM9JysqCcS4CS8tiYH5hExbdWCAoNwb'; -const dev3multiaddr = '/dns4/dev.fluence.dev/tcp/19004/wss/p2p/12D3KooWJbJFaZ3k5sNd8DjQgg3aERoKtBAnirEvPV8yp76kEXHB'; - -const dev2peerId = '12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9'; +import { createConnectedClient, nodes } from '../connection'; describe('Builtins usage suite', () => { + jest.setTimeout(10000); + it('get_modules', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); let modulesList = await getModules(client); @@ -32,7 +27,7 @@ describe('Builtins usage suite', () => { }); it('get_interfaces', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); let interfaces = await getInterfaces(client); @@ -40,7 +35,7 @@ describe('Builtins usage suite', () => { }); it('get_blueprints', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); let bpList = await getBlueprints(client); @@ -51,7 +46,7 @@ describe('Builtins usage suite', () => { const peerId = await generatePeerId(); const client = new FluenceClientImpl(peerId); await client.local(); - await client.connect(dev2multiaddr); + await client.connect(nodes[0].multiaddr); let isConnected = await checkConnection(client); @@ -59,7 +54,7 @@ describe('Builtins usage suite', () => { }); it('upload_modules', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); console.log('peerid: ' + client.selfPeerId); @@ -81,7 +76,7 @@ describe('Builtins usage suite', () => { }); it('add_blueprint', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); let bpId = 'some'; @@ -92,7 +87,7 @@ describe('Builtins usage suite', () => { // FIXME:: there is no error on broken blueprint from a node it.skip('create_service', async function () { - const client = await createConnectedClient(dev2multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); let serviceId = await createService(client, 'test_broken_blueprint'); @@ -100,23 +95,8 @@ describe('Builtins usage suite', () => { expect(serviceId).not.toBeUndefined; }); - it('add_provider', async function () { - const client = await createConnectedClient(dev2multiaddr); - - let key = Math.random().toString(36).substring(7); - let buf = Buffer.from(key); - - let r = Math.random().toString(36).substring(7); - await addProvider(client, buf, dev2peerId, r, undefined, 10000); - - let pr = await getProviders(client, buf, undefined, 10000); - console.log(pr); - console.log(r); - expect(r).toEqual(pr[0][0].service_id); - }); - it('add and remove script', async function () { - const client = await createConnectedClient(dev3multiaddr); + const client = await createConnectedClient(nodes[0].multiaddr); console.log('peerid: ' + client.selfPeerId); diff --git a/src/__test__/integration/client.spec.ts b/src/__test__/integration/client.spec.ts index fa3ed001..0c021d5a 100644 --- a/src/__test__/integration/client.spec.ts +++ b/src/__test__/integration/client.spec.ts @@ -1,13 +1,10 @@ import { encode } from 'bs58'; import { generatePeerId, peerIdToSeed, seedToPeerId } from '../../internal/peerIdUtils'; import { FluenceClientImpl } from '../../internal/FluenceClientImpl'; -import { createConnectedClient } from '../util'; import log from 'loglevel'; import { createClient } from '../../api'; import Multiaddr from 'multiaddr'; - -const devNodeAddress = '/dns4/dev.fluence.dev/tcp/19001/wss/p2p/12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9'; -const devNodePeerId = '12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9'; +import { createConnectedClient, nodes } from '../connection'; describe('Typescript usage suite', () => { it('should create private key from seed and back', async function () { @@ -45,7 +42,7 @@ describe('Typescript usage suite', () => { it('address as string', async function () { // arrange - const addr = devNodeAddress; + const addr = nodes[0].multiaddr; // act const client = (await createClient(addr)) as FluenceClientImpl; @@ -57,7 +54,7 @@ describe('Typescript usage suite', () => { it('address as multiaddr', async function () { // arrange - const addr = new Multiaddr(devNodeAddress); + const addr = new Multiaddr(nodes[0].multiaddr); // act const client = (await createClient(addr)) as FluenceClientImpl; @@ -69,10 +66,7 @@ describe('Typescript usage suite', () => { it('address as node', async function () { // arrange - const addr = { - multiaddr: devNodeAddress, - peerId: devNodePeerId, - }; + const addr = nodes[0]; // act const client = (await createClient(addr)) as FluenceClientImpl; @@ -84,7 +78,7 @@ describe('Typescript usage suite', () => { it('peerid as peer id', async function () { // arrange - const addr = devNodeAddress; + const addr = nodes[0].multiaddr; const pid = await generatePeerId(); // act @@ -97,7 +91,7 @@ describe('Typescript usage suite', () => { it('peerid as seed', async function () { // arrange - const addr = devNodeAddress; + const addr = nodes[0].multiaddr; const pid = peerIdToSeed(await generatePeerId()); // act @@ -111,7 +105,7 @@ describe('Typescript usage suite', () => { it('should make a call through the network', async function () { // arrange - const client = await createConnectedClient(devNodeAddress); + const client = await createConnectedClient(nodes[0].multiaddr); client.registerCallback('test', 'test', (args, _) => { log.trace('should make a call through the network, called "test" "test" with args', args); @@ -151,7 +145,7 @@ describe('Typescript usage suite', () => { it('fireAndForget should work', async function () { // arrange - const client = await createConnectedClient(devNodeAddress); + const client = await createConnectedClient(nodes[0].multiaddr); let resMakingPromise = new Promise((resolve) => { client.registerCallback('test', 'reverse_args', (args, _) => { @@ -180,11 +174,11 @@ describe('Typescript usage suite', () => { it('fetch should work', async function () { // arrange - const client = await createConnectedClient(devNodeAddress); + const client = await createConnectedClient(nodes[0].multiaddr); // act let script = ` - (call "${client.relayPeerId}" ("op" "identify") [] result) + (call "${client.relayPeerId}" ("peer" "identify") [] result) `; const data = new Map(); data.set('__relay', client.relayPeerId); @@ -197,8 +191,8 @@ describe('Typescript usage suite', () => { it('two clients should work inside the same time browser', async function () { // arrange - const client1 = await createConnectedClient(devNodeAddress); - const client2 = await createConnectedClient(devNodeAddress); + const client1 = await createConnectedClient(nodes[0].multiaddr); + const client2 = await createConnectedClient(nodes[0].multiaddr); let resMakingPromise = new Promise((resolve) => { client2.registerCallback('test', 'test', (args, _) => { @@ -225,32 +219,4 @@ describe('Typescript usage suite', () => { let res = await resMakingPromise; expect(res).toEqual(['some a', 'some b', 'some c', 'some d']); }); - - it('event registration should work', async function () { - // arrange - const client1 = await createConnectedClient(devNodeAddress); - const client2 = await createConnectedClient(devNodeAddress); - - client2.registerEvent('event_stream', 'test'); - const resMakingPromise = new Promise((resolve) => { - client2.subscribe('event_stream', resolve); - }); - - // act - let script = ` - (call "${client2.selfPeerId}" ("event_stream" "test") [hello]) - `; - - let data: Map = new Map(); - data.set('hello', 'world'); - - await client1.fireAndForget(script, data); - - // assert - let res = await resMakingPromise; - expect(res).toEqual({ - type: 'test', - args: ['world'], - }); - }); }); diff --git a/src/__test__/unit/air.spec.ts b/src/__test__/unit/air.spec.ts index 577fd390..04209a83 100644 --- a/src/__test__/unit/air.spec.ts +++ b/src/__test__/unit/air.spec.ts @@ -1,4 +1,4 @@ -import { createLocalClient } from '../util'; +import { createLocalClient } from '../connection'; describe('== AIR suite', () => { it('check init_peer_id', async function () { @@ -57,7 +57,7 @@ describe('== AIR suite', () => { const script = `(call %init_peer_id% ("" "") [""])`; - await expect(client.sendScript(script, undefined, 1)).rejects.toContain("Particle expired"); + await expect(client.sendScript(script, undefined, 1)).rejects.toContain('Particle expired'); }); it.skip('call broken script by fetch', async function () { diff --git a/src/__test__/util.ts b/src/__test__/util.ts deleted file mode 100644 index 5cdc83cc..00000000 --- a/src/__test__/util.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { generatePeerId } from '..'; -import { createClient } from '../api'; -import { FluenceClientImpl } from '../internal/FluenceClientImpl'; - -export const createLocalClient = async () => { - const peerId = await generatePeerId(); - const client = new FluenceClientImpl(peerId); - await client.local(); - return client; -}; - -export const createConnectedClient = async (node: string) => { - return (await createClient(node)) as FluenceClientImpl; -}; diff --git a/src/internal/ParticleProcessor.ts b/src/internal/ParticleProcessor.ts index 7e909ca3..c5b19630 100644 --- a/src/internal/ParticleProcessor.ts +++ b/src/internal/ParticleProcessor.ts @@ -79,15 +79,15 @@ export class ParticleProcessor { async executeLocalParticle(particle: ParticleDto) { this.strategy?.onLocalParticleRecieved(particle); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const resolveCallback = function () { - resolve() - } + resolve(); + }; const rejectCallback = function (err: any) { - reject(err) - } + reject(err); + }; // we check by callbacks that the script passed through the interpreter without errors - this.handleParticle(particle, resolveCallback, rejectCallback) + this.handleParticle(particle, resolveCallback, rejectCallback); }); } @@ -169,7 +169,9 @@ export class ParticleProcessor { let actualTtl = particle.timestamp + particle.ttl - now; if (actualTtl <= 0) { this.strategy?.onParticleTimeout(particle, now); - if (reject) reject(`Particle expired. Now: ${now}, ttl: ${particle.ttl}, ts: ${particle.timestamp}`) + if (reject) { + reject(`Particle expired. Now: ${now}, ttl: ${particle.ttl}, ts: ${particle.timestamp}`); + } } else { // if there is no subscription yet, previous data is empty let prevData: Uint8Array = Buffer.from([]); @@ -204,14 +206,14 @@ export class ParticleProcessor { if (stepperOutcome.ret_code == 0) { if (resolve) { - resolve() + resolve(); } } else { const error = stepperOutcome.error_message; if (reject) { reject(error); } else { - log.error("Unhandled error: ", error); + log.error('Unhandled error: ', error); } } } @@ -219,7 +221,7 @@ export class ParticleProcessor { if (reject) { reject(e); } else { - log.error("Unhandled error: ", e) + log.error('Unhandled error: ', e); throw e; } } finally { diff --git a/src/internal/builtins.ts b/src/internal/builtins.ts index 7fb70a23..1d5c3afb 100644 --- a/src/internal/builtins.ts +++ b/src/internal/builtins.ts @@ -66,11 +66,11 @@ const requestResponse = async ( * @returns { Array } - list of available modules on the connected relay */ export const getModules = async (client: FluenceClient, ttl?: number): Promise => { - let callbackFn = "getModules" + let callbackFn = 'getModules'; const particle = new Particle( ` (seq - (call __relay ("dist" "get_modules") [] result) + (call __relay ("dist" "list_modules") [] result) (call myPeerId ("_callback" "${callbackFn}") [result]) ) `, @@ -78,34 +78,46 @@ export const getModules = async (client: FluenceClient, ttl?: number): Promise } - list of available modules on the connected relay + * @returns { Array } - list of available interfaces on the connected relay */ export const getInterfaces = async (client: FluenceClient, ttl?: number): Promise => { - let callbackFn = "getInterfaces" + let callbackFn = 'getInterfaces'; const particle = new Particle( ` - (seq - (call __relay ("srv" "get_interfaces") [] result) - (call myPeerId ("_callback" "${callbackFn}") [result]) - ) - `, + (seq + (seq + (seq + (call relay ("srv" "list") [] services) + (call relay ("op" "identity") [] interfaces[]) + ) + (fold services s + (seq + (call relay ("srv" "get_interface") [s.$.id!] interfaces[]) + (next s) + ) + ) + ) + (call myPeerId ("_callback" "${callbackFn}") [interfaces]) + ) + `, { - __relay: client.relayPeerId, + relay: client.relayPeerId, myPeerId: client.selfPeerId, }, - ttl + ttl, ); - return sendParticleAsFetch(client, particle, callbackFn); + const [res] = await sendParticleAsFetch<[string[]]>(client, particle, callbackFn); + return res; }; /** @@ -149,7 +161,7 @@ export const uploadModule = async ( ) `; - return sendParticleAsFetch(client, new Particle(script, data, ttl), 'getModules', "_callback"); + return sendParticleAsFetch(client, new Particle(script, data, ttl), 'getModules', '_callback'); }; /** @@ -230,7 +242,7 @@ export const createService = async ( */ export const getBlueprints = async (client: FluenceClient, nodeId?: string, ttl?: number): Promise => { let returnValue = 'blueprints'; - let call = (nodeId: string) => `(call "${nodeId}" ("dist" "get_blueprints") [] ${returnValue})`; + let call = (nodeId: string) => `(call "${nodeId}" ("dist" "list_blueprints") [] ${returnValue})`; return requestResponse( client, @@ -244,53 +256,6 @@ export const getBlueprints = async (client: FluenceClient, nodeId?: string, ttl? ); }; -/** - * Add a provider to DHT network to neighborhood around a key. @deprecated prefer using raw Particles instead - */ -export const addProvider = async ( - client: FluenceClient, - key: Buffer, - providerPeer: string, - providerServiceId?: string, - nodeId?: string, - ttl?: number, -): Promise => { - let call = (nodeId: string) => `(call "${nodeId}" ("dht" "add_provider") [key provider] void[])`; - - key = bs58.encode(key) as any; - - let provider = { - peer: providerPeer, - service_id: providerServiceId, - }; - - let data = new Map(); - data.set('key', key); - data.set('provider', provider); - - return requestResponse(client, 'addProvider', call, '', data, () => {}, nodeId, ttl); -}; - -/** - * Get a provider from DHT network from neighborhood around a key. @deprecated prefer using raw Particles instead - * @param { FluenceClient } client - The Fluence Client instance. - * @param {[buffer]} key - get provider by this key - * @param {[string]} nodeId - Optional node peer id to get providers from - * @param {[number]} ttl - Optional ttl for the particle which does the job - * @returns { Array } - List of providers - */ -export const getProviders = async (client: FluenceClient, key: Buffer, nodeId?: string, ttl?: number): Promise => { - key = bs58.encode(key) as any; - - let returnValue = 'providers'; - let call = (nodeId: string) => `(call "${nodeId}" ("dht" "get_providers") [key] providers[])`; - - let data = new Map(); - data.set('key', key); - - return requestResponse(client, 'getProviders', call, returnValue, data, (args) => args[0], nodeId, ttl); -}; - /** * Get relays neighborhood. @deprecated prefer using raw Particles instead * @param { FluenceClient } client - The Fluence Client instance. @@ -317,15 +282,21 @@ export const neighborhood = async (client: FluenceClient, nodeId?: string, ttl?: * @param {[number]} ttl - Optional ttl for the particle which does the job * @returns {[string]} - script id */ -export const addScript = async (client: FluenceClient, script: string, period?: number, nodeId?: string, ttl?: number): Promise => { +export const addScript = async ( + client: FluenceClient, + script: string, + period?: number, + nodeId?: string, + ttl?: number, +): Promise => { let returnValue = 'id'; - let periodV = "" - if (period) periodV = period.toString() + let periodV = ''; + if (period) periodV = period.toString(); let call = (nodeId: string) => `(call "${nodeId}" ("script" "add") [script ${periodV}] ${returnValue})`; let data = new Map(); data.set('script', script); - if (period) data.set('period', period) + if (period) data.set('period', period); return requestResponse(client, 'addScript', call, returnValue, data, (args) => args[0] as string, nodeId, ttl); };