Skip to content

Commit 5cf9cd1

Browse files
committed
Prevent double wrapping, increase test coverage
1 parent 9d48ca8 commit 5cf9cd1

File tree

9 files changed

+143
-3
lines changed

9 files changed

+143
-3
lines changed

library/agent/hooks/instrumentation/builtinShim.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ t.test(
6666
`.replace(/\s+/g, " ")
6767
);
6868

69+
const nonExistingModuleShim = generateBuildinShim("abc", "abc", false);
70+
t.equal(nonExistingModuleShim, undefined);
71+
6972
t.end();
7073
}
7174
);

library/agent/hooks/instrumentation/getExportsForBuiltin.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@ t.test(
4343
// ...
4444
])
4545
);
46+
47+
t.match(getExportsForBuiltin("abc"), undefined);
4648
}
4749
);

library/agent/hooks/instrumentation/getExportsForBuiltin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export function getExportsForBuiltin(name: string) {
22
const mod = process.getBuiltinModule(name);
33

44
if (!mod) {
5-
return new Set();
5+
return undefined;
66
}
77

88
return new Set(["default", ...Object.keys(mod)]);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as t from "tap";
2+
import { getPackageVersionFromPath } from "./getPackageVersionFromPath";
3+
import { join } from "path";
4+
5+
t.test("it works", async (t) => {
6+
t.equal(getPackageVersionFromPath(join(__dirname, "../../..")), "0.0.0");
7+
t.equal(
8+
getPackageVersionFromPath(join(__dirname, "../../../node_modules/accepts")),
9+
"2.0.0"
10+
);
11+
t.equal(getPackageVersionFromPath("test123"), undefined);
12+
});

library/agent/hooks/instrumentation/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ import { onModuleLoad } from "./loadHook";
22
import * as mod from "node:module";
33
import type { RegisterHookFunction } from "./types";
44

5+
let hooksRegistered = false;
6+
57
export function registerNodeHooks() {
68
if (!("registerHooks" in mod) || typeof mod.registerHooks !== "function") {
79
throw new Error("This Node.js version is not supported");
810
}
911

12+
// Do not register hooks multiple times
13+
if (hooksRegistered) {
14+
return;
15+
}
16+
hooksRegistered = true;
17+
1018
// Hook into the ESM & CJS module loading process
1119
// Types are required because official Node.js typings are not up-to-date
1220
(mod.registerHooks as RegisterHookFunction)({

library/agent/hooks/instrumentation/injectedFunctions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export function __wrapBuiltinExports(id: string, exports: unknown): unknown {
5656
}
5757

5858
export function __instrumentModifyArgs(id: string, args: unknown[]): unknown[] {
59+
if (!Array.isArray(args)) {
60+
return [];
61+
}
62+
5963
const agent = getInstance();
6064
if (!agent) {
6165
return args;

library/agent/hooks/instrumentation/instructions.test.ts

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Package } from "../Package";
1212
import { BuiltinModule } from "../BuiltinModule";
1313
import {
1414
__instrumentInspectArgs,
15+
__instrumentModifyArgs,
1516
__wrapBuiltinExports,
1617
} from "./injectedFunctions";
1718
import { createTestAgent } from "../../../helpers/createTestAgent";
@@ -119,6 +120,7 @@ t.test("it works", async (t) => {
119120
t.test("it works using injected functions", async (t) => {
120121
let pkgInspectArgsCalled = false;
121122
let builtinOnRequireCalled = false;
123+
let pkgModifyArgsCalled = false;
122124

123125
const pkg = new Package("foo");
124126
pkg.withVersion("^1.0.0").addFileInstrumentation({
@@ -130,6 +132,10 @@ t.test("it works using injected functions", async (t) => {
130132
inspectArgs: () => {
131133
pkgInspectArgsCalled = true;
132134
},
135+
modifyArgs: (args) => {
136+
pkgModifyArgsCalled = true;
137+
return [42];
138+
},
133139
},
134140
],
135141
});
@@ -145,17 +151,96 @@ t.test("it works using injected functions", async (t) => {
145151

146152
t.equal(pkgInspectArgsCalled, false);
147153
__instrumentInspectArgs("foo.bar.js.bazABCDEF.^1.0.0", []);
154+
__instrumentModifyArgs("foo.bar.js.bazABCDEF.^1.0.0", []);
148155
t.equal(pkgInspectArgsCalled, false);
156+
t.equal(pkgModifyArgsCalled, false);
149157
__instrumentInspectArgs("foo.bar.js.baz.^1.0.0", []);
158+
__instrumentModifyArgs("foo.bar.js.baz.^1.0.0", []);
150159
// No agent yet
151160
t.equal(pkgInspectArgsCalled, false);
161+
t.equal(pkgModifyArgsCalled, false);
162+
163+
createTestAgent();
164+
165+
__instrumentInspectArgs("foo.bar.js.bazABCDEF.^1.0.0", []);
166+
__instrumentModifyArgs("foo.bar.js.bazABCDEF.^1.0.0", []);
167+
t.equal(pkgInspectArgsCalled, false);
168+
t.equal(pkgModifyArgsCalled, false);
152169

153-
const agent = createTestAgent();
154170
__instrumentInspectArgs("foo.bar.js.baz.^1.0.0", []);
155171
t.equal(pkgInspectArgsCalled, true);
172+
t.same(__instrumentModifyArgs("foo.bar.js.baz.^1.0.0", []), [42]);
173+
t.equal(pkgModifyArgsCalled, true);
156174

157175
t.equal(builtinOnRequireCalled, false);
158176
const wrapped = __wrapBuiltinExports("http", {}) as any;
159177
t.equal(builtinOnRequireCalled, true);
160178
t.equal(wrapped.test, 42);
161179
});
180+
181+
t.test("modifyArgs always returns a array", async (t) => {
182+
const pkg = new Package("foo");
183+
pkg.withVersion("^1.0.0").addFileInstrumentation({
184+
path: "xyz.js",
185+
functions: [
186+
{
187+
nodeType: "MethodDefinition",
188+
name: "abc",
189+
modifyArgs: (args) => {
190+
return args;
191+
},
192+
},
193+
{
194+
nodeType: "MethodDefinition",
195+
name: "xyz",
196+
// @ts-expect-error Testing invalid input
197+
modifyArgs: (args) => {
198+
return undefined;
199+
},
200+
},
201+
],
202+
});
203+
204+
setPackagesToInstrument([pkg]);
205+
createTestAgent();
206+
207+
t.same(__instrumentModifyArgs("foo.xyz.js.abc.^1.0.0", [1, 2, 3]), [1, 2, 3]);
208+
// @ts-expect-error Testing invalid input
209+
t.same(__instrumentModifyArgs("foo.xyz.js.abc.^1.0.0", undefined), []);
210+
t.same(
211+
__instrumentModifyArgs("foo.xyz.js.doesnotexist", [1, 2, 3]),
212+
[1, 2, 3]
213+
);
214+
t.same(__instrumentModifyArgs("foo.xyz.js.xyz.^1.0.0", [1, 2, 3]), [1, 2, 3]);
215+
// @ts-expect-error Testing invalid input
216+
t.same(__instrumentModifyArgs("foo.xyz.js.xyz.^1.0.0", undefined), []);
217+
});
218+
219+
t.test("all injected functions handle errors", async (t) => {
220+
const pkg = new Package("foo");
221+
pkg.withVersion("^1.0.0").addFileInstrumentation({
222+
path: "dist/test.mjs",
223+
functions: [
224+
{
225+
nodeType: "MethodDefinition",
226+
name: "abc",
227+
inspectArgs: () => {
228+
throw new Error("test");
229+
},
230+
modifyArgs: () => {
231+
throw new Error("test");
232+
},
233+
modifyReturnValue: () => {
234+
throw new Error("test");
235+
},
236+
},
237+
],
238+
});
239+
240+
setPackagesToInstrument([pkg]);
241+
createTestAgent();
242+
243+
__instrumentInspectArgs("foo.dist/test.mjs.abc.^1.0.0", []);
244+
__instrumentModifyArgs("foo.dist/test.mjs.abc.^1.0.0", []);
245+
t.same(__instrumentModifyArgs("foo.dist/test.mjs.abc.^1.0.0", []), []);
246+
});

library/agent/hooks/instrumentation/loadHook.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ import { createTestAgent } from "../../../helpers/createTestAgent";
33
import { applyHooks } from "../../applyHooks";
44
import { Hooks } from "../Hooks";
55
import * as mod from "node:module";
6+
import { registerNodeHooks } from ".";
7+
8+
t.test(
9+
"it throws an error if Node.js version is not supported",
10+
{
11+
skip:
12+
"registerHooks" in mod ? "Only test on older Node.js versions" : false,
13+
},
14+
async (t) => {
15+
createTestAgent();
16+
const error = t.throws(() => applyHooks(new Hooks(), true));
17+
t.ok(error instanceof Error);
18+
if (error instanceof Error) {
19+
t.match(error.message, /This Node.js version is not supported/);
20+
}
21+
}
22+
);
623

724
t.test(
825
"it works",
@@ -54,6 +71,9 @@ t.test(
5471
}
5572
}
5673

74+
// Try to register hooks again to test if it still works
75+
registerNodeHooks();
76+
5777
try {
5878
await import("hono");
5979
t.fail("import should not work");
@@ -99,6 +119,10 @@ t.test(
99119
t.equal(typeof http.createServer, "function");
100120
t.equal(typeof http.Server, "function");
101121

122+
// Require unpatched module
123+
const assert = require("assert") as typeof import("assert");
124+
t.equal(typeof assert, "function");
125+
102126
process.env.AIKIDO_UNIT_TEST = "false";
103127
}
104128
);

library/helpers/createTestAgent.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export function createTestAgent(opts?: {
1717
token?: Token;
1818
serverless?: string;
1919
suppressConsoleLog?: boolean;
20+
newInstrumentation?: boolean;
2021
}) {
2122
if (opts?.suppressConsoleLog ?? true) {
2223
wrap(console, "log", function log() {
@@ -29,7 +30,8 @@ export function createTestAgent(opts?: {
2930
opts?.logger ?? new LoggerNoop(),
3031
opts?.api ?? new ReportingAPIForTesting(),
3132
opts?.token, // Defaults to undefined
32-
opts?.serverless // Defaults to undefined
33+
opts?.serverless, // Defaults to undefined
34+
opts?.newInstrumentation ?? false
3335
);
3436

3537
setInstance(agent);

0 commit comments

Comments
 (0)