Skip to content

Commit d873831

Browse files
committed
fix(test-utils): Use Date.now() only for cross-process timestamps
The previous fix used performance.now() for sub-millisecond precision, but this is also process-local and can cause incorrect comparisons. If an event is buffered and a listener starts in the same millisecond but with different performance.now() fractional parts, the stale event can incorrectly pass the timestamp filter. Fix: Use only Date.now() (milliseconds since epoch). This is the only reliable cross-process timestamp. Millisecond precision is sufficient since tests have gaps of 100ms+ between transactions. Also renamed getNanosecondTimestamp to getTimestamp for accuracy.
1 parent 12481ec commit d873831

File tree

1 file changed

+21
-23
lines changed

1 file changed

+21
-23
lines changed

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

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export async function startProxyServer(
9191
const callback: OnRequest =
9292
onRequest ||
9393
(async (eventCallbackListeners, proxyRequest, proxyRequestBody, eventBuffer) => {
94-
eventBuffer.push({ data: proxyRequestBody, timestamp: getNanosecondTimestamp() });
94+
eventBuffer.push({ data: proxyRequestBody, timestamp: getTimestamp() });
9595

9696
eventCallbackListeners.forEach(listener => {
9797
listener(proxyRequestBody);
@@ -190,7 +190,7 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
190190

191191
const dataString = Buffer.from(JSON.stringify(data)).toString('base64');
192192

193-
eventBuffer.push({ data: dataString, timestamp: getNanosecondTimestamp() });
193+
eventBuffer.push({ data: dataString, timestamp: getTimestamp() });
194194

195195
eventCallbackListeners.forEach(listener => {
196196
listener(dataString);
@@ -219,7 +219,7 @@ export async function waitForPlainRequest(
219219

220220
return new Promise((resolve, reject) => {
221221
const request = http.request(
222-
`http://localhost:${eventCallbackServerPort}/?timestamp=${getNanosecondTimestamp()}`,
222+
`http://localhost:${eventCallbackServerPort}/?timestamp=${getTimestamp()}`,
223223
{},
224224
response => {
225225
let eventContents = '';
@@ -249,7 +249,7 @@ export async function waitForPlainRequest(
249249
export async function waitForRequest(
250250
proxyServerName: string,
251251
callback: (eventData: SentryRequestCallbackData) => Promise<boolean> | boolean,
252-
timestamp: number = getNanosecondTimestamp(),
252+
timestamp: number = getTimestamp(),
253253
): Promise<SentryRequestCallbackData> {
254254
const eventCallbackServerPort = await retrieveCallbackServerPort(proxyServerName);
255255

@@ -305,7 +305,7 @@ export async function waitForRequest(
305305
export function waitForEnvelopeItem(
306306
proxyServerName: string,
307307
callback: (envelopeItem: EnvelopeItem) => Promise<boolean> | boolean,
308-
timestamp: number = getNanosecondTimestamp(),
308+
timestamp: number = getTimestamp(),
309309
): Promise<EnvelopeItem> {
310310
return new Promise((resolve, reject) => {
311311
waitForRequest(
@@ -330,7 +330,7 @@ export function waitForError(
330330
proxyServerName: string,
331331
callback: (errorEvent: Event) => Promise<boolean> | boolean,
332332
): Promise<Event> {
333-
const timestamp = getNanosecondTimestamp();
333+
const timestamp = getTimestamp();
334334
return new Promise((resolve, reject) => {
335335
waitForEnvelopeItem(
336336
proxyServerName,
@@ -352,7 +352,7 @@ export function waitForSession(
352352
proxyServerName: string,
353353
callback: (session: SerializedSession) => Promise<boolean> | boolean,
354354
): Promise<SerializedSession> {
355-
const timestamp = getNanosecondTimestamp();
355+
const timestamp = getTimestamp();
356356
return new Promise((resolve, reject) => {
357357
waitForEnvelopeItem(
358358
proxyServerName,
@@ -374,7 +374,7 @@ export function waitForTransaction(
374374
proxyServerName: string,
375375
callback: (transactionEvent: Event) => Promise<boolean> | boolean,
376376
): Promise<Event> {
377-
const timestamp = getNanosecondTimestamp();
377+
const timestamp = getTimestamp();
378378
return new Promise((resolve, reject) => {
379379
waitForEnvelopeItem(
380380
proxyServerName,
@@ -410,23 +410,21 @@ async function retrieveCallbackServerPort(serverName: string): Promise<string> {
410410
}
411411

412412
/**
413-
* Get a high-resolution timestamp that is comparable across processes.
413+
* Get a timestamp that is comparable across processes.
414414
*
415-
* We use Date.now() as the base (epoch milliseconds) plus a sub-millisecond
416-
* component from performance.now() to get microsecond-level precision.
415+
* We use Date.now() which returns epoch milliseconds. This is the ONLY reliable
416+
* way to compare timestamps across different Node.js processes (the event proxy
417+
* server and Playwright test runner).
417418
*
418419
* NOTE: We cannot use process.hrtime() because it returns time relative to an
419-
* arbitrary process-local reference point. Since the event proxy server and
420-
* Playwright test runner are separate processes, their hrtime values are not
421-
* comparable, which was causing stale events from previous tests to leak into
422-
* later tests.
420+
* arbitrary process-local reference point that differs per process.
421+
*
422+
* NOTE: We cannot use performance.now() for sub-millisecond precision because
423+
* it's also process-local and can cause incorrect timestamp ordering.
424+
*
425+
* Millisecond precision is sufficient for our use case - tests typically have
426+
* gaps of hundreds of milliseconds or more between transactions.
423427
*/
424-
function getNanosecondTimestamp(): number {
425-
// Date.now() gives us epoch milliseconds (comparable across processes)
426-
// performance.now() gives us sub-millisecond precision within this process
427-
// We use the fractional part of performance.now() to add microseconds
428-
const epochMs = Date.now();
429-
const subMs = performance.now() % 1; // Get just the fractional part (0.0 to 0.999...)
430-
// Convert to nanoseconds: epoch_ms * 1e6 + sub_ms * 1e6
431-
return epochMs * 1e6 + Math.floor(subMs * 1e6);
428+
function getTimestamp(): number {
429+
return Date.now();
432430
}

0 commit comments

Comments
 (0)