Skip to content

Commit bf6f465

Browse files
committed
fix(tests): use vi.mock/mock.module for metrics namespace mocking
Rolldown emits Object.defineProperty getters for `export * as` namespace re-exports in CJS, making them non-configurable and breaking spyOn.
1 parent 21ae629 commit bf6f465

5 files changed

Lines changed: 116 additions & 101 deletions

File tree

packages/browser-utils/test/metrics/elementTiming.test.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import * as sentryCore from '@sentry/core';
1+
import type * as sentryCore from '@sentry/core';
22
import { beforeEach, describe, expect, it, vi } from 'vitest';
33
import { elementTimingIntegration, startTrackingElementTiming } from '../../src/metrics/elementTiming';
44
import * as browserMetricsInstrumentation from '../../src/metrics/instrument';
55
import * as browserMetricsUtils from '../../src/metrics/utils';
66

7-
describe('elementTimingIntegration', () => {
8-
const distributionSpy = vi.spyOn(sentryCore.metrics, 'distribution');
7+
const { distributionSpy } = vi.hoisted(() => ({ distributionSpy: vi.fn() }));
8+
9+
vi.mock('@sentry/core', async () => {
10+
const actual = await vi.importActual('@sentry/core');
11+
return { ...actual, metrics: { ...actual.metrics, distribution: distributionSpy } };
12+
});
913

14+
describe('elementTimingIntegration', () => {
1015
let elementHandler: (data: { entries: PerformanceEntry[] }) => void;
1116

1217
beforeEach(() => {

packages/bun/test/integrations/bunRuntimeMetrics.test.ts

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { afterEach, beforeEach, describe, expect, it, jest, mock, spyOn } from 'bun:test';
2-
import { metrics } from '@sentry/core';
2+
3+
const mockGauge = jest.fn();
4+
const mockCount = jest.fn();
35

46
const mockElu = { idle: 700, active: 300, utilization: 0.3 };
57
const mockEluDelta = { idle: 700, active: 300, utilization: 0.3 };
@@ -12,16 +14,19 @@ mock.module('perf_hooks', () => ({
1214
performance: { eventLoopUtilization: mockEventLoopUtilization },
1315
}));
1416

17+
const actualCore = await import('@sentry/core');
18+
mock.module('@sentry/core', () => ({
19+
...actualCore,
20+
metrics: { ...actualCore.metrics, gauge: mockGauge, count: mockCount },
21+
}));
22+
1523
const { bunRuntimeMetricsIntegration } = await import('../../src/integrations/bunRuntimeMetrics');
1624

1725
describe('bunRuntimeMetricsIntegration', () => {
18-
let gaugeSpy: ReturnType<typeof spyOn>;
19-
let countSpy: ReturnType<typeof spyOn>;
20-
2126
beforeEach(() => {
2227
jest.useFakeTimers();
23-
gaugeSpy = spyOn(metrics, 'gauge').mockImplementation(() => undefined);
24-
countSpy = spyOn(metrics, 'count').mockImplementation(() => undefined);
28+
mockGauge.mockClear();
29+
mockCount.mockClear();
2530

2631
spyOn(process, 'cpuUsage').mockReturnValue({ user: 500_000, system: 200_000 });
2732
spyOn(process, 'memoryUsage').mockReturnValue({
@@ -50,9 +55,9 @@ describe('bunRuntimeMetricsIntegration', () => {
5055
const integration = bunRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 });
5156
integration.setup();
5257

53-
expect(gaugeSpy).not.toHaveBeenCalled();
58+
expect(mockGauge).not.toHaveBeenCalled();
5459
jest.advanceTimersByTime(1_000);
55-
expect(gaugeSpy).toHaveBeenCalled();
60+
expect(mockGauge).toHaveBeenCalled();
5661
});
5762

5863
it('does not throw if performance.eventLoopUtilization is unavailable', () => {
@@ -75,16 +80,16 @@ describe('bunRuntimeMetricsIntegration', () => {
7580
integration.setup();
7681
jest.advanceTimersByTime(1_000);
7782

78-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.cpu.utilization', expect.any(Number), ORIGIN);
83+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.cpu.utilization', expect.any(Number), ORIGIN);
7984
});
8085

8186
it('does not emit cpu.user / cpu.system by default (opt-in)', () => {
8287
const integration = bunRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 });
8388
integration.setup();
8489
jest.advanceTimersByTime(1_000);
8590

86-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.cpu.user', expect.anything(), expect.anything());
87-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.cpu.system', expect.anything(), expect.anything());
91+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.cpu.user', expect.anything(), expect.anything());
92+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.cpu.system', expect.anything(), expect.anything());
8893
});
8994

9095
it('emits cpu.user / cpu.system when cpuTime is opted in', () => {
@@ -95,27 +100,27 @@ describe('bunRuntimeMetricsIntegration', () => {
95100
integration.setup();
96101
jest.advanceTimersByTime(1_000);
97102

98-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.cpu.user', expect.any(Number), SECOND);
99-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.cpu.system', expect.any(Number), SECOND);
103+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.cpu.user', expect.any(Number), SECOND);
104+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.cpu.system', expect.any(Number), SECOND);
100105
});
101106

102107
it('emits mem.rss, mem.heap_used, mem.heap_total (default on)', () => {
103108
const integration = bunRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 });
104109
integration.setup();
105110
jest.advanceTimersByTime(1_000);
106111

107-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.mem.rss', 50_000_000, BYTE);
108-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.mem.heap_used', 20_000_000, BYTE);
109-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.mem.heap_total', 30_000_000, BYTE);
112+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.mem.rss', 50_000_000, BYTE);
113+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.mem.heap_used', 20_000_000, BYTE);
114+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.mem.heap_total', 30_000_000, BYTE);
110115
});
111116

112117
it('does not emit mem.external / mem.array_buffers by default (opt-in)', () => {
113118
const integration = bunRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 });
114119
integration.setup();
115120
jest.advanceTimersByTime(1_000);
116121

117-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.mem.external', expect.anything(), expect.anything());
118-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.mem.array_buffers', expect.anything(), expect.anything());
122+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.mem.external', expect.anything(), expect.anything());
123+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.mem.array_buffers', expect.anything(), expect.anything());
119124
});
120125

121126
it('emits mem.external / mem.array_buffers when opted in', () => {
@@ -126,16 +131,16 @@ describe('bunRuntimeMetricsIntegration', () => {
126131
integration.setup();
127132
jest.advanceTimersByTime(1_000);
128133

129-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.mem.external', 1_000_000, BYTE);
130-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.mem.array_buffers', 500_000, BYTE);
134+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.mem.external', 1_000_000, BYTE);
135+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.mem.array_buffers', 500_000, BYTE);
131136
});
132137

133138
it('emits event loop utilization metric', () => {
134139
const integration = bunRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 });
135140
integration.setup();
136141
jest.advanceTimersByTime(1_000);
137142

138-
expect(gaugeSpy).toHaveBeenCalledWith('bun.runtime.event_loop.utilization', 0.3, ORIGIN);
143+
expect(mockGauge).toHaveBeenCalledWith('bun.runtime.event_loop.utilization', 0.3, ORIGIN);
139144
});
140145

141146
it('does not emit event loop utilization if performance.eventLoopUtilization threw during setup', () => {
@@ -147,7 +152,7 @@ describe('bunRuntimeMetricsIntegration', () => {
147152
integration.setup();
148153
jest.advanceTimersByTime(1_000);
149154

150-
expect(gaugeSpy).not.toHaveBeenCalledWith(
155+
expect(mockGauge).not.toHaveBeenCalledWith(
151156
'bun.runtime.event_loop.utilization',
152157
expect.anything(),
153158
expect.anything(),
@@ -159,7 +164,7 @@ describe('bunRuntimeMetricsIntegration', () => {
159164
integration.setup();
160165
jest.advanceTimersByTime(1_000);
161166

162-
expect(countSpy).toHaveBeenCalledWith('bun.runtime.process.uptime', expect.any(Number), SECOND);
167+
expect(mockCount).toHaveBeenCalledWith('bun.runtime.process.uptime', expect.any(Number), SECOND);
163168
});
164169
});
165170

@@ -172,7 +177,7 @@ describe('bunRuntimeMetricsIntegration', () => {
172177
integration.setup();
173178
jest.advanceTimersByTime(1_000);
174179

175-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.cpu.utilization', expect.anything(), expect.anything());
180+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.cpu.utilization', expect.anything(), expect.anything());
176181
});
177182

178183
it('skips mem.rss when memRss is false', () => {
@@ -183,7 +188,7 @@ describe('bunRuntimeMetricsIntegration', () => {
183188
integration.setup();
184189
jest.advanceTimersByTime(1_000);
185190

186-
expect(gaugeSpy).not.toHaveBeenCalledWith('bun.runtime.mem.rss', expect.anything(), expect.anything());
191+
expect(mockGauge).not.toHaveBeenCalledWith('bun.runtime.mem.rss', expect.anything(), expect.anything());
187192
});
188193

189194
it('skips event loop utilization when eventLoopUtilization is false', () => {
@@ -194,7 +199,7 @@ describe('bunRuntimeMetricsIntegration', () => {
194199
integration.setup();
195200
jest.advanceTimersByTime(1_000);
196201

197-
expect(gaugeSpy).not.toHaveBeenCalledWith(
202+
expect(mockGauge).not.toHaveBeenCalledWith(
198203
'bun.runtime.event_loop.utilization',
199204
expect.anything(),
200205
expect.anything(),
@@ -209,7 +214,7 @@ describe('bunRuntimeMetricsIntegration', () => {
209214
integration.setup();
210215
jest.advanceTimersByTime(1_000);
211216

212-
expect(countSpy).not.toHaveBeenCalledWith('bun.runtime.process.uptime', expect.anything(), expect.anything());
217+
expect(mockCount).not.toHaveBeenCalledWith('bun.runtime.process.uptime', expect.anything(), expect.anything());
213218
});
214219
});
215220

@@ -225,10 +230,10 @@ describe('bunRuntimeMetricsIntegration', () => {
225230

226231
// Should fire at minimum 1000ms, not at 100ms
227232
jest.advanceTimersByTime(100);
228-
expect(gaugeSpy).not.toHaveBeenCalled();
233+
expect(mockGauge).not.toHaveBeenCalled();
229234

230235
jest.advanceTimersByTime(900);
231-
expect(gaugeSpy).toHaveBeenCalled();
236+
expect(mockGauge).toHaveBeenCalled();
232237
});
233238

234239
it('falls back to default when NaN', () => {
@@ -241,10 +246,10 @@ describe('bunRuntimeMetricsIntegration', () => {
241246

242247
// Should fire at the default 30000ms, not at 1000ms
243248
jest.advanceTimersByTime(1000);
244-
expect(gaugeSpy).not.toHaveBeenCalled();
249+
expect(mockGauge).not.toHaveBeenCalled();
245250

246251
jest.advanceTimersByTime(29_000);
247-
expect(gaugeSpy).toHaveBeenCalled();
252+
expect(mockGauge).toHaveBeenCalled();
248253
});
249254
});
250255
});

packages/core/rollup.npm.config.mjs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ const settings = {
2626
: Boolean(process.env.SENTRY_BUILD_PRESERVE_MODULES),
2727
},
2828
plugins: [
29-
replacePlugin(
30-
{
31-
__SENTRY_SDK_VERSION__: JSON.stringify(packageVersion),
32-
},
33-
{
34-
preventAssignment: true,
35-
},
36-
),
29+
replacePlugin(
30+
{
31+
__SENTRY_SDK_VERSION__: JSON.stringify(packageVersion),
32+
},
33+
{
34+
preventAssignment: true,
35+
},
36+
),
3737
],
3838
},
3939
};

packages/effect/test/metrics.test.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
import { describe, expect, it } from '@effect/vitest';
2-
import * as sentryCore from '@sentry/core';
32
import * as Context from 'effect/Context';
43
import { Duration, Effect, Layer, Metric } from 'effect';
54
import { TestClock } from 'effect/testing';
65
import { afterEach, beforeEach, vi } from 'vitest';
76
import { SentryEffectMetricsLayer } from '../src/metrics';
87

9-
describe('SentryEffectMetricsLayer', () => {
10-
const mockCount = vi.fn();
11-
const mockGauge = vi.fn();
12-
const mockDistribution = vi.fn();
8+
const { mockCount, mockGauge, mockDistribution } = vi.hoisted(() => ({
9+
mockCount: vi.fn(),
10+
mockGauge: vi.fn(),
11+
mockDistribution: vi.fn(),
12+
}));
13+
14+
vi.mock('@sentry/core', async () => {
15+
const actual = await vi.importActual('@sentry/core');
16+
return {
17+
...actual,
18+
metrics: { ...actual.metrics, count: mockCount, gauge: mockGauge, distribution: mockDistribution },
19+
};
20+
});
1321

22+
describe('SentryEffectMetricsLayer', () => {
1423
beforeEach(() => {
15-
vi.spyOn(sentryCore.metrics, 'count').mockImplementation(mockCount);
16-
vi.spyOn(sentryCore.metrics, 'gauge').mockImplementation(mockGauge);
17-
vi.spyOn(sentryCore.metrics, 'distribution').mockImplementation(mockDistribution);
24+
mockCount.mockClear();
25+
mockGauge.mockClear();
26+
mockDistribution.mockClear();
1827
});
1928

2029
afterEach(() => {
2130
vi.clearAllMocks();
22-
vi.restoreAllMocks();
2331
});
2432

2533
it.effect('creates counter metrics', () =>
@@ -146,19 +154,14 @@ describe('SentryEffectMetricsLayer', () => {
146154
});
147155

148156
describe('SentryEffectMetricsLayer flushing', () => {
149-
const mockCount = vi.fn();
150-
const mockGauge = vi.fn();
151-
const mockDistribution = vi.fn();
152-
153157
beforeEach(() => {
154-
vi.spyOn(sentryCore.metrics, 'count').mockImplementation(mockCount);
155-
vi.spyOn(sentryCore.metrics, 'gauge').mockImplementation(mockGauge);
156-
vi.spyOn(sentryCore.metrics, 'distribution').mockImplementation(mockDistribution);
158+
mockCount.mockClear();
159+
mockGauge.mockClear();
160+
mockDistribution.mockClear();
157161
});
158162

159163
afterEach(() => {
160164
vi.clearAllMocks();
161-
vi.restoreAllMocks();
162165
});
163166

164167
const TestLayer = SentryEffectMetricsLayer.pipe(Layer.provideMerge(TestClock.layer()));

0 commit comments

Comments
 (0)