From 2b9b3b153258fdcdbb0f2509b57cbfb9f45e35ab Mon Sep 17 00:00:00 2001 From: s1gr1d <32902192+s1gr1d@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:07:56 +0200 Subject: [PATCH] test(e2e): Migrate nuxt-4-cloudflare to span streaming Co-Authored-By: Claude Fable 5 --- .../server/plugins/sentry.ts | 1 - .../nuxt-4-cloudflare/tests/db.test.ts | 68 +++++++++++-------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/server/plugins/sentry.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/server/plugins/sentry.ts index f1725d6bf9e4..faa56e1db7ef 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/server/plugins/sentry.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/server/plugins/sentry.ts @@ -3,7 +3,6 @@ import { sentryCloudflareNitroPlugin } from '@sentry/nuxt/module/plugins'; export default defineNitroPlugin( sentryCloudflareNitroPlugin({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1.0, tunnel: 'http://localhost:3031/', // proxy server diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/tests/db.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/tests/db.test.ts index d2f846fd29cf..af9f1e7259d4 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/tests/db.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4-cloudflare/tests/db.test.ts @@ -1,43 +1,57 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { collectStreamedSpans, getSpanOp } from '@sentry-internal/test-utils'; + +// Streamed child spans arrive across several envelopes, so collect until the request's segment +// span and the expected db spans have all been flushed. The streamed segment name is method-only +// (`GET`), so the segment is selected via `url.path`. +function collectDbSpans(minDbSpans: number) { + return collectStreamedSpans( + 'nuxt-4-cloudflare', + spans => + spans.some(span => span.is_segment && span.attributes['url.path']?.value === '/api/db-mysql') && + spans.filter(span => getSpanOp(span) === 'db').length >= minDbSpans, + ); +} test('a real mysql query emits a db span with orchestrion-channel attributes', async ({ request }) => { - const transactionPromise = waitForTransaction('nuxt-4-cloudflare', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && - (transactionEvent.spans?.some(span => span.op === 'db') ?? false) - ); - }); + const spansPromise = collectDbSpans(1); const res = await request.get('/api/db-mysql'); expect(res.status()).toBe(200); - const transactionEvent = await transactionPromise; - const dbSpans = transactionEvent.spans!.filter(span => span.op === 'db'); + const spans = await spansPromise; + const rootSpan = spans.find(span => span.is_segment && span.attributes['url.path']?.value === '/api/db-mysql'); + expect(rootSpan).toBeDefined(); + expect(getSpanOp(rootSpan!)).toBe('http.server'); + + const dbSpans = spans.filter(span => getSpanOp(span) === 'db' && span.trace_id === rootSpan!.trace_id); - const firstQuery = dbSpans.find(span => span.description === 'SELECT 1 + 1 AS solution'); + const firstQuery = dbSpans.find(span => span.attributes['db.query.text']?.value === 'SELECT 1 + 1 AS solution'); expect(firstQuery).toBeDefined(); - expect(firstQuery!.data?.['sentry.origin']).toBe('auto.db.mysql'); - expect(firstQuery!.data?.['db.system.name']).toBe('mysql'); - expect(firstQuery!.data?.['db.query.text']).toBe('SELECT 1 + 1 AS solution'); - expect(firstQuery!.data?.['server.address']).toBe('127.0.0.1'); - expect(firstQuery!.data?.['server.port']).toBe(3306); - expect(firstQuery!.data?.['db.user']).toBe('root'); + expect(firstQuery!.name).toBe('SELECT'); + expect(firstQuery!.attributes).toMatchObject({ + 'sentry.origin': { type: 'string', value: 'auto.db.mysql' }, + 'db.system.name': { type: 'string', value: 'mysql' }, + 'db.query.text': { type: 'string', value: 'SELECT 1 + 1 AS solution' }, + 'server.address': { type: 'string', value: '127.0.0.1' }, + 'server.port': { type: 'integer', value: 3306 }, + 'db.user': { type: 'string', value: 'root' }, + }); }); -test('a nested query lands on the same transaction (async context restored)', async ({ request }) => { - const transactionPromise = waitForTransaction('nuxt-4-cloudflare', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && - (transactionEvent.spans?.filter(span => span.op === 'db').length ?? 0) >= 2 - ); - }); +test('a nested query lands on the same trace (async context restored)', async ({ request }) => { + const spansPromise = collectDbSpans(2); const res = await request.get('/api/db-mysql'); expect(res.status()).toBe(200); - const transactionEvent = await transactionPromise; - const descriptions = transactionEvent.spans!.filter(span => span.op === 'db').map(span => span.description); - expect(descriptions).toContain('SELECT 1 + 1 AS solution'); - expect(descriptions).toContain('SELECT NOW()'); + const spans = await spansPromise; + const rootSpan = spans.find(span => span.is_segment && span.attributes['url.path']?.value === '/api/db-mysql'); + expect(rootSpan).toBeDefined(); + + const queryTexts = spans + .filter(span => getSpanOp(span) === 'db' && span.trace_id === rootSpan!.trace_id) + .map(span => span.attributes['db.query.text']?.value); + expect(queryTexts).toContain('SELECT 1 + 1 AS solution'); + expect(queryTexts).toContain('SELECT NOW()'); });