JS SDK: fix host functions in mocha tests (#1003)

This commit is contained in:
folex 2020-12-19 23:59:29 +03:00 committed by GitHub
parent f0b4c7a718
commit b79f0920d4
9 changed files with 168 additions and 53 deletions

6
package-lock.json generated
View File

@ -25,9 +25,9 @@
}
},
"@fluencelabs/aquamarine-stepper": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@fluencelabs/aquamarine-stepper/-/aquamarine-stepper-0.0.16.tgz",
"integrity": "sha512-yrRMH2ysrxkhOGUe599urPopx6bon44qHppyvE6RKdrE1qVgxYKONWU+BNM+ouzIZ3UW5hoT0gQZ7lmI3HS30g=="
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/@fluencelabs/aquamarine-stepper/-/aquamarine-stepper-0.0.21.tgz",
"integrity": "sha512-bw0tdC5+fihzw+BxA02TrNIzMp2reuV21RqPMlDUExh2tbSzHYKBXKOxGsIY10j3QYWpHQZK9N341VnA3nw6Sw=="
},
"@sinonjs/commons": {
"version": "1.7.2",

View File

@ -5,8 +5,8 @@
"main": "./dist/fluence.js",
"typings": "./dist/fluence.d.ts",
"scripts": {
"test": "mocha -r esm -r ts-node/register src/**/*.spec.ts",
"test-ts": "ts-mocha -r esm -p tsconfig.json src/**/*.spec.ts",
"test": "mocha --timeout 10000 -r esm -r ts-node/register src/**/*.spec.ts",
"test-ts": "ts-mocha --timeout 10000 -r esm -p tsconfig.json src/**/*.spec.ts",
"package:build": "NODE_ENV=production webpack && npm run package",
"package": "tsc && rsync -r src/aqua/*.js dist/aqua",
"start": "webpack-dev-server -p",
@ -16,7 +16,7 @@
"author": "Fluence Labs",
"license": "Apache-2.0",
"dependencies": {
"@fluencelabs/aquamarine-stepper": "0.0.16",
"@fluencelabs/aquamarine-stepper": "0.0.21",
"async": "3.2.0",
"base64-js": "1.3.1",
"bs58": "4.0.1",

7
src/aqua/index.d.ts vendored
View File

@ -11,7 +11,8 @@
*/
export function invoke(wasm: any, init_user_id: string, aqua: string, prev_data: string, data: string, log_level: string): string;
export function ast(wasm: any, script: string): string;
export function return_current_peer_id(wasm: any, peerId: string, arg0: any): void;
export function return_call_service_result(wasm: any, ret: string, arg0: any): void;
export function getStringFromWasm0(wasm: any, arg1: any, arg2: any): string
export function getInt32Memory0(wasm: any): number[]
export function passStringToWasm0(wasm: any, arg: any, malloc: any, realloc: any): number
export let WASM_VECTOR_LEN: number;
export function free(wasm: any, ptr: any, len: any): void

View File

@ -36,7 +36,6 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
});
export function passStringToWasm0(wasm, arg, malloc, realloc) {
if (realloc === undefined) {
const buf = cachedTextEncoder.encode(arg);
const ptr = malloc(buf.length);
@ -57,8 +56,8 @@ export function passStringToWasm0(wasm, arg, malloc, realloc) {
if (code > 0x7F) break;
mem[ptr + offset] = code;
}
if (offset !== len) {
if (offset !== 0) {
arg = arg.slice(offset);
}
@ -69,7 +68,9 @@ export function passStringToWasm0(wasm, arg, malloc, realloc) {
offset += ret.written;
}
WASM_VECTOR_LEN = offset;
return ptr;
}
@ -132,3 +133,22 @@ export function ast(wasm, script) {
wasm.__wbindgen_free(r0, r1);
}
}
export function return_current_peer_id(wasm, peerId, arg0) {
var ptr0 = passStringToWasm0(wasm, peerId, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0(wasm)[arg0 / 4 + 1] = len0;
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr0;
}
export function return_call_service_result(wasm, ret, arg0) {
var ptr1 = passStringToWasm0(wasm, ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len1 = WASM_VECTOR_LEN;
getInt32Memory0(wasm)[arg0 / 4 + 1] = len1;
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr1;
}
export function free(wasm, ptr, len) {
wasm.__wbindgen_free(ptr, len);
}

View File

@ -21,12 +21,7 @@ import * as PeerId from "peer-id";
import Multiaddr from "multiaddr"
import {FluenceConnection} from "./fluenceConnection";
import {Subscriptions} from "./subscriptions";
import {
enqueueParticle,
getCurrentParticleId,
popParticle,
setCurrentParticleId
} from "./globalState";
import {enqueueParticle, getCurrentParticleId, popParticle, setCurrentParticleId} from "./globalState";
import {instantiateInterpreter, InterpreterInvoke} from "./stepper";
import log from "loglevel";
import {waitService} from "./helpers/waitService";
@ -34,6 +29,8 @@ import {ModuleConfig} from "./moduleConfig";
const bs58 = require('bs58')
const INFO_LOG_LEVEL = 2
export class FluenceClient {
readonly selfPeerId: PeerId;
readonly selfPeerIdStr: string;
@ -84,12 +81,21 @@ export class FluenceClient {
let stepperOutcomeStr = this.interpreter(particle.init_peer_id, particle.script, JSON.stringify(prevData), JSON.stringify(particle.data))
let stepperOutcome: StepperOutcome = JSON.parse(stepperOutcomeStr);
log.info("inner interpreter outcome:");
log.info(stepperOutcome);
if (log.getLevel() <= INFO_LOG_LEVEL) {
log.info("inner interpreter outcome:");
let so = {...stepperOutcome}
try {
so.data = JSON.parse(Buffer.from(so.data).toString("utf8"));
log.info(so);
} catch (e) {
log.info("cannot parse StepperOutcome data as JSON: ", e);
}
}
// update data after aquamarine execution
let newParticle: Particle = {...particle};
newParticle.data = JSON.parse(stepperOutcome.call_path)
newParticle.data = stepperOutcome.data
this.subscriptions.update(newParticle)
// do nothing if there is no `next_peer_pks` or if client isn't connected to the network

View File

@ -16,7 +16,7 @@
import {toByteArray} from "base64-js";
import * as aqua from "./aqua"
import {getInt32Memory0, getStringFromWasm0, passStringToWasm0, WASM_VECTOR_LEN} from "./aqua"
import {return_current_peer_id, return_call_service_result, getStringFromWasm0, free} from "./aqua"
import {service} from "./service";
import PeerId from "peer-id";
@ -25,13 +25,13 @@ import {wasmBs64} from "@fluencelabs/aquamarine-stepper";
import Instance = WebAssembly.Instance;
import Exports = WebAssembly.Exports;
import ExportValue = WebAssembly.ExportValue;
import Imports = WebAssembly.Imports;
export type InterpreterInvoke = (init_user_id: string, script: string, prev_data: string, data: string) => string
type ImportObject = {
"./aquamarine_client_bg.js": {
__wbg_callserviceimpl_7d3cf77a2722659e: (arg0: any, arg1: any, arg2: any, arg3: any, arg4: any, arg5: any, arg6: any) => void;
__wbg_getcurrentpeeridimpl_154ce1848a306ff5: (arg0: any) => void
__wbg_getcurrentpeeridimpl_154ce1848a306ff5: (arg0: any) => void;
__wbindgen_throw: (arg: any) => void;
};
host: LogImport
};
@ -89,7 +89,7 @@ function log_import(cfg: HostImportsConfig): LogImport {
log_utf8_string: (level: any, target: any, offset: any, size: any) => {
let wasm = cfg.exports;
try {
let str = getStringFromWasm0(wasm, offset, size)
let str = getStringFromWasm0(wasm, offset, size);
switch (level) {
case 1:
@ -106,7 +106,7 @@ function log_import(cfg: HostImportsConfig): LogImport {
break;
case 5:
// we don't want a trace in trace logs
log.debug(str)
log.debug(str)
break;
}
} finally {
@ -127,25 +127,22 @@ function newImportObject(cfg: HostImportsConfig, peerId: PeerId): ImportObject {
let serviceId = getStringFromWasm0(wasm, arg1, arg2)
let fnName = getStringFromWasm0(wasm, arg3, arg4)
let args = getStringFromWasm0(wasm, arg5, arg6);
var ret = service(serviceId, fnName, args);
let retStr = JSON.stringify(ret)
var ptr0 = passStringToWasm0(wasm, retStr, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0(wasm)[arg0 / 4 + 1] = len0;
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr0;
let serviceResult = service(serviceId, fnName, args);
let resultStr = JSON.stringify(serviceResult)
return_call_service_result(wasm, resultStr, arg0);
} finally {
call_export(wasm.__wbindgen_free, arg1, arg2);
call_export(wasm.__wbindgen_free, arg3, arg4);
call_export(wasm.__wbindgen_free, arg5, arg6);
free(wasm, arg1, arg2)
free(wasm, arg3, arg4)
free(wasm, arg5, arg6)
}
},
__wbg_getcurrentpeeridimpl_154ce1848a306ff5: (arg0: any) => {
let peerIdStr = peerId.toB58String();
let wasm = cfg.exports;
var ret = peerId.toB58String();
var ptr0 = passStringToWasm0(wasm, ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
var len0 = WASM_VECTOR_LEN;
getInt32Memory0(wasm)[arg0 / 4 + 1] = len0;
getInt32Memory0(wasm)[arg0 / 4 + 0] = ptr0;
return_current_peer_id(wasm, peerIdStr, arg0);
},
__wbindgen_throw: (arg: any) => {
console.log(`wbindgen throw: ${JSON.stringify(arg)}`);
}
},
host: log_import(cfg)
@ -157,7 +154,8 @@ function newLogImport(cfg: HostImportsConfig): ImportObject {
host: log_import(cfg),
"./aquamarine_client_bg.js": {
__wbg_callserviceimpl_7d3cf77a2722659e: _ => {},
__wbg_getcurrentpeeridimpl_154ce1848a306ff5: _ => {}
__wbg_getcurrentpeeridimpl_154ce1848a306ff5: _ => {},
__wbindgen_throw: _ => {}
},
};
}
@ -165,7 +163,9 @@ function newLogImport(cfg: HostImportsConfig): ImportObject {
/// Instantiates AIR interpreter, and returns its `invoke` function as closure
/// NOTE: an interpreter is also called a stepper from time to time
export async function instantiateInterpreter(peerId: PeerId): Promise<InterpreterInvoke> {
let cfg = new HostImportsConfig((cfg) => newImportObject(cfg, peerId))
let cfg = new HostImportsConfig((cfg) => {
return newImportObject(cfg, peerId);
})
let instance = await interpreterInstance(cfg);
return (init_user_id: string, script: string, prev_data: string, data: string) => {

View File

@ -16,6 +16,6 @@
export interface StepperOutcome {
ret_code: number,
call_path: string,
data: number[],
next_peer_pks: string[]
}

View File

@ -1,27 +1,115 @@
import 'mocha';
import Fluence from "../fluence";
import {build} from "../particle";
import { ServiceMultiple } from "../service";
import { registerService } from "../globalState";
import {ServiceMultiple} from "../service";
import {registerService} from "../globalState";
import {expect} from "chai";
describe("AIR", () => {
it("call local function", async function () {
let service = new ServiceMultiple("console");
registerService(service);
service.registerFunction('log', (args: any[]) => {
console.log(`log: ${args}`);
function registerPromiseService<T>(serviceId: string, fnName: string, f: (args: any[]) => T): Promise<T> {
let service = new ServiceMultiple(serviceId);
registerService(service);
return {}
return new Promise((resolve, reject) => {
service.registerFunction(fnName, (args: any[]) => {
resolve(f(args))
return {result: f(args)}
})
})
}
describe("== AIR suite", () => {
it("check init_peer_id", async function () {
let serviceId = "init_peer"
let fnName = "id"
let checkPromise = registerPromiseService(serviceId, fnName, (args) => args[0])
let client = await Fluence.local();
let script = `(call %init_peer_id% ("console" "log") ["hello"])`
let script = `(call %init_peer_id% ("${serviceId}" "${fnName}") [%init_peer_id%])`
let particle = await build(client.selfPeerId, script, new Map())
await client.executeParticle(particle);
expect(await checkPromise).to.be.equal(client.selfPeerIdStr)
})
it("call local function", async function () {
let serviceId = "console"
let fnName = "log"
let checkPromise = registerPromiseService(serviceId, fnName, (args) => args[0])
let client = await Fluence.local();
let arg = "hello"
let script = `(call %init_peer_id% ("${serviceId}" "${fnName}") ["${arg}"])`
// Wrap script into particle, so it can be executed by local WASM runtime
let particle = await build(client.selfPeerId, script, new Map())
await client.executeParticle(particle);
expect(await checkPromise).to.be.equal(arg)
})
it("check particle arguments", async function () {
let serviceId = "check"
let fnName = "args"
let checkPromise = registerPromiseService(serviceId, fnName, (args) => args[0])
let client = await Fluence.local();
let arg = "arg1"
let value = "hello"
let script = `(call %init_peer_id% ("${serviceId}" "${fnName}") [${arg}])`
let data = new Map()
data.set("arg1", value)
let particle = await build(client.selfPeerId, script, data)
await client.executeParticle(particle);
expect(await checkPromise).to.be.equal(value)
})
it("check chain of services work properly", async function () {
this.timeout(5000);
let serviceId1 = "check1"
let fnName1 = "fn1"
let checkPromise1 = registerPromiseService(serviceId1, fnName1, (args) => args[0])
let serviceId2 = "check2"
let fnName2 = "fn2"
let checkPromise2 = registerPromiseService(serviceId2, fnName2, (args) => args[0])
let serviceId3 = "check3"
let fnName3 = "fn3"
let checkPromise3 = registerPromiseService(serviceId3, fnName3, (args) => args)
let client = await Fluence.local();
let arg1 = "arg1"
let arg2 = "arg2"
// language=Clojure
let script = `(seq
(seq
(call %init_peer_id% ("${serviceId1}" "${fnName1}") ["${arg1}"] result1)
(call %init_peer_id% ("${serviceId2}" "${fnName2}") ["${arg2}"] result2))
(call %init_peer_id% ("${serviceId3}" "${fnName3}") [result1 result2]))
`
let particle = await build(client.selfPeerId, script, new Map())
await client.executeParticle(particle);
expect(await checkPromise1).to.be.equal(arg1)
expect(await checkPromise2).to.be.equal(arg2)
expect(await checkPromise3).to.be.deep.equal([{result: arg1}, {result: arg2}])
})
})

View File

@ -1,7 +1,7 @@
import 'mocha';
import Fluence from "../fluence";
describe("AIR AST parsing suite", () => {
describe("== AST parsing suite", () => {
it("parse simple script and return ast", async function () {
let ast = await Fluence.parseAIR(`
(call node ("service" "function") [1 2 3 arg] output)