Skip to content

Commit bae7fb6

Browse files
committed
fix(test-utils): Filter live events by listener registration time
The timestamp filter only applied to BUFFERED events. Live events arriving after a listener registered were sent to ALL listeners without any timestamp check. This caused stale events from previous tests to leak through if the event was still in-flight when the next test started listening. Fix: Track each listener's registration timestamp and filter live events the same way as buffered events - only send events that occurred AFTER the listener was registered.
1 parent 0a8c255 commit bae7fb6

File tree

1 file changed

+24
-10
lines changed

1 file changed

+24
-10
lines changed

dev-packages/test-utils/src/event-proxy-server.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ interface SentryRequestCallbackData {
3030
}
3131

3232
interface EventCallbackListener {
33-
(data: string): void;
33+
callback: (data: string) => void;
34+
/** Timestamp when this listener was registered. Only events after this time should be sent. */
35+
registeredAt: number;
3436
}
3537

3638
type SentryResponseStatusCode = number;
@@ -91,10 +93,15 @@ export async function startProxyServer(
9193
const callback: OnRequest =
9294
onRequest ||
9395
(async (eventCallbackListeners, proxyRequest, proxyRequestBody, eventBuffer) => {
94-
eventBuffer.push({ data: proxyRequestBody, timestamp: getTimestamp() });
96+
const eventTimestamp = getTimestamp();
97+
eventBuffer.push({ data: proxyRequestBody, timestamp: eventTimestamp });
9598

99+
// Only send to listeners that registered BEFORE this event was received.
100+
// This prevents stale events from previous tests leaking to newer listeners.
96101
eventCallbackListeners.forEach(listener => {
97-
listener(proxyRequestBody);
102+
if (eventTimestamp > listener.registeredAt) {
103+
listener.callback(proxyRequestBody);
104+
}
98105
});
99106

100107
return [200, '{}', {}];
@@ -132,19 +139,21 @@ export async function startProxyServer(
132139
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
133140
const listenerTimestamp = Number(searchParams.get('timestamp')!);
134141

135-
const callbackListener = (data: string): void => {
136-
eventCallbackResponse.write(data.concat('\n'), 'utf8');
142+
const callbackListener: EventCallbackListener = {
143+
callback: (data: string): void => {
144+
eventCallbackResponse.write(data.concat('\n'), 'utf8');
145+
},
146+
registeredAt: listenerTimestamp,
137147
};
138148

139149
eventCallbackListeners.add(callbackListener);
140150

141151
// Use strict inequality to prevent stale events from previous tests leaking through.
142152
// If a previous test's event was buffered at the same millisecond as this listener started,
143-
// we want to exclude it. New events arriving after the listener is registered go through
144-
// the callback directly (not the buffer), so they're not affected by this filter.
153+
// we want to exclude it.
145154
eventBuffer.forEach(bufferedEvent => {
146155
if (bufferedEvent.timestamp > listenerTimestamp) {
147-
callbackListener(bufferedEvent.data);
156+
callbackListener.callback(bufferedEvent.data);
148157
}
149158
});
150159

@@ -194,10 +203,15 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
194203

195204
const dataString = Buffer.from(JSON.stringify(data)).toString('base64');
196205

197-
eventBuffer.push({ data: dataString, timestamp: getTimestamp() });
206+
const eventTimestamp = getTimestamp();
207+
eventBuffer.push({ data: dataString, timestamp: eventTimestamp });
198208

209+
// Only send to listeners that registered BEFORE this event was received.
210+
// This prevents stale events from previous tests leaking to newer listeners.
199211
eventCallbackListeners.forEach(listener => {
200-
listener(dataString);
212+
if (eventTimestamp > listener.registeredAt) {
213+
listener.callback(dataString);
214+
}
201215
});
202216

203217
if (options.envelopeDumpPath) {

0 commit comments

Comments
 (0)