Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Event } from '@sentry/core';
import { afterAll, expect, test } from 'vitest';
import { conditionalTest } from '../../../utils';
import { afterAll, describe, expect, test } from 'vitest';
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';

const EVENT = {
Expand Down Expand Up @@ -35,7 +34,7 @@ const EVENT = {
],
};

conditionalTest({ min: 20 })('should capture process and thread breadcrumbs', () => {
describe('should capture process and thread breadcrumbs', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Event } from '@sentry/core';
import { afterAll, describe, expect, test } from 'vitest';
import { conditionalTest } from '../../utils';
import { cleanupChildProcesses, createRunner } from '../../utils/runner';

const WORKER_EVENT: Event = {
Expand Down Expand Up @@ -44,7 +43,7 @@ describe('should capture child process events', () => {
cleanupChildProcesses();
});

conditionalTest({ min: 20 })('worker', () => {
describe('worker', () => {
test('ESM', async () => {
await createRunner(__dirname, 'worker.mjs').expect({ event: WORKER_EVENT }).start().completed();
});
Expand All @@ -54,7 +53,7 @@ describe('should capture child process events', () => {
});
});

conditionalTest({ min: 20 })('fork', () => {
describe('fork', () => {
test('ESM', async () => {
await createRunner(__dirname, 'fork.mjs').expect({ event: CHILD_EVENT }).start().completed();
});
Expand Down
18 changes: 0 additions & 18 deletions dev-packages/node-integration-tests/suites/esm/warn-esm/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,7 @@ afterAll(() => {

const esmWarning = `[Sentry] You are using Node.js v${process.versions.node} in ESM mode ("import syntax"). The Sentry Node.js SDK is not compatible with ESM in Node.js versions before 18.19.0 or before 20.6.0. Please either build your application with CommonJS ("require() syntax"), or upgrade your Node.js version.`;

test("warns if using ESM on Node.js versions that don't support `register()`", async () => {
const nodeMajorVersion = Number(process.versions.node.split('.')[0]);
if (nodeMajorVersion >= 18) {
return;
}

const runner = createRunner(__dirname, 'server.mjs').ignore('event').start();

await runner.makeRequest('get', '/test/success');

expect(runner.getLogs()).toContain(esmWarning);
});

test('does not warn if using ESM on Node.js versions that support `register()`', async () => {
const nodeMajorVersion = Number(process.versions.node.split('.')[0]);
if (nodeMajorVersion < 18) {
return;
}

const runner = createRunner(__dirname, 'server.mjs').ignore('event').start();

await runner.makeRequest('get', '/test/success');
Expand Down
5 changes: 2 additions & 3 deletions dev-packages/node-integration-tests/suites/pino/test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { afterAll, expect } from 'vitest';
import { conditionalTest } from '../../utils';
import { afterAll, describe, expect } from 'vitest';
import { cleanupChildProcesses, createEsmTests } from '../../utils/runner';

conditionalTest({ min: 20 })('Pino integration', () => {
describe('Pino integration', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { mkdirSync, rmdirSync, unlinkSync, writeFileSync } from 'fs';
import * as path from 'path';
import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import { conditionalTest } from '../../../utils';
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';

// This test takes some time because it connects the debugger etc.
Expand Down Expand Up @@ -108,19 +107,15 @@ module.exports = { out_of_app_function };`,
.completed();
});

conditionalTest({ min: 19 })('Node v19+', () => {
test('Should not import inspector when not in use', async () => {
await createRunner(__dirname, 'deny-inspector.mjs').ensureNoErrorOutput().start().completed();
});
test('Should not import inspector when not in use', async () => {
await createRunner(__dirname, 'deny-inspector.mjs').ensureNoErrorOutput().start().completed();
});

conditionalTest({ min: 20 })('Node v20+', () => {
test('Should retain original local variables when error is re-thrown', async () => {
await createRunner(__dirname, 'local-variables-rethrow.js')
.expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT })
.start()
.completed();
});
test('Should retain original local variables when error is re-thrown', async () => {
await createRunner(__dirname, 'local-variables-rethrow.js')
.expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT })
.start()
.completed();
});

test('Includes local variables for caught exceptions when enabled', async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as childProcess from 'child_process';
import * as path from 'path';
import { describe, expect, test } from 'vitest';
import { conditionalTest } from '../../../utils';
import { createRunner } from '../../../utils/runner';

describe('OnUncaughtException integration', () => {
Expand Down Expand Up @@ -103,37 +102,7 @@ describe('OnUncaughtException integration', () => {
.completed();
});

conditionalTest({ max: 18 })('Worker thread error handling Node 18', () => {
test('should capture uncaught worker thread errors - without childProcess integration', async () => {
await createRunner(__dirname, 'worker-thread/uncaught-worker.mjs')
.withInstrument(path.join(__dirname, 'worker-thread/instrument.mjs'))
.expect({
event: {
level: 'fatal',
exception: {
values: [
{
type: 'Error',
value: 'job failed',
mechanism: {
type: 'auto.node.onuncaughtexception',
handled: false,
},
stacktrace: {
frames: expect.any(Array),
},
},
],
},
},
})
.start()
.completed();
});
});

// childProcessIntegration only exists in Node 20+
conditionalTest({ min: 20 })('Worker thread error handling Node 20+', () => {
describe('Worker thread error handling', () => {
test.each(['mjs', 'js'])('should not interfere with worker thread error handling ".%s"', async extension => {
const runner = createRunner(__dirname, `worker-thread/caught-worker.${extension}`)
.withFlags('--import', path.join(__dirname, `worker-thread/instrument.${extension}`))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { afterAll, describe, expect } from 'vitest';
import { conditionalTest } from '../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

describe('fastify auto-instrumentation', () => {
afterAll(() => {
cleanupChildProcesses();
});

// Fastify v5 does not support Node 18
conditionalTest({ min: 20 })('fastify v5', () => {
describe('fastify v5', () => {
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
test('creates transaction with fastify hook, request-handler and manual spans', async () => {
const runner = createRunner()
Expand Down Expand Up @@ -50,10 +48,9 @@ describe('fastify auto-instrumentation', () => {
});

// Fastify v5 only publishes the `tracing:fastify.request.handler:error` diagnostics channel when
// `tracingChannel(...).hasSubscribers` is truthy. That aggregate getter does not exist on Node 18
// (it was added in Node 20), so fastify takes the fast path and never publishes the channel there —
// making automatic error capture (without `setupFastifyErrorHandler`) impossible on Node 18.
conditionalTest({ min: 20 })('error capture via diagnostics channel', () => {
// `tracingChannel(...).hasSubscribers` is truthy, which is what enables automatic error capture
// without `setupFastifyErrorHandler`.
describe('error capture via diagnostics channel', () => {
test('captures errors thrown in route handlers', async () => {
const runner = createRunner()
.ignore('transaction')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterAll, expect } from 'vitest';
import { afterAll, describe, expect } from 'vitest';
import {
GEN_AI_INPUT_MESSAGES,
GEN_AI_OPERATION_NAME,
Expand All @@ -16,13 +16,10 @@ import {
GEN_AI_USAGE_TOTAL_TOKENS,
} from '@sentry/conventions/attributes';
import { GEN_AI_RESPONSE_STOP_REASON_ATTRIBUTE } from '../../../../../../packages/server-utils/src/ai/core/gen-ai-attributes';
import { conditionalTest } from '../../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../../utils/runner';
import { createEsmTests } from '../../../../utils/runner/createEsmAndCjsTests';

// LangChain v1 requires Node.js 20+ (dropped Node 18 support)
// See: https://docs.langchain.com/oss/javascript/migrate/langgraph-v1#dropped-node-18-support
conditionalTest({ min: 20 })('LangChain integration (v1)', () => {
describe('LangChain integration (v1)', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { TransactionEvent } from '@sentry/core';
import { MongoMemoryServer } from 'mongodb-memory-server-global';
import { afterAll, beforeAll, expect } from 'vitest';
import { conditionalTest, isOrchestrionEnabled } from '../../../utils';
import { afterAll, beforeAll, describe, expect } from 'vitest';
import { isOrchestrionEnabled } from '../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

// Pins mongodb 7 so the >= 6.4 promise-based `Connection.prototype.command`
// band is exercised against a real mongodb. mongodb 7 requires Node >= 20.19, so this suite is
// skipped on older Node (on Node 18 the driver throws `ReferenceError: crypto is not defined`).
conditionalTest({ min: 20 })('MongoDB v7 auto-instrumentation', () => {
// band is exercised against a real mongodb.
describe('MongoDB v7 auto-instrumentation', () => {
let mongoServer: MongoMemoryServer;

beforeAll(async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { MongoMemoryServer } from 'mongodb-memory-server-global';
import { afterAll, beforeAll, expect } from 'vitest';
import { conditionalTest, isOrchestrionEnabled } from '../../../utils';
import { afterAll, beforeAll, describe, expect } from 'vitest';
import { isOrchestrionEnabled } from '../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

// mongoose >= 9.7.0 publishes its operations via `node:diagnostics_channel`, so the SDK subscribes
// to those channels (`subscribeMongooseDiagnosticChannels`) instead of monkey-patching. This suite
// pins `^9.7` and asserts the diagnostics-channel path: stable OTel DB semconv attributes, redacted
// query text, span relationships, and that the legacy IITM patcher does NOT also fire (no double
// instrumentation). mongoose 9 requires Node >=20.19, so this suite is skipped on older Node.
conditionalTest({ min: 20 })('Mongoose tracing channel Test', () => {
// instrumentation).
describe('Mongoose tracing channel Test', () => {
const driverOrigin = isOrchestrionEnabled() ? 'auto.db.mongo' : 'auto.db.otel.mongo';
let mongoServer: MongoMemoryServer;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { MongoMemoryServer } from 'mongodb-memory-server-global';
import { afterAll, beforeAll, expect } from 'vitest';
import { conditionalTest, isOrchestrionEnabled } from '../../../utils';
import { afterAll, beforeAll, describe, expect } from 'vitest';
import { isOrchestrionEnabled } from '../../../utils';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

// Pins the highest mongoose 9 below 9.7, the top of the IITM patcher's `>=5.9.7 <9.7.0` range, so the
// monkey-patch path is exercised against a real mongoose 9. mongoose >= 9.7 publishes via
// diagnostics_channel and is covered by the `mongoose-tracing-channel` suite instead.
// mongoose 9 requires Node >=20.19, so this suite is skipped on older Node.
conditionalTest({ min: 20 })('Mongoose v9 Test', () => {
describe('Mongoose v9 Test', () => {
const origin = isOrchestrionEnabled() ? 'auto.db.mongoose' : 'auto.db.otel.mongoose';
let mongoServer: MongoMemoryServer;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { afterAll, expect } from 'vitest';
import { conditionalTest } from '../../../utils';
import { afterAll, describe, expect } from 'vitest';
import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner';

// mysql2 >= 3.20.0 publishes its operations via `node:diagnostics_channel`, so the SDK subscribes
// to those channels (`subscribeMysql2DiagnosticChannels`) instead of monkey-patching. This suite
// pins `^3.20.0` and asserts the diagnostics-channel path: stable OTel DB semconv attributes,
// redacted query text, and that the legacy IITM patcher (gated to `< 3.20.0`) does NOT also fire.
// `TracingChannel` is only reliable on Node >= 20, so this suite is skipped on older Node.
conditionalTest({ min: 20 })('mysql2 tracing channel Test', () => {
describe('mysql2 tracing channel Test', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import * as path from 'path';
import { afterAll, test } from 'vitest';
import { conditionalTest } from '../../../utils';
import { afterAll, describe, test } from 'vitest';
import { cleanupChildProcesses, createRunner } from '../../../utils/runner';

afterAll(() => {
cleanupChildProcesses();
});

// The runtime module hook needs Node >= 18.19; gate on 20 to stay on the stable
// `Module.registerHooks` / `Channel.hasSubscribers` surface.
conditionalTest({ min: 20 })('orchestrion lazy channel registration', () => {
describe('orchestrion lazy channel registration', () => {
// The scenario self-asserts (via `node:assert`) that a default channel
// integration has NOT subscribed to its channel until the instrumented module
// is loaded, then that it HAS once loaded. A violation throws, which
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { afterAll, expect } from 'vitest';
import { conditionalTest } from '../../../utils';
import { afterAll, describe, expect } from 'vitest';
import { cleanupChildProcesses, createEsmAndCjsTests, describeWithDockerCompose } from '../../../utils/runner';

afterAll(() => {
cleanupChildProcesses();
});

// Prisma 7 requires Node.js 20.19+
conditionalTest({ min: 20 })('Prisma ORM v7 Tests', () => {
describe('Prisma ORM v7 Tests', () => {
describeWithDockerCompose('Prisma ORM v7', { workingDirectory: [__dirname] }, () => {
createEsmAndCjsTests(
__dirname,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NODE_VERSION, type Event } from '@sentry/node';
import type { Event } from '@sentry/node';
import { afterAll, describe, expect } from 'vitest';
import {
GEN_AI_CONVERSATION_ID,
Expand All @@ -25,14 +25,10 @@ import { GEN_AI_TOOL_CALL_ID_ATTRIBUTE } from '../../../../../../packages/server
import { cleanupChildProcesses, createEsmAndCjsTests, createEsmTests } from '../../../../utils/runner';
import { isOrchestrionEnabled } from '../../../../utils';

// On Node 18, we only test v6 as v7 is not supported
const matrix =
NODE_VERSION.major === 18
? ([['6', '^6.0.0']] as const)
: ([
['6', '^6.0.0'],
['7', '^7.0.0'],
] as const);
const matrix = [
['6', '^6.0.0'],
['7', '^7.0.0'],
] as const;

describe.each(matrix)('Vercel AI integration (version %s)', (version, vercelAiVersion) => {
afterAll(() => {
Expand Down
5 changes: 0 additions & 5 deletions packages/node/test/integrations/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ describe('Context', () => {
vi.clearAllMocks();
});

conditionalTest({ max: 18 })('it does not return free_memory on older node versions', () => {
const appContext = getAppContext();
expect(appContext.free_memory).toBeUndefined();
});

conditionalTest({ min: 22 })(
'returns free_memory if process.availableMemory is defined and returns a valid value',
() => {
Expand Down
Loading