diff --git a/cmd/aries-js-worker/main.go b/cmd/aries-js-worker/main.go index f79f364bd..f965751bc 100644 --- a/cmd/aries-js-worker/main.go +++ b/cmd/aries-js-worker/main.go @@ -317,7 +317,7 @@ func newErrResult(id, msg string) *result { return &result{ ID: id, IsErr: true, - ErrMsg: msg, + ErrMsg: "aries wasm: " + msg, } } diff --git a/cmd/aries-js-worker/package-lock.json b/cmd/aries-js-worker/package-lock.json index 6f5b14ace..24a0f9699 100644 --- a/cmd/aries-js-worker/package-lock.json +++ b/cmd/aries-js-worker/package-lock.json @@ -446,6 +446,14 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1568,6 +1576,24 @@ "readable-stream": "^2.3.6" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2922,8 +2948,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { "version": "2.14.0", diff --git a/cmd/aries-js-worker/src/aries.js b/cmd/aries-js-worker/src/aries.js index 88c53fe76..0cf4d23b5 100644 --- a/cmd/aries-js-worker/src/aries.js +++ b/cmd/aries-js-worker/src/aries.js @@ -85,9 +85,7 @@ function newMsg(pkg, fn, payload) { */ export const Framework = class { constructor(opts) { - return (async () => { - return await Aries(opts); - })(); + return Aries(opts) } }; @@ -183,11 +181,16 @@ const Aries = function(opts) { createInvitation: async function (text) { return invoke(aw, pending, this.pkgname, "CreateInvitation", text, "timeout while creating invitation") }, - receiveInvitation: async function (text) { - return invoke(aw, pending, this.pkgname, "ReceiveInvitation", text, "timeout while receiving invitation") + receiveInvitation: async function (invitation) { + return invoke(aw, pending, this.pkgname, "ReceiveInvitation", invitation, "timeout while receiving invitation") }, - acceptInvitation: async function (text) { - return invoke(aw, pending, this.pkgname, "AcceptInvitation", text, "timeout while accepting invitation") + acceptInvitation: async function (connectionID, publicDID="") { + return new Promise((resolve, reject) => { + invoke(aw, pending, this.pkgname, "AcceptInvitation", {id: connectionID, public: publicDID}, "timeout while accepting invitation").then( + resp => resolve(resp), + err => reject(new Error("failed to accept invitation: " + err.message)) + ) + }) }, acceptExchangeRequest: async function (text) { return invoke(aw, pending, this.pkgname, "AcceptExchangeRequest", text, "timeout while accepting exchange request") @@ -198,8 +201,8 @@ const Aries = function(opts) { removeConnection: async function (text) { return invoke(aw, pending, this.pkgname, "RemoveConnection", text, "timeout while removing invitation") }, - queryConnectionByID: async function (text) { - return invoke(aw, pending, this.pkgname, "QueryConnectionByID", text, "timeout while querying connection by ID") + queryConnectionByID: async function (connectionID) { + return invoke(aw, pending, this.pkgname, "QueryConnectionByID", {id: connectionID}, "timeout while querying connection by ID") }, queryConnections: async function (text) { return invoke(aw, pending, this.pkgname, "QueryConnections", text, "timeout while querying connections") @@ -279,10 +282,9 @@ const Aries = function(opts) { notifications.set("asset-ready", async (result) => { clearTimeout(timer) invoke(aw, pending, "aries", "Start", opts, "timeout while starting aries").then( - resp => resolve(), + resp => resolve(instance), err => reject(new Error(err.message)) ) - resolve(instance) }) }) } diff --git a/pkg/didcomm/protocol/didexchange/service.go b/pkg/didcomm/protocol/didexchange/service.go index ea4f789a1..98c8ca81b 100644 --- a/pkg/didcomm/protocol/didexchange/service.go +++ b/pkg/didcomm/protocol/didexchange/service.go @@ -418,7 +418,7 @@ func (s *Service) AcceptExchangeRequest(connectionID, publicDID, label string) e func (s *Service) accept(connectionID, publicDID, label, stateID, errMsg string) error { msg, err := s.getEventTransientData(connectionID) if err != nil { - return fmt.Errorf("%s : %w", errMsg, err) + return fmt.Errorf("failed to accept invitation for connectionID=%s : %s : %w", connectionID, errMsg, err) } connRecord, err := s.connectionStore.GetConnectionRecord(connectionID) diff --git a/scripts/check_js_integration.sh b/scripts/check_js_integration.sh index 07df98589..be4215041 100755 --- a/scripts/check_js_integration.sh +++ b/scripts/check_js_integration.sh @@ -8,14 +8,39 @@ set -e ROOT=`pwd` +echo "" echo "Running aries-js-worker integration tests..." +echo "" echo "----> packing aries-js-worker" +echo "" cd $ROOT/cmd/aries-js-worker npm install npm link +echo "" echo "----> setting up aries-js-worker tests" +echo "" cd $ROOT/test/aries-js-worker npm install npm link @hyperledger/aries-framework-go +echo "" +echo "----> starting fixtures" +echo "" +cd $ROOT/test/aries-js-worker/fixtures +docker-compose up -d +echo "" echo "----> executing aries-js-worker tests" -npm test +echo "" +cd $ROOT/test/aries-js-worker +# capture exit code if it fails +npm test || code=$? +if [ -z ${code+x} ]; then + # set exit code because it did not fail + code=0 +fi +echo "" +echo "----> stopping fixtures" +echo "" +cd $ROOT/test/aries-js-worker/fixtures +docker-compose stop +cd $ROOT +exit $code diff --git a/test/aries-js-worker/.gitignore b/test/aries-js-worker/.gitignore index 94732d8ef..5ecc3bc80 100644 --- a/test/aries-js-worker/.gitignore +++ b/test/aries-js-worker/.gitignore @@ -6,3 +6,4 @@ node_modules/ public/ +environment.js diff --git a/test/aries-js-worker/fixtures/.env b/test/aries-js-worker/fixtures/.env new file mode 100644 index 000000000..8cbc85760 --- /dev/null +++ b/test/aries-js-worker/fixtures/.env @@ -0,0 +1,22 @@ +# +# Copyright SecureKey Technologies Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Agent configurations +E2E_AGENT_REST_IMAGE=aries-framework-go/agent-rest +E2E_AGENT_REST_IMAGE_TAG=latest + +# Transport Schemes +E2E_HTTP_SCHEME=http +E2E_WS_SCHEME=ws + +# Router configurations +E2E_ROUTER_HOST=0.0.0.0 +E2E_ROUTER_HTTP_INBOUND_PORT=10091 +E2E_ROUTER_WS_INBOUND_PORT=10092 +E2E_ROUTER_API_PORT=10093 +E2E_ROUTER_DB_PATH=/tmp/db/aries +E2E_ROUTER_WEBHOOK_PORT=10094 +E2E_ROUTER_AUTOACCEPT=true diff --git a/test/aries-js-worker/fixtures/docker-compose.yml b/test/aries-js-worker/fixtures/docker-compose.yml new file mode 100644 index 000000000..3f1ac341e --- /dev/null +++ b/test/aries-js-worker/fixtures/docker-compose.yml @@ -0,0 +1,26 @@ +# +# Copyright SecureKey Technologies Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +version: '2' + +services: + + carl.router.agent.example.com: + container_name: aries-js-worker-router.com + image: ${E2E_AGENT_REST_IMAGE}:${E2E_AGENT_REST_IMAGE_TAG} + environment: + - ARIESD_API_HOST=${E2E_ROUTER_HOST}:${E2E_ROUTER_API_PORT} + - ARIESD_INBOUND_HOST=${E2E_HTTP_SCHEME}@${E2E_ROUTER_HOST}:${E2E_ROUTER_HTTP_INBOUND_PORT},${E2E_WS_SCHEME}@${E2E_ROUTER_HOST}:${E2E_ROUTER_WS_INBOUND_PORT} + - ARIESD_INBOUND_HOST_EXTERNAL=${E2E_HTTP_SCHEME}@http://localhost:${E2E_ROUTER_HTTP_INBOUND_PORT},${E2E_WS_SCHEME}@ws://localhost:${E2E_ROUTER_WS_INBOUND_PORT} + - ARIESD_DB_PATH=${E2E_ROUTER_DB_PATH} + - ARIESD_DEFAULT_LABEL=carl-router-agent + - ARIESD_OUTBOUND_TRANSPORT=${E2E_HTTP_SCHEME},${E2E_WS_SCHEME} + - ARIESD_AUTO_ACCEPT=${E2E_ROUTER_AUTOACCEPT} +# - ARIESD_LOG_LEVEL=DEBUG + ports: + - ${E2E_ROUTER_HTTP_INBOUND_PORT}:${E2E_ROUTER_HTTP_INBOUND_PORT} + - ${E2E_ROUTER_WS_INBOUND_PORT}:${E2E_ROUTER_WS_INBOUND_PORT} + - ${E2E_ROUTER_API_PORT}:${E2E_ROUTER_API_PORT} + command: start diff --git a/test/aries-js-worker/karma.conf.js b/test/aries-js-worker/karma.conf.js index 61ac74209..46298325d 100644 --- a/test/aries-js-worker/karma.conf.js +++ b/test/aries-js-worker/karma.conf.js @@ -4,17 +4,44 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ +const fs = require("fs") + process.env.CHROME_BIN = require('puppeteer').executablePath() +const ENV_CONFIG = "fixtures/.env"; +const ENV_CONFIG_OUT = "test/environment.js"; + +(() => { + require("dotenv").config({path: ENV_CONFIG}) + const util = require("util") + const config = {} + for (const attr in process.env) { + if (attr.startsWith("E2E_")) { + config[attr.replace("E2E_", "")] = process.env[attr] + } + } + fs.writeFileSync(ENV_CONFIG_OUT, "export const environment = " + util.inspect(config)) +})() module.exports = function(config) { config.set({ frameworks: ["mocha", "chai"], - browsers: ["ChromeHeadless"], + browsers: ["ChromeHeadless_cors"], singleRun: true, + captureConsole: true, files: [ {pattern: "test/**/*.js", type: "module"}, {pattern: "public/aries-framework-go/assets/*", included: false}, - {pattern: "node_modules/@hyperledger/aries-framework-go/dist/web/*", type: "module"} - ] + {pattern: "node_modules/@hyperledger/aries-framework-go/dist/web/*", type: "module"}, + "node_modules/axios/dist/axios.min.js", + {pattern: "test/common.js", included: false}, + {pattern: "test/environment.js", included: false}, + ], + reporters: ["spec"], + customLaunchers: { + ChromeHeadless_cors: { + base: "ChromeHeadless", + flags: ["--disable-web-security"] + } + } }) } \ No newline at end of file diff --git a/test/aries-js-worker/package-lock.json b/test/aries-js-worker/package-lock.json index 98410e687..2a9d8b55e 100644 --- a/test/aries-js-worker/package-lock.json +++ b/test/aries-js-worker/package-lock.json @@ -99,6 +99,41 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -532,6 +567,12 @@ "void-elements": "^2.0.0" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1219,6 +1260,15 @@ } } }, + "karma-spec-reporter": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz", + "integrity": "sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo=", + "dev": true, + "requires": { + "colors": "^1.1.2" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", diff --git a/test/aries-js-worker/package.json b/test/aries-js-worker/package.json index 08adfd8ed..a8171d0d6 100644 --- a/test/aries-js-worker/package.json +++ b/test/aries-js-worker/package.json @@ -10,13 +10,16 @@ "author": "hyperledger/aries", "license": "Apache-2.0", "devDependencies": { + "axios": "0.19.2", "chai": "4.2.0", - "karma": "^4.4.1", - "karma-chai": "^0.1.0", - "karma-chrome-launcher": "^3.1.0", - "karma-mocha": "^1.3.0", + "dotenv": "8.2.0", + "karma": "4.4.1", + "karma-chai": "0.1.0", + "karma-chrome-launcher": "3.1.0", + "karma-mocha": "1.3.0", + "karma-spec-reporter": "0.0.32", "mocha": "7.0.1", - "puppeteer": "^2.1.1" + "puppeteer": "2.1.1" }, "dependencies": {} } diff --git a/test/aries-js-worker/test/common.js b/test/aries-js-worker/test/common.js new file mode 100644 index 000000000..e14779ac1 --- /dev/null +++ b/test/aries-js-worker/test/common.js @@ -0,0 +1,48 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +export async function newAries() { + return new Aries.Framework({ + assetsPath: "/base/public/aries-framework-go/assets", + "agent-default-label": "dem-js-agent", + "http-resolver-url": [], + "auto-accept": true, + "outbound-transport": ["ws", "http"], + "transport-return-route": "all", + "log-level": "debug" + }) +} + +export async function healthCheck(url, timeout, msgTimeout) { + if (url.startsWith("http")) { + return testHttpUrl(url, timeout, msgTimeout) + } else if (url.startsWith("ws")) { + return testWsUrl(url, timeout, msgTimeout) + } else { + throw new Error(`unsupported protocol for url: ${url}`) + } +} + +function testHttpUrl(url, timeout, msgTimeout) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error(msgTimeout)), timeout) + // TODO HTTP GET for the HTTP inbound transport endpoint (eg. http://0.0.0.0:10091) returns 405. Axios fails, fetch() doesn't. + // Golang's http.Get() does not fail for non 2xx codes. + fetch(url).then( + resp => {clearTimeout(timer); resolve(resp)}, + err => {clearTimeout(timer); console.log(err); reject(new Error(`failed to fetch url=${url}: ${err.message}`))} + ) + }) +} + +function testWsUrl(url, timeout, msgTimeout) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error(msgTimeout)), timeout) + const ws = new WebSocket(url) + ws.onopen = () => {clearTimeout(timer); resolve()} + ws.onerror = err => {clearTimeout(timer); reject(new Error(err.message))} + }) +} diff --git a/test/aries-js-worker/test/didexchange/edgeagent_router.js b/test/aries-js-worker/test/didexchange/edgeagent_router.js new file mode 100644 index 000000000..24fa676aa --- /dev/null +++ b/test/aries-js-worker/test/didexchange/edgeagent_router.js @@ -0,0 +1,85 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +import { newAries, healthCheck } from "../common.js" +import { environment } from "../environment.js" + +const routerHttpUrl = `${environment.HTTP_SCHEME}://${environment.ROUTER_HOST}:${environment.ROUTER_HTTP_INBOUND_PORT}` +const routerWsUrl = `${environment.WS_SCHEME}://${environment.ROUTER_HOST}:${environment.ROUTER_WS_INBOUND_PORT}` +const routerControllerApiUrl = `${environment.HTTP_SCHEME}://${environment.ROUTER_HOST}:${environment.ROUTER_API_PORT}` + +const routerConnPath = "/connections" +const routerCreateInvitationPath = `${routerControllerApiUrl}${routerConnPath}/create-invitation` + +describe("DID-Exchange between an Edge Agent and a router", function() { + var aries + var invitation + + before(async () => { + return new Promise((resolve, reject) => { + newAries().then( + a => {aries = a; resolve()}, + err => reject(new Error(err.message)) + ) + }) + }) + + after(() => { + aries.destroy() + }) + + it(`Router is running on "${routerHttpUrl},${routerWsUrl}" with controller "${routerControllerApiUrl}"`, async function() { + await healthCheck(routerHttpUrl, 5000, "healthCheck timeout!") + await healthCheck(routerWsUrl, 5000, "healthCheck timeout!") + await healthCheck(routerControllerApiUrl, 5000, "healthCheck timeout!") + }) + + it("Edge Agent receives an invitation from the router via the controller API", async function() { + const response = await axios.post(routerCreateInvitationPath) + invitation = response.data.invitation + assert.isObject(invitation) + assert.property(invitation, "serviceEndpoint") + assert.property(invitation, "recipientKeys") + assert.property(invitation, "@id") + assert.property(invitation, "label") + assert.property(invitation, "@type") + assert.equal(invitation["@type"], "https://didcomm.org/didexchange/1.0/invitation") + }) + + it("Edge Agent accepts the invitation from the router", function(done) { + aries.startNotifier(notice => { + try { + assert.isFalse(notice.isErr) + assert.property(notice, "payload") + const connection = notice.payload + assert.property(connection, "connection_id") + aries.didexchange.acceptInvitation(connection.connection_id) + } catch (err) { + done(err) + } + done() + }, ["connections"]) + aries.didexchange.receiveInvitation(invitation) + }) + + it("Edge Agent validates that the connection's state is 'completed'", function(done) { + aries.startNotifier(notice => { + try { + assert.isObject(notice) + assert.property(notice, "isErr") + assert.isFalse(notice.isErr) + assert.property(notice, "payload") + assert.property(notice.payload, "state") + } catch (err) { + done(err) + } + if (notice.payload.state === "completed") { + done() + } + }, ["connections"]) + }) +}) + diff --git a/test/aries-js-worker/test/test.js b/test/aries-js-worker/test/test.js deleted file mode 100644 index 819162043..000000000 --- a/test/aries-js-worker/test/test.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright SecureKey Technologies Inc. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -const assert = chai.assert - -async function loadAries() { - return new Aries.Framework({ - assetsPath: "/base/public/aries-framework-go/assets", - "agent-default-label": "dem-js-agent", - "http-resolver-url": "", - "auto-accept": true, - "outbound-transport": ["ws", "http"], - "transport-return-route": "all", - "log-level": "debug" - }) -} - -describe("_test", function() { - var aries - before(async () => { - return new Promise((resolve, reject) => { - loadAries().then( - a => {aries = a; resolve()}, - err => reject(new Error(err.message)) - ) - }) - }) - - after(() => { - aries.destroy() - }) - - describe("#_echo()", function() { - it("should echo input", async function() { - const expected = "sup" - const result = await aries._test._echo(expected) - assert.isObject(result) - assert.property(result, "echo") - assert.equal(result.echo, expected) - }) - }) -}) -