@contrast/agentify 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/function-hooks.js +1 -1
- package/package.json +1 -1
- package/lib/contrast-methods.test.js +0 -72
- package/lib/function-hooks.test.js +0 -175
- package/lib/index.test.js +0 -89
- package/lib/instrumentation-locks.test.js +0 -55
- package/lib/rewrite-hooks.test.js +0 -76
- package/lib/sources.test.js +0 -196
package/lib/function-hooks.js
CHANGED
|
@@ -97,7 +97,7 @@ module.exports = function (deps) {
|
|
|
97
97
|
|
|
98
98
|
let result = code;
|
|
99
99
|
|
|
100
|
-
if (code.indexOf('ContrastMethods')) {
|
|
100
|
+
if (code.indexOf('ContrastMethods') > -1) {
|
|
101
101
|
const unwritten = functionHooks.unwrite(code);
|
|
102
102
|
functionHooks.cache.set(data.obj, unwritten);
|
|
103
103
|
result = unwritten;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/agentify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Configures Contrast agent services and instrumentation within an application",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const sinon = require('sinon');
|
|
5
|
-
|
|
6
|
-
describe('agentify contrast-methods', function() {
|
|
7
|
-
let contrastMethods;
|
|
8
|
-
let core;
|
|
9
|
-
|
|
10
|
-
beforeEach(function() {
|
|
11
|
-
const mocks = require('../../test/mocks');
|
|
12
|
-
core = mocks.core();
|
|
13
|
-
core.logger = mocks.logger();
|
|
14
|
-
contrastMethods = require('./contrast-methods')(core);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('api.eval()', function() {
|
|
18
|
-
it('acts as identity function', function() {
|
|
19
|
-
[{}, null, 1, 'two', undefined].forEach(v => {
|
|
20
|
-
expect(v).to.equal(contrastMethods.api.eval(v));
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('.getGlobal', function() {
|
|
26
|
-
it('returns global :)', function() {
|
|
27
|
-
expect(contrastMethods.getGlobal()).to.equal(global);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('.install', function() {
|
|
32
|
-
let globalMock;
|
|
33
|
-
let contrastMethods;
|
|
34
|
-
|
|
35
|
-
beforeEach(function() {
|
|
36
|
-
contrastMethods = require('./contrast-methods')(core);
|
|
37
|
-
globalMock = {};
|
|
38
|
-
sinon.stub(contrastMethods, 'getGlobal').returns(globalMock);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('installs on global', function() {
|
|
42
|
-
expect(globalMock.ContrastMethods).not.to.exist;
|
|
43
|
-
contrastMethods.install();
|
|
44
|
-
expect(globalMock.ContrastMethods).to.exist;
|
|
45
|
-
expect(() => {
|
|
46
|
-
delete globalMock.ContrastMethods;
|
|
47
|
-
}).throw(/Cannot delete property/);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('installs just once', function() {
|
|
51
|
-
expect(globalMock.ContrastMethods).not.to.exist;
|
|
52
|
-
contrastMethods.install();
|
|
53
|
-
contrastMethods.install();
|
|
54
|
-
expect(globalMock.ContrastMethods).to.equal(contrastMethods.api);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('installation failure handing', function() {
|
|
58
|
-
beforeEach(function() {
|
|
59
|
-
Object.defineProperty(globalMock, 'ContrastMethods', {
|
|
60
|
-
configurable: false,
|
|
61
|
-
value: 'does not matter for test'
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
it('logs error when global.ContrastMethods cannot be redefined', function() {
|
|
65
|
-
contrastMethods.install();
|
|
66
|
-
const [{ err }, description] = core.logger.error.getCall(0).args;
|
|
67
|
-
expect(description).to.eql('Unable to install global.ContrastMethods');
|
|
68
|
-
expect(err.message).to.eql('Cannot redefine property: ContrastMethods');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const functionHooks = require('./function-hooks');
|
|
5
|
-
|
|
6
|
-
describe('agentify function-hooks', function () {
|
|
7
|
-
let coreMock;
|
|
8
|
-
|
|
9
|
-
beforeEach(function () {
|
|
10
|
-
const mocks = require('../../test/mocks');
|
|
11
|
-
|
|
12
|
-
coreMock = mocks.core();
|
|
13
|
-
coreMock.patcher = mocks.patcher();
|
|
14
|
-
coreMock.logger = mocks.logger();
|
|
15
|
-
coreMock.depHooks = mocks.depHooks();
|
|
16
|
-
coreMock.scopes = mocks.scopes();
|
|
17
|
-
coreMock.rewriter = mocks.rewriter();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should install function hooks', function () {
|
|
21
|
-
functionHooks(coreMock);
|
|
22
|
-
coreMock.functionHooks.install();
|
|
23
|
-
expect(coreMock.functionHooks.installed).to.be.true;
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should skip if it is already installed', function () {
|
|
27
|
-
functionHooks(coreMock);
|
|
28
|
-
coreMock.functionHooks.installed = true;
|
|
29
|
-
coreMock.functionHooks.install();
|
|
30
|
-
|
|
31
|
-
expect(coreMock.logger.debug).to.not.have.been.called;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should return cached value when the function is already called', function () {
|
|
35
|
-
const functionA = function sum(a, b) {
|
|
36
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
37
|
-
return a + b;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const cachedValue = 'cachedValue';
|
|
41
|
-
|
|
42
|
-
functionHooks(coreMock);
|
|
43
|
-
|
|
44
|
-
coreMock.functionHooks.cache.set(functionA, cachedValue);
|
|
45
|
-
|
|
46
|
-
const data = {
|
|
47
|
-
obj: functionA,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
let result;
|
|
51
|
-
coreMock.patcher.patch.callsFake((prototype, method, options) => {
|
|
52
|
-
result = options.around(() => ({}), data);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
coreMock.functionHooks.install();
|
|
56
|
-
|
|
57
|
-
expect(result).to.eql(cachedValue);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should call the function itself when the function is not hooked', function () {
|
|
61
|
-
const functionA = function sum(a, b) {
|
|
62
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
63
|
-
return a + b;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
functionHooks(coreMock);
|
|
67
|
-
|
|
68
|
-
const data = {
|
|
69
|
-
obj: functionA,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
let result;
|
|
73
|
-
coreMock.patcher.patch.callsFake((prototype, method, options) => {
|
|
74
|
-
result = options.around(functionA.toString.bind(functionA), data);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const unwriteResult = `function sum(a, b) {
|
|
78
|
-
eval(2 + 2);
|
|
79
|
-
return a + b;
|
|
80
|
-
}`;
|
|
81
|
-
|
|
82
|
-
coreMock.rewriter.unwrite.callsFake((data) => unwriteResult);
|
|
83
|
-
|
|
84
|
-
coreMock.functionHooks.install();
|
|
85
|
-
|
|
86
|
-
expect(result).to.eql(unwriteResult);
|
|
87
|
-
expect(coreMock.functionHooks.cache.get(data.obj)).to.eql(unwriteResult);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should the call the original function of the hooked function', function () {
|
|
91
|
-
const functionA = function sum(a, b) {
|
|
92
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
93
|
-
return a + b;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const functionB = function sum(a, b) {
|
|
97
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
98
|
-
return a + b + 2;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
functionHooks(coreMock);
|
|
102
|
-
|
|
103
|
-
const weekMap = coreMock.patcher.hookedFunctions;
|
|
104
|
-
weekMap.set(functionA, { fn: functionB });
|
|
105
|
-
|
|
106
|
-
const data = {
|
|
107
|
-
obj: functionA,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
let result;
|
|
111
|
-
coreMock.patcher.patch.callsFake((prototype, method, options) => {
|
|
112
|
-
result = options.around(functionA.toString.bind(functionA), data);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const unwriteResult = `function sum(a, b) {
|
|
116
|
-
eval(2 + 2);
|
|
117
|
-
return a + b + 2;
|
|
118
|
-
}`;
|
|
119
|
-
|
|
120
|
-
coreMock.rewriter.unwrite.callsFake((data) => unwriteResult);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
coreMock.functionHooks.install();
|
|
124
|
-
|
|
125
|
-
expect(result).to.eql(unwriteResult);
|
|
126
|
-
expect(coreMock.functionHooks.cache.get(data.obj)).to.eql(unwriteResult);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should log warning message when there is a problem unwriting the code', function () {
|
|
130
|
-
const err = new Error('Error');
|
|
131
|
-
const functionA = function sum(a, b) {
|
|
132
|
-
eval(global.ContrastMethods.eval('2 + 2'));
|
|
133
|
-
return a + b;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
functionHooks(coreMock);
|
|
137
|
-
|
|
138
|
-
const data = {
|
|
139
|
-
obj: functionA,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
coreMock.patcher.patch.callsFake((prototype, method, options) => {
|
|
143
|
-
options.around(functionA.toString.bind(functionA), data);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
coreMock.rewriter.unwrite.throws(err);
|
|
147
|
-
|
|
148
|
-
coreMock.functionHooks.install();
|
|
149
|
-
|
|
150
|
-
expect(coreMock.logger.warn).to.have.been.calledWith({ err, code: functionA.toString() }, 'Failed to unwrite function code');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should call the original toString if the function is not hooked', function() {
|
|
154
|
-
const functionA = () => 1;
|
|
155
|
-
functionHooks(coreMock);
|
|
156
|
-
coreMock.functionHooks.install();
|
|
157
|
-
|
|
158
|
-
const resultOfA = functionA.toString();
|
|
159
|
-
|
|
160
|
-
expect(resultOfA).to.eqls('() => 1');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should uninstall the hooks', function() {
|
|
164
|
-
const { toString } = Function.prototype;
|
|
165
|
-
const functionA = () => 1;
|
|
166
|
-
functionHooks(coreMock);
|
|
167
|
-
coreMock.functionHooks.install();
|
|
168
|
-
coreMock.functionHooks.uninstall();
|
|
169
|
-
|
|
170
|
-
const resultOfA = functionA.toString();
|
|
171
|
-
|
|
172
|
-
expect(resultOfA).to.eqls('() => 1');
|
|
173
|
-
expect(Function.prototype.toString).to.eqls(toString);
|
|
174
|
-
});
|
|
175
|
-
});
|
package/lib/index.test.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const chai = require('chai');
|
|
4
|
-
const { expect } = chai;
|
|
5
|
-
const sinon = require('sinon');
|
|
6
|
-
|
|
7
|
-
describe('agentify', function() {
|
|
8
|
-
const Module = require('module');
|
|
9
|
-
let core;
|
|
10
|
-
let agentify;
|
|
11
|
-
let runMain;
|
|
12
|
-
|
|
13
|
-
beforeEach(function() {
|
|
14
|
-
const factory = require('.');
|
|
15
|
-
const mocks = require('../../test/mocks');
|
|
16
|
-
|
|
17
|
-
core = mocks.core();
|
|
18
|
-
core.logger = mocks.logger();
|
|
19
|
-
core.depHooks = mocks.depHooks();
|
|
20
|
-
core.scopes = mocks.scopes();
|
|
21
|
-
core.patcher = mocks.patcher();
|
|
22
|
-
agentify = factory(core);
|
|
23
|
-
runMain = sinon.stub(Module, 'runMain');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('factory', function() {
|
|
27
|
-
it('invoking with callback will initialize and patch runMain', async function() {
|
|
28
|
-
let ranFlag;
|
|
29
|
-
|
|
30
|
-
agentify((deps) => {
|
|
31
|
-
expect(deps).to.equal(core);
|
|
32
|
-
ranFlag = true;
|
|
33
|
-
}, {
|
|
34
|
-
svcList: ['depHooks']
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
await Module.runMain();
|
|
38
|
-
|
|
39
|
-
expect(ranFlag).to.be.true;
|
|
40
|
-
expect(runMain).to.be.called;
|
|
41
|
-
expect(core.depHooks.install).to.have.been.called;
|
|
42
|
-
expect(core.logger.error).not.to.been.called;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('will not run install methods opts = { install: false }', async function() {
|
|
46
|
-
const a = agentify(() => {}, { install: false });
|
|
47
|
-
sinon.spy(a, 'install');
|
|
48
|
-
|
|
49
|
-
await Module.runMain();
|
|
50
|
-
|
|
51
|
-
expect(a.install).not.to.have.been.called;
|
|
52
|
-
expect(core.logger.error).not.called;
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('handles errors in pre runMain hook (init)', async function() {
|
|
56
|
-
const err = new Error('bonk');
|
|
57
|
-
const a = agentify(() => {
|
|
58
|
-
throw err;
|
|
59
|
-
});
|
|
60
|
-
sinon.spy(a, 'install');
|
|
61
|
-
sinon.spy(a, 'handleInstallFailure');
|
|
62
|
-
|
|
63
|
-
await Module.runMain();
|
|
64
|
-
|
|
65
|
-
expect(a.handleInstallFailure).to.have.been.calledWith(err);
|
|
66
|
-
expect(a.install).to.not.have.been.called;
|
|
67
|
-
expect(core.logger.error).calledWith({ err }, 'A fatal agent installation error has occurred. The application will be run without instrumentation.');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
it('instantiation handles "unregistered" services', async function() {
|
|
72
|
-
agentify((deps) => 0, {
|
|
73
|
-
install: true,
|
|
74
|
-
svcList: ['foo']
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// Having non-existent service in list is handled during initialization
|
|
78
|
-
await Module.runMain();
|
|
79
|
-
|
|
80
|
-
expect(core.logger.error).not.called;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('requires init function arg', function() {
|
|
84
|
-
expect(() => {
|
|
85
|
-
agentify('not a function');
|
|
86
|
-
}).to.throw('Invalid usage: first argument must be a function');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const sinon = require('sinon');
|
|
5
|
-
const Module = require('module');
|
|
6
|
-
const instrumentationLocks = require('./instrumentation-locks');
|
|
7
|
-
const mocks = require('../../test/mocks');
|
|
8
|
-
|
|
9
|
-
describe('agentify instrumentation-locks', function () {
|
|
10
|
-
let core, storeSpy;
|
|
11
|
-
|
|
12
|
-
beforeEach(function () {
|
|
13
|
-
core = mocks.core();
|
|
14
|
-
core.logger = mocks.logger();
|
|
15
|
-
core.scopes = require('@contrast/scopes')(core);
|
|
16
|
-
core.patcher = require('@contrast/patcher')(core);
|
|
17
|
-
storeSpy = sinon.stub();
|
|
18
|
-
|
|
19
|
-
const origRequire = Module.prototype.require;
|
|
20
|
-
sinon.stub(Module.prototype, 'require').callsFake(function (...args) {
|
|
21
|
-
storeSpy(core.scopes.instrumentation.getStore());
|
|
22
|
-
return origRequire.call(this, ...args);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
instrumentationLocks(core);
|
|
26
|
-
core.instrumentationLocks.install();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('require gets called with instrumentation locked', function () {
|
|
30
|
-
// side effects are in place for this to exercise properly
|
|
31
|
-
require('crypto');
|
|
32
|
-
expect(storeSpy).to.have.been.calledWith({
|
|
33
|
-
lock: true,
|
|
34
|
-
name: 'Module.prototype.require',
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('will not run with lock if already locked', function () {
|
|
39
|
-
// side effects are in place for this to exercise properly
|
|
40
|
-
core.scopes.instrumentation.run({ lock: true, name: 'test' }, () => {
|
|
41
|
-
// target here is arbitrary
|
|
42
|
-
require('events');
|
|
43
|
-
|
|
44
|
-
expect(storeSpy).to.have.been.calledWith({
|
|
45
|
-
lock: true,
|
|
46
|
-
name: 'test',
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
expect(storeSpy).to.not.have.been.calledWith({
|
|
50
|
-
lock: true,
|
|
51
|
-
name: 'Module.prototype.require',
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const sinon = require('sinon');
|
|
4
|
-
const { expect } = require('chai');
|
|
5
|
-
const Module = require('module');
|
|
6
|
-
|
|
7
|
-
const testCode = require.resolve('../test/resources/file');
|
|
8
|
-
|
|
9
|
-
describe('agentify rewrite-hooks', function() {
|
|
10
|
-
let core, rewriteHooks, origCompileSpy;
|
|
11
|
-
|
|
12
|
-
beforeEach(function() {
|
|
13
|
-
const mocks = require('../../test/mocks');
|
|
14
|
-
core = mocks.core();
|
|
15
|
-
core.logger = mocks.logger();
|
|
16
|
-
core.agentify = mocks.agentify();
|
|
17
|
-
core.config = mocks.config();
|
|
18
|
-
core.config.agent.node.enable_rewrite = true;
|
|
19
|
-
core.rewriter = mocks.rewriter();
|
|
20
|
-
origCompileSpy = sinon.spy(Module.prototype, '_compile');
|
|
21
|
-
rewriteHooks = require('./rewrite-hooks')(core);
|
|
22
|
-
rewriteHooks.install();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
afterEach(function() {
|
|
26
|
-
delete require.cache[testCode];
|
|
27
|
-
rewriteHooks.restore();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('Should not rewrite code when enable_rewrite is false', function() {
|
|
31
|
-
sinon.restore();
|
|
32
|
-
core.config.agent.node.enable_rewrite = false;
|
|
33
|
-
rewriteHooks = require('./rewrite-hooks')(core);
|
|
34
|
-
rewriteHooks.install();
|
|
35
|
-
require('../test/resources/file');
|
|
36
|
-
expect(core.rewriter.rewrite).to.not.have.been.called;
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('Should rewrite code', function() {
|
|
40
|
-
require('../test/resources/file');
|
|
41
|
-
expect(core.rewriter.rewrite).to.have.been.calledWith(
|
|
42
|
-
sinon.match.string,
|
|
43
|
-
{ filename: testCode }
|
|
44
|
-
);
|
|
45
|
-
expect(core.logger.error).to.not.have.been.called;
|
|
46
|
-
expect(origCompileSpy).to.have.been.calledWith(
|
|
47
|
-
"const ContrastMethods = global.ContrastMethods\n/* eslint-disable */\n'use strict'\nconst variable = 'variable';\n",
|
|
48
|
-
testCode
|
|
49
|
-
);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('Should log error if module contains a SyntaxError', function() {
|
|
53
|
-
core.rewriter.rewrite.callsFake((content) => ({
|
|
54
|
-
code: content.replace('const variable', 'const variable const a')
|
|
55
|
-
}));
|
|
56
|
-
require('../test/resources/file');
|
|
57
|
-
expect(core.rewriter.rewrite).to.have.been.calledWith(
|
|
58
|
-
sinon.match.string,
|
|
59
|
-
{ filename: testCode }
|
|
60
|
-
);
|
|
61
|
-
expect(core.logger.error).to.have.been.calledWith(
|
|
62
|
-
{
|
|
63
|
-
err: sinon.match({
|
|
64
|
-
message: 'Missing initializer in const declaration'
|
|
65
|
-
})
|
|
66
|
-
},
|
|
67
|
-
'Failed to compile rewritten code for %s, rewritten code %s, compiling original code.',
|
|
68
|
-
testCode,
|
|
69
|
-
sinon.match('const variable const a')
|
|
70
|
-
);
|
|
71
|
-
expect(origCompileSpy).to.have.been.calledWith(
|
|
72
|
-
"/* eslint-disable */\n'use strict'\nconst variable = 'variable';\n",
|
|
73
|
-
testCode
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
});
|
package/lib/sources.test.js
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { expect } = require('chai');
|
|
4
|
-
const sinon = require('sinon');
|
|
5
|
-
const sourcesModule = require('./sources');
|
|
6
|
-
|
|
7
|
-
describe('agentify sources', function () {
|
|
8
|
-
let mocks, coreMock, depHooksMock, patcherMock, loggerMock, scopesMock;
|
|
9
|
-
|
|
10
|
-
beforeEach(function () {
|
|
11
|
-
mocks = require('../../test/mocks');
|
|
12
|
-
loggerMock = mocks.logger();
|
|
13
|
-
patcherMock = mocks.patcher();
|
|
14
|
-
depHooksMock = mocks.depHooks();
|
|
15
|
-
scopesMock = mocks.scopes();
|
|
16
|
-
coreMock = {
|
|
17
|
-
logger: loggerMock,
|
|
18
|
-
patcher: patcherMock,
|
|
19
|
-
depHooks: depHooksMock,
|
|
20
|
-
scopes: scopesMock,
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('attaching the sources module to the core object', function () {
|
|
25
|
-
it('attaches the module to the core object', function () {
|
|
26
|
-
const sources = sourcesModule(coreMock);
|
|
27
|
-
expect(coreMock).to.haveOwnProperty('sources');
|
|
28
|
-
expect(coreMock.sources).to.haveOwnProperty('install');
|
|
29
|
-
expect(sources).to.haveOwnProperty('install');
|
|
30
|
-
expect(sources).to.haveOwnProperty('functions');
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('installing instrumentation for HTTP type of sources', function () {
|
|
35
|
-
let argumentsMap, httpMock, httpsMock, http2Mock, spdyMock;
|
|
36
|
-
|
|
37
|
-
beforeEach(function () {
|
|
38
|
-
httpMock = { name: 'http' };
|
|
39
|
-
httpsMock = { name: 'https' };
|
|
40
|
-
http2Mock = { name: 'http2' };
|
|
41
|
-
spdyMock = { name: 'spdy' };
|
|
42
|
-
const patchType = 'http-sources';
|
|
43
|
-
argumentsMap = {
|
|
44
|
-
http: {
|
|
45
|
-
source: httpMock,
|
|
46
|
-
patcherArgs: [
|
|
47
|
-
[httpMock, 'createServer', { name: 'httpServer', patchType }],
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
https: {
|
|
51
|
-
source: httpsMock,
|
|
52
|
-
patcherArgs: [
|
|
53
|
-
[httpsMock, 'createServer', { name: 'httpsServer', patchType }],
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
http2: {
|
|
57
|
-
source: http2Mock,
|
|
58
|
-
patcherArgs: [
|
|
59
|
-
[http2Mock, 'createServer', { name: 'http2Server', patchType }],
|
|
60
|
-
[
|
|
61
|
-
http2Mock,
|
|
62
|
-
'createSecureServer',
|
|
63
|
-
{ name: 'http2SecureServer', patchType },
|
|
64
|
-
],
|
|
65
|
-
],
|
|
66
|
-
},
|
|
67
|
-
spdy: {
|
|
68
|
-
source: spdyMock,
|
|
69
|
-
patcherArgs: [
|
|
70
|
-
[spdyMock, 'createServer', { name: 'spdyServer', patchType }],
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
coreMock.depHooks.resolve.callsFake((moduleMetadata, callback) =>
|
|
75
|
-
callback(argumentsMap[moduleMetadata.name].source)
|
|
76
|
-
);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should call patcher.patch with the correct arguments from depHooks.resolve', function () {
|
|
80
|
-
const sources = sourcesModule(coreMock);
|
|
81
|
-
sources.install();
|
|
82
|
-
|
|
83
|
-
const callOrder = ['http', 'https', 'http2', 'http2', 'spdy'];
|
|
84
|
-
let consecutiveCalls = 0;
|
|
85
|
-
|
|
86
|
-
for (let i = 0; i < callOrder.length; i++) {
|
|
87
|
-
consecutiveCalls = callOrder[i] == 'http2' ? consecutiveCalls : 0;
|
|
88
|
-
const argument = (callNumber, argIndex) =>
|
|
89
|
-
patcherMock.patch.getCall(callNumber).args[argIndex];
|
|
90
|
-
const expectedArgument = (callNumber, argIndex, consecutiveCalls) =>
|
|
91
|
-
argumentsMap[callOrder[callNumber]].patcherArgs[consecutiveCalls][
|
|
92
|
-
argIndex
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
expect(argument(i, 0)).to.equal(
|
|
96
|
-
expectedArgument(i, 0, consecutiveCalls)
|
|
97
|
-
);
|
|
98
|
-
expect(argument(i, 1)).to.equal(
|
|
99
|
-
expectedArgument(i, 1, consecutiveCalls)
|
|
100
|
-
);
|
|
101
|
-
expect(argument(i, 2)).to.include(
|
|
102
|
-
expectedArgument(i, 2, consecutiveCalls)
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
if (callOrder[i] == 'http2' && !consecutiveCalls) {
|
|
106
|
-
consecutiveCalls++;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
expect(patcherMock.patch).to.have.been.callCount(callOrder.length);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('functions', function () {
|
|
115
|
-
let emitAroundHook, createServerPostHook, dataObj;
|
|
116
|
-
|
|
117
|
-
beforeEach(function () {
|
|
118
|
-
({
|
|
119
|
-
functions: { createServerPostHook, emitAroundHook },
|
|
120
|
-
} = sourcesModule(coreMock));
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('createServerPostHook', function () {
|
|
124
|
-
let serverMock, prototypeMock;
|
|
125
|
-
beforeEach(function () {
|
|
126
|
-
serverMock = {};
|
|
127
|
-
prototypeMock = {
|
|
128
|
-
emit() {},
|
|
129
|
-
};
|
|
130
|
-
dataObj = {
|
|
131
|
-
result: serverMock,
|
|
132
|
-
};
|
|
133
|
-
Object.setPrototypeOf(serverMock, prototypeMock);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('calls patcher.patch() if proper data argument is supplied', function () {
|
|
137
|
-
createServerPostHook('httpServer')(dataObj);
|
|
138
|
-
expect(loggerMock.error).to.not.have.been.called;
|
|
139
|
-
expect(patcherMock.patch).to.have.been.calledWith(
|
|
140
|
-
prototypeMock,
|
|
141
|
-
'emit',
|
|
142
|
-
{
|
|
143
|
-
name: 'server.emit',
|
|
144
|
-
patchType: 'req-async-storage',
|
|
145
|
-
around: sinon.match.func
|
|
146
|
-
}
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('calls logger.error() if none or invalid data argument is supplied', function () {
|
|
151
|
-
createServerPostHook('httpServer')({});
|
|
152
|
-
expect(loggerMock.error).to.have.been.calledWith(
|
|
153
|
-
'Unable to patch server prototype, continue without instrumentation'
|
|
154
|
-
);
|
|
155
|
-
Object.setPrototypeOf(serverMock, null);
|
|
156
|
-
createServerPostHook('httpServer')(serverMock);
|
|
157
|
-
expect(loggerMock.error).to.have.been.called.calledTwice;
|
|
158
|
-
expect(patcherMock.patch).to.not.have.been.called;
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('emitAroundHook', function () {
|
|
163
|
-
let nextFn, reqMock, resMock, eventMock;
|
|
164
|
-
beforeEach(function () {
|
|
165
|
-
reqMock = () => {
|
|
166
|
-
this.method = 'GET';
|
|
167
|
-
this.url = 'http://localhost';
|
|
168
|
-
};
|
|
169
|
-
resMock = () => {};
|
|
170
|
-
eventMock = 'request';
|
|
171
|
-
dataObj = {
|
|
172
|
-
args: [eventMock, reqMock, resMock],
|
|
173
|
-
};
|
|
174
|
-
nextFn = function () {
|
|
175
|
-
return {
|
|
176
|
-
sourcesStore: scopesMock.sources.getStore(),
|
|
177
|
-
};
|
|
178
|
-
};
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('executes the hooked nextFn in the provided context if the event name is "request"', function () {
|
|
182
|
-
const result = emitAroundHook('httpServer')(nextFn, dataObj);
|
|
183
|
-
expect(result.sourcesStore).to.deep.include({
|
|
184
|
-
serverType: 'httpServer',
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('executes the hooked nextFn without binding it to a context if the event name is not "request"', function () {
|
|
189
|
-
eventMock = 'listen';
|
|
190
|
-
dataObj.args[0] = eventMock;
|
|
191
|
-
const result = emitAroundHook('httpServer')(nextFn, dataObj);
|
|
192
|
-
expect(result.sourcesStore).to.equal(undefined);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
});
|