diff --git a/packages/opentelemetry/jest.config.js b/packages/opentelemetry/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/opentelemetry/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 7c4b1ca3d83e..a14af077d1bc 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -73,9 +73,9 @@ "clean": "rimraf build coverage sentry-opentelemetry-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "yarn test:jest", - "test:jest": "jest", - "test:watch": "jest --watch", + "test": "yarn test:unit", + "test:unit": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/opentelemetry/test/asyncContextStrategy.test.ts b/packages/opentelemetry/test/asyncContextStrategy.test.ts index d0c4c137c236..cdbb91774561 100644 --- a/packages/opentelemetry/test/asyncContextStrategy.test.ts +++ b/packages/opentelemetry/test/asyncContextStrategy.test.ts @@ -7,6 +7,7 @@ import { withIsolationScope, withScope, } from '@sentry/core'; +import { describe, beforeEach, afterEach, afterAll, test, expect, it } from 'vitest'; import type { Scope } from '@sentry/core'; import { setOpenTelemetryContextAsyncContextStrategy } from '../src/asyncContextStrategy'; @@ -301,130 +302,142 @@ describe('asyncContextStrategy', () => { }); describe('withScope()', () => { - it('will make the passed scope the active scope within the callback', done => { - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - done(); - }); - }); - - it('will pass a scope that is different from the current active isolation scope', done => { - withScope(scope => { - expect(getIsolationScope()).not.toBe(scope); - done(); - }); - }); - - it('will always make the inner most passed scope the current scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will make the passed scope the active scope within the callback', () => + new Promise(done => { + withScope(scope => { + expect(getCurrentScope()).toBe(scope); done(); }); - }); - }); - - it('forks the scope when not passing any scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); - - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); - - it('forks the scope when passing undefined', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); + })); - withScope(undefined, scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); + it('will pass a scope that is different from the current active isolation scope', () => + new Promise(done => { + withScope(scope => { + expect(getIsolationScope()).not.toBe(scope); + done(); + }); + })); + + it('will always make the inner most passed scope the current scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); + + it('forks the scope when not passing any scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); + + it('forks the scope when passing undefined', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(undefined, scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); - it('sets the passed in scope as active scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); + it('sets the passed in scope as active scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); - const customScope = new ScopeClass(); + const customScope = new ScopeClass(); - withScope(customScope, scope => { - expect(getCurrentScope()).toBe(customScope); - expect(scope).toBe(customScope); - done(); - }); - }); + withScope(customScope, scope => { + expect(getCurrentScope()).toBe(customScope); + expect(scope).toBe(customScope); + done(); + }); + })); }); describe('withIsolationScope()', () => { - it('will make the passed isolation scope the active isolation scope within the callback', done => { - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - done(); - }); - }); - - it('will pass an isolation scope that is different from the current active scope', done => { - withIsolationScope(scope => { - expect(getCurrentScope()).not.toBe(scope); - done(); - }); - }); - - it('will always make the inner most passed scope the current scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will make the passed isolation scope the active isolation scope within the callback', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); done(); }); - }); - }); - - it('forks the isolation scope when not passing any isolation scope', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); - - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); - - it('forks the isolation scope when passing undefined', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); + })); - withIsolationScope(undefined, scope => { - expect(getIsolationScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); + it('will pass an isolation scope that is different from the current active scope', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getCurrentScope()).not.toBe(scope); + done(); + }); + })); + + it('will always make the inner most passed scope the current scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); + + it('forks the isolation scope when not passing any isolation scope', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); + + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); + + it('forks the isolation scope when passing undefined', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); + + withIsolationScope(undefined, scope => { + expect(getIsolationScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); - it('sets the passed in isolation scope as active isolation scope', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); + it('sets the passed in isolation scope as active isolation scope', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); - const customScope = new ScopeClass(); + const customScope = new ScopeClass(); - withIsolationScope(customScope, scope => { - expect(getIsolationScope()).toBe(customScope); - expect(scope).toBe(customScope); - done(); - }); - }); + withIsolationScope(customScope, scope => { + expect(getIsolationScope()).toBe(customScope); + expect(scope).toBe(customScope); + done(); + }); + })); }); }); diff --git a/packages/opentelemetry/test/custom/client.test.ts b/packages/opentelemetry/test/custom/client.test.ts index 8b4005c199d0..7be1313dea45 100644 --- a/packages/opentelemetry/test/custom/client.test.ts +++ b/packages/opentelemetry/test/custom/client.test.ts @@ -1,4 +1,5 @@ import { ProxyTracer } from '@opentelemetry/api'; +import { describe, expect, it } from 'vitest'; import { TestClient, getDefaultTestClientOptions } from '../helpers/TestClient'; diff --git a/packages/opentelemetry/test/helpers/mockSdkInit.ts b/packages/opentelemetry/test/helpers/mockSdkInit.ts index 869d4c82446f..0bcf1b101896 100644 --- a/packages/opentelemetry/test/helpers/mockSdkInit.ts +++ b/packages/opentelemetry/test/helpers/mockSdkInit.ts @@ -1,12 +1,14 @@ import { ProxyTracerProvider, context, propagation, trace } from '@opentelemetry/api'; import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import type { ClientOptions, Options } from '@sentry/core'; +import { getClient } from '@sentry/core'; -import { getCurrentScope, getGlobalScope, getIsolationScope } from '@sentry/core'; +import { getCurrentScope, getGlobalScope, getIsolationScope, flush } from '@sentry/core'; import { setOpenTelemetryContextAsyncContextStrategy } from '../../src/asyncContextStrategy'; import { clearOpenTelemetrySetupCheck } from '../../src/utils/setupCheck'; import { init as initTestClient } from './TestClient'; import { initOtel } from './initOtel'; +import type { OpenTelemetryClient } from '../../src/types'; const PUBLIC_DSN = 'https://username@domain/123'; @@ -24,6 +26,7 @@ function resetGlobals(): void { getCurrentScope().setClient(undefined); getIsolationScope().clear(); getGlobalScope().clear(); + delete (global as any).__SENTRY__; } export function mockSdkInit(options?: Partial) { @@ -32,25 +35,26 @@ export function mockSdkInit(options?: Partial) { init({ dsn: PUBLIC_DSN, ...options }); } -export function cleanupOtel(_provider?: BasicTracerProvider): void { +export async function cleanupOtel(_provider?: BasicTracerProvider): Promise { clearOpenTelemetrySetupCheck(); + const provider = getProvider(_provider); - if (!provider) { - return; + if (provider) { + await provider.forceFlush(); + await provider.shutdown(); } - void provider.forceFlush(); - void provider.shutdown(); - // Disable all globally registered APIs trace.disable(); context.disable(); propagation.disable(); + + await flush(); } export function getProvider(_provider?: BasicTracerProvider): BasicTracerProvider | undefined { - let provider = _provider || trace.getTracerProvider(); + let provider = _provider || getClient()?.traceProvider || trace.getTracerProvider(); if (provider instanceof ProxyTracerProvider) { provider = provider.getDelegate(); diff --git a/packages/opentelemetry/test/integration/breadcrumbs.test.ts b/packages/opentelemetry/test/integration/breadcrumbs.test.ts index aef5149b8920..247cb55094b1 100644 --- a/packages/opentelemetry/test/integration/breadcrumbs.test.ts +++ b/packages/opentelemetry/test/integration/breadcrumbs.test.ts @@ -1,20 +1,21 @@ import { addBreadcrumb, captureException, getClient, withIsolationScope, withScope } from '@sentry/core'; +import { describe, afterEach, expect, it, vi } from 'vitest'; import { startSpan } from '../../src/trace'; import type { TestClientInterface } from '../helpers/TestClient'; import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit'; describe('Integration | breadcrumbs', () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); describe('without tracing', () => { it('correctly adds & retrieves breadcrumbs', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb }); @@ -49,8 +50,8 @@ describe('Integration | breadcrumbs', () => { }); it('handles parallel isolation scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb }); @@ -95,8 +96,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -140,8 +141,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs for the current isolation scope only', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -192,8 +193,8 @@ describe('Integration | breadcrumbs', () => { }); it('ignores scopes inside of root span', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -233,8 +234,8 @@ describe('Integration | breadcrumbs', () => { }); it('handles deep nesting of scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -291,8 +292,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs in async isolation scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); diff --git a/packages/opentelemetry/test/integration/scope.test.ts b/packages/opentelemetry/test/integration/scope.test.ts index c984fdc2b189..f741c29804e0 100644 --- a/packages/opentelemetry/test/integration/scope.test.ts +++ b/packages/opentelemetry/test/integration/scope.test.ts @@ -8,14 +8,15 @@ import { withIsolationScope, withScope, } from '@sentry/core'; +import { describe, afterEach, expect, it, vi } from 'vitest'; import { startSpan } from '../../src/trace'; import type { TestClientInterface } from '../helpers/TestClient'; import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit'; describe('Integration | Scope', () => { - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); describe.each([ @@ -23,8 +24,8 @@ describe('Integration | Scope', () => { ['without tracing', false], ])('%s', (_name, tracingEnabled) => { it('correctly syncs OTEL context & Sentry hub/scope', async () => { - const beforeSend = jest.fn(() => null); - const beforeSendTransaction = jest.fn(() => null); + const beforeSend = vi.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: tracingEnabled ? 1 : 0, @@ -141,8 +142,8 @@ describe('Integration | Scope', () => { }); it('isolates parallel scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeSendTransaction = jest.fn(() => null); + const beforeSend = vi.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: tracingEnabled ? 1 : 0, beforeSend, beforeSendTransaction }); @@ -259,8 +260,8 @@ describe('Integration | Scope', () => { }); it('isolates parallel isolation scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeSendTransaction = jest.fn(() => null); + const beforeSend = vi.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: tracingEnabled ? 1 : 0, beforeSend, beforeSendTransaction }); diff --git a/packages/opentelemetry/test/integration/transactions.test.ts b/packages/opentelemetry/test/integration/transactions.test.ts index fa2705dec961..a745b9187e57 100644 --- a/packages/opentelemetry/test/integration/transactions.test.ts +++ b/packages/opentelemetry/test/integration/transactions.test.ts @@ -13,6 +13,7 @@ import { } from '@sentry/core'; import { logger } from '@sentry/core'; import type { Event, TransactionEvent } from '@sentry/core'; +import { describe, afterEach, expect, it, vi } from 'vitest'; import { TraceState } from '@opentelemetry/core'; import { SENTRY_TRACE_STATE_DSC } from '../../src/constants'; @@ -23,15 +24,15 @@ import type { TestClientInterface } from '../helpers/TestClient'; import { cleanupOtel, getProvider, mockSdkInit } from '../helpers/mockSdkInit'; describe('Integration | Transactions', () => { - afterEach(() => { - jest.restoreAllMocks(); - jest.useRealTimers(); - cleanupOtel(); + afterEach(async () => { + vi.restoreAllMocks(); + vi.useRealTimers(); + await cleanupOtel(); }); it('correctly creates transaction & spans', async () => { const transactions: TransactionEvent[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); @@ -177,7 +178,7 @@ describe('Integration | Transactions', () => { }); it('correctly creates concurrent transaction & spans', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); @@ -322,7 +323,7 @@ describe('Integration | Transactions', () => { }); it('correctly creates transaction & spans with a trace header data', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const traceId = 'd4cda95b652f4a1592b449d5929fda1b'; const parentSpanId = '6e0c63257de34c92'; @@ -434,14 +435,14 @@ describe('Integration | Transactions', () => { }); it('cleans up spans that are not flushed for over 5 mins', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const now = Date.now(); - jest.useFakeTimers(); - jest.setSystemTime(now); + vi.useFakeTimers(); + vi.setSystemTime(now); const logs: unknown[] = []; - jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); + vi.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); @@ -478,12 +479,12 @@ describe('Integration | Transactions', () => { expect(beforeSendTransaction).toHaveBeenCalledTimes(0); // Now wait for 5 mins - jest.advanceTimersByTime(5 * 60 * 1_000 + 1); + vi.advanceTimersByTime(5 * 60 * 1_000 + 1); // Adding another span will trigger the cleanup startSpan({ name: 'other span' }, () => {}); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); // Old spans have been cleared away const finishedSpans2 = []; @@ -507,11 +508,11 @@ describe('Integration | Transactions', () => { it('includes child spans that are finished in the same tick but after their parent span', async () => { const now = Date.now(); - jest.useFakeTimers(); - jest.setSystemTime(now); + vi.useFakeTimers(); + vi.setSystemTime(now); const logs: unknown[] = []; - jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); + vi.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); const transactions: Event[] = []; @@ -547,7 +548,7 @@ describe('Integration | Transactions', () => { subSpan2.end(); }); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); expect(transactions).toHaveLength(1); expect(transactions[0]?.spans).toHaveLength(2); @@ -564,11 +565,11 @@ describe('Integration | Transactions', () => { it('discards child spans that are finished after their parent span', async () => { const now = Date.now(); - jest.useFakeTimers(); - jest.setSystemTime(now); + vi.useFakeTimers(); + vi.setSystemTime(now); const logs: unknown[] = []; - jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); + vi.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); const transactions: Event[] = []; @@ -607,7 +608,7 @@ describe('Integration | Transactions', () => { }, 1); }); - jest.advanceTimersByTime(2); + vi.advanceTimersByTime(2); expect(transactions).toHaveLength(1); expect(transactions[0]?.spans).toHaveLength(1); @@ -625,7 +626,7 @@ describe('Integration | Transactions', () => { it('uses & inherits DSC on span trace state', async () => { const transactionEvents: Event[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactionEvents.push(event); return null; }); diff --git a/packages/opentelemetry/test/propagator.test.ts b/packages/opentelemetry/test/propagator.test.ts index 48b6372e65a0..971cc610fb44 100644 --- a/packages/opentelemetry/test/propagator.test.ts +++ b/packages/opentelemetry/test/propagator.test.ts @@ -9,6 +9,7 @@ import { } from '@opentelemetry/api'; import { suppressTracing } from '@opentelemetry/core'; import { getCurrentScope, withScope } from '@sentry/core'; +import { describe, beforeEach, afterEach, expect, it } from 'vitest'; import { SENTRY_BAGGAGE_HEADER, SENTRY_SCOPES_CONTEXT_KEY, SENTRY_TRACE_HEADER } from '../src/constants'; import { SentryPropagator } from '../src/propagator'; @@ -30,8 +31,8 @@ describe('SentryPropagator', () => { }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('returns fields set', () => { diff --git a/packages/opentelemetry/test/sampler.test.ts b/packages/opentelemetry/test/sampler.test.ts index 585e3271a93a..984a65a50a04 100644 --- a/packages/opentelemetry/test/sampler.test.ts +++ b/packages/opentelemetry/test/sampler.test.ts @@ -3,19 +3,21 @@ import { TraceState } from '@opentelemetry/core'; import { SamplingDecision } from '@opentelemetry/sdk-trace-base'; import { ATTR_HTTP_REQUEST_METHOD } from '@opentelemetry/semantic-conventions'; import { generateSpanId, generateTraceId } from '@sentry/core'; +import { describe, afterEach, vi, expect, it } from 'vitest'; + import { SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING } from '../src/constants'; import { SentrySampler } from '../src/sampler'; import { TestClient, getDefaultTestClientOptions } from './helpers/TestClient'; import { cleanupOtel } from './helpers/mockSdkInit'; describe('SentrySampler', () => { - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('works with tracesSampleRate=0', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: 0 })); - const spyOnDroppedEvent = jest.spyOn(client, 'recordDroppedEvent'); + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); const ctx = context.active(); @@ -42,7 +44,7 @@ describe('SentrySampler', () => { it('works with tracesSampleRate=0 & for a child span', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: 0 })); - const spyOnDroppedEvent = jest.spyOn(client, 'recordDroppedEvent'); + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); const traceId = generateTraceId(); @@ -69,7 +71,7 @@ describe('SentrySampler', () => { it('works with tracesSampleRate=1', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: 1 })); - const spyOnDroppedEvent = jest.spyOn(client, 'recordDroppedEvent'); + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); const ctx = context.active(); @@ -92,7 +94,7 @@ describe('SentrySampler', () => { it('works with traceSampleRate=undefined', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: undefined })); - const spyOnDroppedEvent = jest.spyOn(client, 'recordDroppedEvent'); + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); const ctx = context.active(); @@ -114,7 +116,7 @@ describe('SentrySampler', () => { it('ignores local http client root spans', () => { const client = new TestClient(getDefaultTestClientOptions({ tracesSampleRate: 0 })); - const spyOnDroppedEvent = jest.spyOn(client, 'recordDroppedEvent'); + const spyOnDroppedEvent = vi.spyOn(client, 'recordDroppedEvent'); const sampler = new SentrySampler(client); const ctx = context.active(); diff --git a/packages/opentelemetry/test/spanExporter.test.ts b/packages/opentelemetry/test/spanExporter.test.ts index c8052bbad2a3..e192f77a5f41 100644 --- a/packages/opentelemetry/test/spanExporter.test.ts +++ b/packages/opentelemetry/test/spanExporter.test.ts @@ -1,5 +1,7 @@ import { ATTR_HTTP_RESPONSE_STATUS_CODE } from '@opentelemetry/semantic-conventions'; import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, startInactiveSpan, startSpanManual } from '@sentry/core'; +import { describe, afterEach, beforeEach, expect, it } from 'vitest'; + import { createTransactionForOtelSpan } from '../src/spanExporter'; import { cleanupOtel, mockSdkInit } from './helpers/mockSdkInit'; @@ -10,8 +12,8 @@ describe('createTransactionForOtelSpan', () => { }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('works with a basic span', () => { diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts index 93d5fa448dda..a820e7502929 100644 --- a/packages/opentelemetry/test/trace.test.ts +++ b/packages/opentelemetry/test/trace.test.ts @@ -21,6 +21,7 @@ import { withScope, } from '@sentry/core'; import type { Event, Scope } from '@sentry/core'; +import { describe, afterEach, beforeEach, expect, it, vi } from 'vitest'; import { SEMATTRS_HTTP_METHOD } from '@opentelemetry/semantic-conventions'; import { continueTrace, startInactiveSpan, startSpan, startSpanManual } from '../src/trace'; @@ -37,8 +38,8 @@ describe('trace', () => { mockSdkInit({ tracesSampleRate: 1 }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); describe('startSpan', () => { @@ -843,7 +844,7 @@ describe('trace', () => { }); it('includes the scope at the time the span was started when finished', async () => { - const beforeSendTransaction = jest.fn(event => event); + const beforeSendTransaction = vi.fn(event => event); const client = getClient()!; @@ -1351,8 +1352,8 @@ describe('trace (tracing disabled)', () => { mockSdkInit({ tracesSampleRate: 0 }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('startSpan calls callback without span', () => { @@ -1375,13 +1376,13 @@ describe('trace (tracing disabled)', () => { }); describe('trace (sampling)', () => { - afterEach(() => { - cleanupOtel(); - jest.clearAllMocks(); + afterEach(async () => { + await cleanupOtel(); + vi.clearAllMocks(); }); it('samples with a tracesSampleRate, when Math.random() > tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); mockSdkInit({ tracesSampleRate: 0.5 }); @@ -1397,7 +1398,7 @@ describe('trace (sampling)', () => { }); it('samples with a tracesSampleRate, when Math.random() < tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.4); + vi.spyOn(Math, 'random').mockImplementation(() => 0.4); mockSdkInit({ tracesSampleRate: 0.5 }); @@ -1416,7 +1417,7 @@ describe('trace (sampling)', () => { }); it('positive parent sampling takes precedence over tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); mockSdkInit({ tracesSampleRate: 1 }); @@ -1440,7 +1441,7 @@ describe('trace (sampling)', () => { }); it('negative parent sampling takes precedence over tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); mockSdkInit({ tracesSampleRate: 0.5 }); @@ -1462,7 +1463,7 @@ describe('trace (sampling)', () => { }); it('positive remote parent sampling takes precedence over tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); mockSdkInit({ tracesSampleRate: 0.5 }); @@ -1489,7 +1490,7 @@ describe('trace (sampling)', () => { }); it('negative remote parent sampling takes precedence over tracesSampleRate', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); mockSdkInit({ tracesSampleRate: 0.5 }); @@ -1517,7 +1518,7 @@ describe('trace (sampling)', () => { it('samples with a tracesSampler returning a boolean', () => { let tracesSamplerResponse: boolean = true; - const tracesSampler = jest.fn(() => { + const tracesSampler = vi.fn(() => { return tracesSamplerResponse; }); @@ -1569,11 +1570,11 @@ describe('trace (sampling)', () => { }); it('samples with a tracesSampler returning a number', () => { - jest.spyOn(Math, 'random').mockImplementation(() => 0.6); + vi.spyOn(Math, 'random').mockImplementation(() => 0.6); let tracesSamplerResponse: number = 1; - const tracesSampler = jest.fn(() => { + const tracesSampler = vi.fn(() => { return tracesSamplerResponse; }); @@ -1646,7 +1647,7 @@ describe('trace (sampling)', () => { }); it('samples with a tracesSampler even if parent is remotely sampled', () => { - const tracesSampler = jest.fn(() => { + const tracesSampler = vi.fn(() => { return false; }); @@ -1706,8 +1707,8 @@ describe('HTTP methods (sampling)', () => { mockSdkInit({ tracesSampleRate: 1 }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('does sample when HTTP method is other than OPTIONS or HEAD', () => { @@ -1761,8 +1762,8 @@ describe('continueTrace', () => { mockSdkInit({ tracesSampleRate: 1 }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('works without trace & baggage data', () => { @@ -1863,8 +1864,8 @@ describe('suppressTracing', () => { mockSdkInit({ tracesSampleRate: 1 }); }); - afterEach(() => { - cleanupOtel(); + afterEach(async () => { + await cleanupOtel(); }); it('works for a root span', () => { diff --git a/packages/opentelemetry/test/utils/getActiveSpan.test.ts b/packages/opentelemetry/test/utils/getActiveSpan.test.ts index b4e0efd32cd0..964c75bc4ed9 100644 --- a/packages/opentelemetry/test/utils/getActiveSpan.test.ts +++ b/packages/opentelemetry/test/utils/getActiveSpan.test.ts @@ -1,6 +1,7 @@ import { trace } from '@opentelemetry/api'; import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import { getRootSpan } from '@sentry/core'; +import { describe, afterEach, beforeEach, expect, it } from 'vitest'; import { getActiveSpan } from '../../src/utils/getActiveSpan'; import { TestClient, getDefaultTestClientOptions } from '../helpers/TestClient'; diff --git a/packages/opentelemetry/test/utils/getRequestSpanData.test.ts b/packages/opentelemetry/test/utils/getRequestSpanData.test.ts index da0066758534..72b64a307c99 100644 --- a/packages/opentelemetry/test/utils/getRequestSpanData.test.ts +++ b/packages/opentelemetry/test/utils/getRequestSpanData.test.ts @@ -1,5 +1,6 @@ /* eslint-disable deprecation/deprecation */ import { SEMATTRS_HTTP_METHOD, SEMATTRS_HTTP_URL } from '@opentelemetry/semantic-conventions'; +import { describe, expect, it } from 'vitest'; import { getRequestSpanData } from '../../src/utils/getRequestSpanData'; import { createSpan } from '../helpers/createSpan'; diff --git a/packages/opentelemetry/test/utils/getSpanKind.test.ts b/packages/opentelemetry/test/utils/getSpanKind.test.ts index 50e57ee4fac7..66dbd14d3866 100644 --- a/packages/opentelemetry/test/utils/getSpanKind.test.ts +++ b/packages/opentelemetry/test/utils/getSpanKind.test.ts @@ -1,5 +1,6 @@ import type { Span } from '@opentelemetry/api'; import { SpanKind } from '@opentelemetry/api'; +import { describe, expect, it } from 'vitest'; import { getSpanKind } from '../../src/utils/getSpanKind'; diff --git a/packages/opentelemetry/test/utils/getTraceData.test.ts b/packages/opentelemetry/test/utils/getTraceData.test.ts index cde519143e26..815354017701 100644 --- a/packages/opentelemetry/test/utils/getTraceData.test.ts +++ b/packages/opentelemetry/test/utils/getTraceData.test.ts @@ -1,5 +1,7 @@ import { context, trace } from '@opentelemetry/api'; import { getCurrentScope, setAsyncContextStrategy } from '@sentry/core'; +import { describe, afterEach, beforeEach, expect, it, vi } from 'vitest'; + import { getTraceData } from '../../src/utils/getTraceData'; import { makeTraceState } from '../../src/utils/makeTraceState'; import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit'; @@ -10,9 +12,9 @@ describe('getTraceData', () => { mockSdkInit(); }); - afterEach(() => { - cleanupOtel(); - jest.clearAllMocks(); + afterEach(async () => { + await cleanupOtel(); + vi.clearAllMocks(); }); it('returns the tracing data from the span, if a span is available', () => { diff --git a/packages/opentelemetry/test/utils/groupSpansWithParents.test.ts b/packages/opentelemetry/test/utils/groupSpansWithParents.test.ts index d9a3fa60cb97..f1bea09bd2f5 100644 --- a/packages/opentelemetry/test/utils/groupSpansWithParents.test.ts +++ b/packages/opentelemetry/test/utils/groupSpansWithParents.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { groupSpansWithParents } from '../../src/utils/groupSpansWithParents'; import { createSpan } from '../helpers/createSpan'; diff --git a/packages/opentelemetry/test/utils/mapStatus.test.ts b/packages/opentelemetry/test/utils/mapStatus.test.ts index 79ffdf057b5a..83d7548aa3ad 100644 --- a/packages/opentelemetry/test/utils/mapStatus.test.ts +++ b/packages/opentelemetry/test/utils/mapStatus.test.ts @@ -2,6 +2,7 @@ import { SEMATTRS_HTTP_STATUS_CODE, SEMATTRS_RPC_GRPC_STATUS_CODE } from '@opentelemetry/semantic-conventions'; import { SPAN_STATUS_ERROR, SPAN_STATUS_OK } from '@sentry/core'; import type { SpanStatus } from '@sentry/core'; +import { describe, expect, it } from 'vitest'; import { mapStatus } from '../../src/utils/mapStatus'; import { createSpan } from '../helpers/createSpan'; diff --git a/packages/opentelemetry/test/utils/parseSpanDescription.test.ts b/packages/opentelemetry/test/utils/parseSpanDescription.test.ts index d43dfcd9f587..61e7ba7aada9 100644 --- a/packages/opentelemetry/test/utils/parseSpanDescription.test.ts +++ b/packages/opentelemetry/test/utils/parseSpanDescription.test.ts @@ -14,6 +14,7 @@ import { SEMATTRS_MESSAGING_SYSTEM, SEMATTRS_RPC_SERVICE, } from '@opentelemetry/semantic-conventions'; +import { describe, expect, it } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; import { diff --git a/packages/opentelemetry/test/utils/setupCheck.test.ts b/packages/opentelemetry/test/utils/setupCheck.test.ts index 8ac8bcb8e844..53d8437e119a 100644 --- a/packages/opentelemetry/test/utils/setupCheck.test.ts +++ b/packages/opentelemetry/test/utils/setupCheck.test.ts @@ -1,4 +1,5 @@ import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; +import { describe, afterEach, beforeEach, expect, it } from 'vitest'; import { SentrySampler } from '../../src/sampler'; import { SentrySpanProcessor } from '../../src/spanProcessor'; diff --git a/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts b/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts index 34c41ad0ef7f..21f7279fd48d 100644 --- a/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts +++ b/packages/opentelemetry/test/utils/setupEventContextTrace.test.ts @@ -1,5 +1,6 @@ import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; import { captureException, setCurrentClient } from '@sentry/core'; +import { describe, afterEach, beforeEach, expect, it, vi, afterAll } from 'vitest'; import { setupEventContextTrace } from '../../src/setupEventContextTrace'; import type { TestClientInterface } from '../helpers/TestClient'; @@ -10,7 +11,7 @@ import { cleanupOtel } from '../helpers/mockSdkInit'; const PUBLIC_DSN = 'https://username@domain/123'; describe('setupEventContextTrace', () => { - const beforeSend = jest.fn(() => null); + const beforeSend = vi.fn(() => null); let client: TestClientInterface; let provider: BasicTracerProvider | undefined; @@ -38,7 +39,7 @@ describe('setupEventContextTrace', () => { }); afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works with no active span', async () => { diff --git a/packages/opentelemetry/test/utils/spanToJSON.test.ts b/packages/opentelemetry/test/utils/spanToJSON.test.ts index 103ede3eaafd..a56d1ec6d240 100644 --- a/packages/opentelemetry/test/utils/spanToJSON.test.ts +++ b/packages/opentelemetry/test/utils/spanToJSON.test.ts @@ -1,4 +1,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, spanToJSON } from '@sentry/core'; +import { describe, expect, it } from 'vitest'; + import { createSpan } from '../helpers/createSpan'; describe('spanToJSON', () => { diff --git a/packages/opentelemetry/test/utils/spanTypes.test.ts b/packages/opentelemetry/test/utils/spanTypes.test.ts index af07e5c45af5..1849f24ce412 100644 --- a/packages/opentelemetry/test/utils/spanTypes.test.ts +++ b/packages/opentelemetry/test/utils/spanTypes.test.ts @@ -1,4 +1,5 @@ import type { Span } from '@opentelemetry/api'; +import { describe, expect, it } from 'vitest'; import { spanHasAttributes, spanHasEvents, spanHasKind, spanHasParentId } from '../../src/utils/spanTypes'; diff --git a/packages/opentelemetry/tsconfig.test.json b/packages/opentelemetry/tsconfig.test.json index 87f6afa06b86..ca7dbeb3be94 100644 --- a/packages/opentelemetry/tsconfig.test.json +++ b/packages/opentelemetry/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/opentelemetry/vite.config.ts b/packages/opentelemetry/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/opentelemetry/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +};