add complete invocation test for ngrok (failing for now)
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 97%
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 @@
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 @@
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 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 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 @@
// 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 @@
.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 @@
.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 @@
.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 @@
mockActionAndInvocation,
mockActionDoubleInvocation,
// advanced
+ openwhiskNock,
+ openwhiskApiUrlActions,
+ openwhiskApiAuthHeader,
mockAction,
+ mockCreateBackupAction,
+ mockInstallAgent,
+ mockReadBackupAction,
+ mockRestoreAction,
+ mockRemoveBackupAction,
+ nodejsActionDescription,
expectAgent,
nockActivation,
expectAgentInvocation,