Skip to content
Merged
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
Expand Up @@ -5,7 +5,6 @@ Sentry.init({
dsn: process.env.E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1,
traceLifecycle: process.env.E2E_TEST_STREAMED_SPANS === '1' ? 'stream' : 'static',
transportOptions: {
// We expect the app to send a lot of events in a short time
bufferSize: 1000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"test:assert:proxy": "pnpm test && TEST_ENV=development pnpm playwright test db-drivers",
"test:assert": "pnpm test:assert:proxy",
"test:assert:tunnel-generated": "E2E_TEST_TUNNEL_ROUTE_MODE=dynamic E2E_TEST_DSN=http://public@localhost:3031/1337 pnpm test",
"test:assert:tunnel-streamed": "E2E_TEST_TUNNEL_ROUTE_MODE=dynamic E2E_TEST_DSN=http://public@localhost:3031/1337 E2E_TEST_STREAMED_SPANS=1 pnpm test",
"test:assert:tunnel-static": "E2E_TEST_TUNNEL_ROUTE_MODE=static E2E_TEST_DSN=http://public@localhost:3031/1337 pnpm test",
"test:assert:tunnel-custom": "E2E_TEST_CUSTOM_TUNNEL_ROUTE=1 pnpm test",
"test:assert:tunnel-object": "E2E_TEST_TUNNEL_ROUTE_MODE=object E2E_TEST_DSN=http://public@localhost:3031/1337 pnpm test"
Expand Down Expand Up @@ -57,11 +56,6 @@
"build-command": "pnpm test:build:tunnel-generated",
"assert-command": "pnpm test:assert:tunnel-generated"
},
{
"label": "tanstackstart-react (tunnel-streamed)",
"build-command": "pnpm test:build:tunnel-generated",
"assert-command": "pnpm test:assert:tunnel-streamed"
},
{
"label": "tanstackstart-react (tunnel-static)",
"build-command": "pnpm test:build:tunnel-static",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Sentry from '@sentry/tanstackstart-react';

Sentry.init({
traceLifecycle: 'static',
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: __APP_DSN__,
tracesSampleRate: 1.0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { collectStreamedSpans, getSpanOp } from '@sentry-internal/test-utils';

const usesManagedTunnelRoute =
(process.env.E2E_TEST_TUNNEL_ROUTE_MODE ?? 'off') !== 'off' || process.env.E2E_TEST_CUSTOM_TUNNEL_ROUTE === '1';
Expand All @@ -10,89 +10,104 @@ test.skip(usesManagedTunnelRoute, 'Default e2e suites run only in the proxy vari
// `sentryTanstackStart()` auto-wires into the server bundle, and the runtime hook in `vite dev`,
// where the drivers stay external on Node's own loader.
test('Instruments ioredis automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
return (
transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-ioredis'
);
});
const spansPromise = collectStreamedSpans(
'tanstackstart-react',
spans =>
spans.some(
span =>
span.is_segment &&
getSpanOp(span) === 'http.server' &&
String(span.attributes['url.path']?.value ?? '').includes('db-ioredis'),
) &&
spans.some(span => span.attributes['db.query.text']?.value === 'set test-key [1 other arguments]') &&
spans.some(span => span.attributes['db.query.text']?.value === 'get test-key'),
);

await fetch(`${baseURL}/api/db-ioredis`);

const transactionEvent = await transactionEventPromise;

const spans = transactionEvent.spans || [];
const spans = await spansPromise;
// ioredis also emits handshake commands (SETINFO, INFO) as db.query spans.
const redisSpans = spans.filter(
span =>
getSpanOp(span) === 'db.query' &&
(span.attributes['db.operation.name']?.value === 'set' || span.attributes['db.operation.name']?.value === 'get'),
);

expect(spans).toContainEqual(
expect(redisSpans).toHaveLength(2);
expect(redisSpans).toContainEqual(
expect.objectContaining({
op: 'db.query',
origin: 'auto.db.redis',
description: 'set test-key [1 other arguments]',
name: 'set localhost:6379',
status: 'ok',
data: expect.objectContaining({
'db.system.name': 'redis',
'db.operation.name': 'set',
'db.query.text': 'set test-key [1 other arguments]',
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'db.query' },
'sentry.origin': { type: 'string', value: 'auto.db.redis' },
'db.system.name': { type: 'string', value: 'redis' },
'db.operation.name': { type: 'string', value: 'set' },
'db.query.text': { type: 'string', value: 'set test-key [1 other arguments]' },
}),
}),
);
expect(spans).toContainEqual(
expect(redisSpans).toContainEqual(
expect.objectContaining({
op: 'db.query',
origin: 'auto.db.redis',
description: 'get test-key',
name: 'get localhost:6379',
status: 'ok',
data: expect.objectContaining({
'db.system.name': 'redis',
'db.operation.name': 'get',
'db.query.text': 'get test-key',
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'db.query' },
'sentry.origin': { type: 'string', value: 'auto.db.redis' },
'db.system.name': { type: 'string', value: 'redis' },
'db.operation.name': { type: 'string', value: 'get' },
'db.query.text': { type: 'string', value: 'get test-key' },
}),
}),
);
});

test('Instruments mysql automatically', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
return (
transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.transaction === 'GET /api/db-mysql'
);
});
const spansPromise = collectStreamedSpans(
'tanstackstart-react',
spans =>
spans.some(
span =>
span.is_segment &&
getSpanOp(span) === 'http.server' &&
String(span.attributes['url.path']?.value ?? '').includes('db-mysql'),
) &&
spans.some(span => span.attributes['db.query.text']?.value === 'SELECT ? + ? AS solution') &&
spans.some(span => span.attributes['db.query.text']?.value === 'SELECT NOW()'),
);

await fetch(`${baseURL}/api/db-mysql`);

const transactionEvent = await transactionEventPromise;
const spans = await spansPromise;
const mysqlSpans = spans.filter(span => span.attributes['sentry.origin']?.value === 'auto.db.mysql');

const spans = transactionEvent.spans || [];
const firstQuery = mysqlSpans.find(span => span.attributes['db.query.text']?.value === 'SELECT ? + ? AS solution');
expect(firstQuery).toBeDefined();
expect(firstQuery!.name).toBe('SELECT');
expect(firstQuery!.status).toBe('ok');
expect(firstQuery!.attributes).toMatchObject({
'sentry.op': { type: 'string', value: 'db' },
'sentry.origin': { type: 'string', value: 'auto.db.mysql' },
'db.system.name': { type: 'string', value: 'mysql' },
'db.query.text': { type: 'string', value: 'SELECT ? + ? AS solution' },
'db.user': { type: 'string', value: 'root' },
'db.connection_string': { type: 'string', value: expect.any(String) },
'server.address': { type: 'string', value: expect.any(String) },
'server.port': { type: 'integer', value: 3306 },
});

expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.mysql',
description: 'SELECT ? + ? AS solution',
status: 'ok',
data: expect.objectContaining({
'db.system.name': 'mysql',
'db.query.text': 'SELECT ? + ? AS solution',
'db.user': 'root',
'db.connection_string': expect.any(String),
'server.address': expect.any(String),
'server.port': 3306,
}),
}),
);
expect(spans).toContainEqual(
expect.objectContaining({
op: 'db',
origin: 'auto.db.mysql',
description: 'SELECT NOW()',
status: 'ok',
data: expect.objectContaining({
'db.system.name': 'mysql',
'db.query.text': 'SELECT NOW()',
'db.user': 'root',
'db.connection_string': expect.any(String),
'server.address': expect.any(String),
'server.port': 3306,
}),
}),
);
const secondQuery = mysqlSpans.find(span => span.attributes['db.query.text']?.value === 'SELECT NOW()');
expect(secondQuery).toBeDefined();
expect(secondQuery!.name).toBe('SELECT');
expect(secondQuery!.status).toBe('ok');
expect(secondQuery!.attributes).toMatchObject({
'sentry.op': { type: 'string', value: 'db' },
'sentry.origin': { type: 'string', value: 'auto.db.mysql' },
'db.system.name': { type: 'string', value: 'mysql' },
'db.query.text': { type: 'string', value: 'SELECT NOW()' },
'db.user': { type: 'string', value: 'root' },
'db.connection_string': { type: 'string', value: expect.any(String) },
'server.address': { type: 'string', value: expect.any(String) },
'server.port': { type: 'integer', value: 3306 },
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
import { getSpanOp, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils';

const usesManagedTunnelRoute =
(process.env.E2E_TEST_TUNNEL_ROUTE_MODE ?? 'off') !== 'off' || process.env.E2E_TEST_CUSTOM_TUNNEL_ROUTE === '1';
Expand Down Expand Up @@ -123,16 +123,16 @@ test('Does not send SSR loader error to Sentry', async ({ baseURL, page }) => {
if (!event.type && event.exception?.values?.[0]?.value === 'Sentry SSR Test Error') {
errorEventOccurred = true;
}
return event?.transaction === 'GET /ssr-error';
return false;
});

const transactionEventPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
return transactionEvent?.transaction === 'GET /ssr-error';
const serverSpanPromise = waitForStreamedSpan('tanstackstart-react', span => {
return span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/ssr-error';
});

await page.goto('/ssr-error');

await transactionEventPromise;
await serverSpanPromise;

await (await fetch(`${baseURL}/api/flush`)).text();

Expand Down
Loading
Loading