This is an automated email from the ASF dual-hosted git repository. alexkli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openwhisk-wskdebug.git
commit 8eebc66262db5fed8c7387391c670adf61c9d646 Author: Alexander Klimetschek <aklim...@adobe.com> AuthorDate: Mon Mar 30 22:35:24 2020 -0700 add complete invocation test for ngrok (failing for now) --- package-lock.json | 33 ++++++++++++ package.json | 1 + src/agentmgr.js | 2 +- src/{ => agents}/ngrok.js | 2 +- test/ngrok.test.js | 129 ++++++++++++++++++++++++++++++++++++++++++++-- test/test.js | 85 ++++++++++++++++++++++-------- 6 files changed, 225 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2f7890..64dbc66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2222,6 +2222,33 @@ } } }, + "mock-require": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz", + "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==", + "dev": true, + "requires": { + "get-caller-file": "^1.0.2", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2833,6 +2860,12 @@ "es6-error": "^4.0.1" } }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", diff --git a/package.json b/package.json index 4e43645..801767f 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "get-port": "^5.1.1", "mocha": "^7.1.0", "mocha-multi-reporters": "^1.1.7", + "mock-require": "^3.0.3", "nock": "^12.0.2", "nyc": "^15.0.0", "strip-ansi": "^6.0.0", diff --git a/src/agentmgr.js b/src/agentmgr.js index 3264cc1..c1aa673 100644 --- a/src/agentmgr.js +++ b/src/agentmgr.js @@ -17,7 +17,7 @@ 'use strict'; -const NgrokAgent = require('./ngrok'); +const NgrokAgent = require('./agents/ngrok'); const fs = require('fs-extra'); const sleep = require('util').promisify(setTimeout); diff --git a/src/ngrok.js b/src/agents/ngrok.js similarity index 98% rename from src/ngrok.js rename to src/agents/ngrok.js index 4551ef9..d778b57 100644 --- a/src/ngrok.js +++ b/src/agents/ngrok.js @@ -63,7 +63,7 @@ class NgrokAgent { console.log(`Ngrok forwarding: ${ngrokUrl} => http://localhost:${this.ngrokServerPort} (auth: ${this.ngrokAuth})`); - return fs.readFileSync(`${__dirname}/../agent/agent-ngrok.js`, {encoding: 'utf8'}); + return fs.readFileSync(`${__dirname}/../../agent/agent-ngrok.js`, {encoding: 'utf8'}); } async stop() { diff --git a/test/ngrok.test.js b/test/ngrok.test.js index fc4a033..0ae21b3 100644 --- a/test/ngrok.test.js +++ b/test/ngrok.test.js @@ -19,11 +19,30 @@ 'use strict'; -const Debugger = require("../src/debugger"); - const test = require('./test'); +let Debugger = require("../src/debugger"); + const assert = require('assert'); const nock = require('nock'); +const fetch = require('isomorphic-fetch'); +const mockRequire = require('mock-require'); + +function mockNgrokLibrary(connect, kill) { + mockRequire("ngrok", { + connect: connect || function() { + console.log('ngrok.connect called'); + }, + kill: kill || function() { + console.log('ngrok.kill called'); + } + }); + // the modules have been loaded from another test file before, + // so we need to re-require them in the reverse order + // to make the mockRequire("ngrok") have an effect + mockRequire.reRequire("../src/agents/ngrok"); + mockRequire.reRequire("../src/agentmgr"); + Debugger = mockRequire.reRequire("../src/debugger"); +} describe('ngrok', function() { this.timeout(30000); @@ -67,11 +86,113 @@ describe('ngrok', function() { const dbgr = new Debugger(argv); await dbgr.start(); // no need to run() for this test - dbgr.run(); await dbgr.stop(); assert(ngrok.isDone(), "Expected these HTTP requests: " + ngrok.pendingMocks().join()); }); - // TODO: test ngrokHandler, POST to local server + /* + + Runtime setup: + + [ wskdebug ]------<start>-----+ + ^ | + | | + <handle> | + | v + [ local server ]<---------[ local ngrok ] + ^ + | + | + [ ngrok.io ] + ^ + | + [ openwhisk action ]----------+ + + Test setup: + + [ wskdebug ]------<start>-----+ + ^ | + | | + <handle> | + | v + [ local server ] [ MOCKED ngrok ] + ^ | + | <pass on port> + | | + | | + [ MOCKED invocation call ] <--+ + */ + + it("should handle action invocation using ngrok", async function() { + const actionName = "myaction"; + + // port of the local server started by wskdebug to be expecting calls from ngrok + // which we will do in this test + let ngrokServerPort; + mockNgrokLibrary(function(opts) { + ngrokServerPort = opts.addr; + return "https://UNIT_TEST.ngrok.io"; + }); + + // should not use this code if we specify local sources which return CORRECT + const code = `const main = () => ({ msg: 'WRONG' });`; + + let ngrokAuth; + + test.mockAction(actionName, code); + test.mockCreateBackupAction(actionName); + + // ngrok agent installation + // custom version instead of test.mockInstallAgent() to catch the ngrokAuth + test.openwhiskNock() + .put( + `${test.openwhiskApiUrlActions()}/${actionName}?overwrite=true`, + body => { + ngrokAuth = body.parameters.find(e => e.key === "$ngrokAuth").value; + return body.annotations.some(v => v.key === "wskdebug" && v.value === true); + } + ) + .matchHeader("authorization", test.openwhiskApiAuthHeader()) + .reply(200, test.nodejsActionDescription(actionName)); + + + // wskdebug myaction --ngrok -p ${test.port} + const argv = { + port: test.port, + action: actionName, + sourcePath: "action.js", + ngrok: true + }; + process.chdir("test/nodejs/plain-flat"); + + const dbgr = new Debugger(argv); + await dbgr.start(); + dbgr.run(); + + // wait for everything to startup + await test.sleep(10); + + try { + + const response = await fetch(`http://127.0.0.1:${ngrokServerPort}`, { + method: "POST", + headers: { + authorization: ngrokAuth + }, + body: JSON.stringify({ + $activationId: "1234567890" + }) + }); + + assert.strictEqual(response.status, 200); + const result = await response.json(); + assert.strictEqual(result.msg, "CORRECT"); + + } finally { + await dbgr.stop(); + } + + assert(nock.isDone(), "Expected these HTTP requests: " + nock.pendingMocks().join()); + }); }); diff --git a/test/test.js b/test/test.js index 81f5b23..5a266d7 100644 --- a/test/test.js +++ b/test/test.js @@ -70,6 +70,18 @@ function assertAllNocksInvoked() { ); } +function openwhiskNock() { + return openwhisk; +} + +function openwhiskApiUrlActions() { + return `/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions`; +} + +function openwhiskApiAuthHeader() { + return `Basic ${FAKE_OPENWHISK_AUTH}`; +} + function agentRetryResponse() { return { response: { @@ -101,8 +113,8 @@ function agentExitResponse() { function mockAction(name, code, binary=false) { // reading action without code openwhisk - .get(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${name}`) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .get(`${openwhiskApiUrlActions()}/${name}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .query({"code":"false"}) .reply(200, nodejsActionDescription(name, binary)); @@ -112,56 +124,78 @@ function mockAction(name, code, binary=false) { // reading action with code openwhisk - .get(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${name}`) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .get(`${openwhiskApiUrlActions()}/${name}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, action); } -function expectAgent(name, code, binary=false) { +function mockCreateBackupAction(name, binary=false) { const backupName = name + WSKDEBUG_BACKUP_ACTION_SUFFIX; // wskdebug creating the backup action openwhisk - .put(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${backupName}?overwrite=true`) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .put(`${openwhiskApiUrlActions()}/${backupName}?overwrite=true`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, nodejsActionDescription(backupName, binary)); +} - // wskdebug creating the backup action +function mockInstallAgent(name) { + // wskdebug overwriting the action with the agent openwhisk .put( - `/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${name}?overwrite=true`, + `${openwhiskApiUrlActions()}/${name}?overwrite=true`, body => body.annotations.some(v => v.key === "wskdebug" && v.value === true) ) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, nodejsActionDescription(name)); +} + +function mockReadBackupAction(name, code, binary=false) { + const backupName = name + WSKDEBUG_BACKUP_ACTION_SUFFIX; // reading it later on restore openwhisk - .get(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${backupName}`) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .get(`${openwhiskApiUrlActions()}/${backupName}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, Object.assign(nodejsActionDescription(backupName, binary), { exec: { code } })); +} +function mockRestoreAction(name, code, binary=false) { // restoring action openwhisk .put( - `/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${name}?overwrite=true`, + `${openwhiskApiUrlActions()}/${name}?overwrite=true`, body => body.exec && body.exec.code === code ) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, nodejsActionDescription(name, binary)); +} + +function mockRemoveBackupAction(name) { + const backupName = name + WSKDEBUG_BACKUP_ACTION_SUFFIX; // removing backup after restore openwhisk - .delete(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${backupName}`) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .delete(`${openwhiskApiUrlActions()}/${backupName}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200); } +function expectAgent(name, code, binary=false) { + mockCreateBackupAction(name, binary); + mockInstallAgent(name); + + // shutdown/restore process + mockReadBackupAction(name, code, binary); + mockRestoreAction(name, code, binary); + mockRemoveBackupAction(name); +} + function nockActivation(name, bodyFn) { return openwhisk - .post(`/api/v1/namespaces/${FAKE_OPENWHISK_NAMESPACE}/actions/${name}`, bodyFn) + .post(`${openwhiskApiUrlActions()}/${name}`, bodyFn) .query(true) // support both ?blocking=true and non blocking (no query params) - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`); + .matchHeader("authorization", openwhiskApiAuthHeader()); } function mockAgentPoll(name) { @@ -328,7 +362,7 @@ function mockOpenwhiskSwagger(openwhisk) { .get('/') .optionally() .matchHeader("accept", "application/json") - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, { "api_paths": ["/api/v1"], "description": "OpenWhisk", @@ -394,7 +428,7 @@ function mockOpenwhiskSwagger(openwhisk) { .get('/api/v1') .optionally() .matchHeader("accept", "application/json") - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200,{ "api_version":"1.0.0", "api_version_path":"v1", @@ -411,7 +445,7 @@ function mockOpenwhiskSwagger(openwhisk) { .get('/api/v1/api-docs') .optionally() .matchHeader("accept", "application/json") - .matchHeader("authorization", `Basic ${FAKE_OPENWHISK_AUTH}`) + .matchHeader("authorization", openwhiskApiAuthHeader()) .reply(200, JSON.parse(fs.readFileSync("./test/openwhisk-swagger.json"))); } @@ -487,7 +521,16 @@ module.exports = { mockActionAndInvocation, mockActionDoubleInvocation, // advanced + openwhiskNock, + openwhiskApiUrlActions, + openwhiskApiAuthHeader, mockAction, + mockCreateBackupAction, + mockInstallAgent, + mockReadBackupAction, + mockRestoreAction, + mockRemoveBackupAction, + nodejsActionDescription, expectAgent, nockActivation, expectAgentInvocation,